mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-15 14:25:52 +00:00
Merge m-c to b-i, a=merge
This commit is contained in:
commit
642c6b1a30
@ -1417,13 +1417,13 @@ BrowserGlue.prototype = {
|
||||
let currentsetResource = this._rdf.GetResource("currentset");
|
||||
let toolbarIsCustomized = !!this._getPersist(toolbarResource,
|
||||
currentsetResource);
|
||||
function getToolbarFolderCount() {
|
||||
let getToolbarFolderCount = function () {
|
||||
let toolbarFolder =
|
||||
PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root;
|
||||
let toolbarChildCount = toolbarFolder.childCount;
|
||||
toolbarFolder.containerOpen = false;
|
||||
return toolbarChildCount;
|
||||
}
|
||||
};
|
||||
|
||||
if (toolbarIsCustomized || getToolbarFolderCount() > 3) {
|
||||
this._setPersist(toolbarResource, collapsedResource, "false");
|
||||
|
@ -105,7 +105,7 @@ function test() {
|
||||
"myVar.prop + 42": 2549
|
||||
}))
|
||||
.then(() => deleteWatchExpression("myVar.prop + 42"))
|
||||
.then(() => testEdit("self", "910", {
|
||||
.then(() => testEdit("self", "0910", {
|
||||
"myVar.prop": 910
|
||||
}))
|
||||
.then(() => deleteLastWatchExpression("myVar.prop"))
|
||||
|
@ -115,7 +115,6 @@ enum {
|
||||
ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET);
|
||||
|
||||
namespace mozilla {
|
||||
class ElementAnimation;
|
||||
class EventChainPostVisitor;
|
||||
class EventChainPreVisitor;
|
||||
class EventChainVisitor;
|
||||
@ -124,6 +123,7 @@ class EventStateManager;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class AnimationPlayer;
|
||||
class Link;
|
||||
class UndoManager;
|
||||
class DOMRect;
|
||||
@ -803,7 +803,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void GetAnimationPlayers(nsTArray<nsRefPtr<ElementAnimation> >& aPlayers);
|
||||
void GetAnimationPlayers(nsTArray<nsRefPtr<AnimationPlayer> >& aPlayers);
|
||||
|
||||
NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML);
|
||||
virtual void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "AnimationCommon.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/AnimationPlayer.h"
|
||||
#include "mozilla/dom/Attr.h"
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsIAtom.h"
|
||||
@ -2865,24 +2866,24 @@ Element::MozRequestPointerLock()
|
||||
}
|
||||
|
||||
void
|
||||
Element::GetAnimationPlayers(nsTArray<nsRefPtr<ElementAnimation> >& aPlayers)
|
||||
Element::GetAnimationPlayers(nsTArray<nsRefPtr<AnimationPlayer> >& aPlayers)
|
||||
{
|
||||
nsIAtom* properties[] = { nsGkAtoms::transitionsProperty,
|
||||
nsGkAtoms::animationsProperty };
|
||||
for (size_t propIdx = 0; propIdx < MOZ_ARRAY_LENGTH(properties);
|
||||
propIdx++) {
|
||||
ElementAnimationCollection* collection =
|
||||
static_cast<ElementAnimationCollection*>(
|
||||
AnimationPlayerCollection* collection =
|
||||
static_cast<AnimationPlayerCollection*>(
|
||||
GetProperty(properties[propIdx]));
|
||||
if (!collection) {
|
||||
continue;
|
||||
}
|
||||
for (size_t animIdx = 0;
|
||||
animIdx < collection->mAnimations.Length();
|
||||
animIdx++) {
|
||||
ElementAnimation* anim = collection->mAnimations[animIdx];
|
||||
if (anim->IsCurrent()) {
|
||||
aPlayers.AppendElement(anim);
|
||||
for (size_t playerIdx = 0;
|
||||
playerIdx < collection->mPlayers.Length();
|
||||
playerIdx++) {
|
||||
AnimationPlayer* player = collection->mPlayers[playerIdx];
|
||||
if (player->IsCurrent()) {
|
||||
aPlayers.AppendElement(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +126,17 @@ TimeRanges::Normalize()
|
||||
}
|
||||
}
|
||||
|
||||
TimeRanges::index_type
|
||||
TimeRanges::Find(double aTime)
|
||||
{
|
||||
for (index_type i = 0; i < mRanges.Length(); ++i) {
|
||||
if (aTime >= mRanges[i].mStart && aTime < mRanges[i].mEnd) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return NoIndex;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TimeRanges::WrapObject(JSContext* aCx)
|
||||
{
|
||||
|
@ -80,6 +80,12 @@ private:
|
||||
};
|
||||
|
||||
nsAutoTArray<TimeRange,4> mRanges;
|
||||
|
||||
public:
|
||||
typedef nsTArray<TimeRange>::index_type index_type;
|
||||
static const index_type NoIndex = index_type(-1);
|
||||
|
||||
index_type Find(double aTime);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -458,25 +458,18 @@ ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aDa
|
||||
return OnStartContainer(aRequest, image);
|
||||
}
|
||||
|
||||
nsDOMTokenList* classList = mImageContent->AsElement()->ClassList();
|
||||
mozilla::ErrorResult rv;
|
||||
// Do these two off a script runner because decode complete notifications often
|
||||
// come during painting and these will trigger invalidation.
|
||||
if (aType == imgINotificationObserver::DECODE_COMPLETE) {
|
||||
if (mImageContent && !nsContentUtils::IsChildOfSameType(this)) {
|
||||
// Update the background-color of the image only after the
|
||||
// image has been decoded to prevent flashes of just the
|
||||
// background-color.
|
||||
classList->Add(NS_LITERAL_STRING("decoded"), rv);
|
||||
NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &ImageDocument::AddDecodedClass);
|
||||
nsContentUtils::AddScriptRunner(runnable);
|
||||
}
|
||||
|
||||
if (aType == imgINotificationObserver::DISCARD) {
|
||||
// mImageContent can be null if the document is already destroyed
|
||||
if (mImageContent && !nsContentUtils::IsChildOfSameType(this)) {
|
||||
// Remove any decoded-related styling when the image is unloaded.
|
||||
classList->Remove(NS_LITERAL_STRING("decoded"), rv);
|
||||
NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &ImageDocument::RemoveDecodedClass);
|
||||
nsContentUtils::AddScriptRunner(runnable);
|
||||
}
|
||||
|
||||
if (aType == imgINotificationObserver::LOAD_COMPLETE) {
|
||||
@ -490,6 +483,34 @@ ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aDa
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ImageDocument::AddDecodedClass()
|
||||
{
|
||||
if (!mImageContent || nsContentUtils::IsChildOfSameType(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDOMTokenList* classList = mImageContent->AsElement()->ClassList();
|
||||
mozilla::ErrorResult rv;
|
||||
// Update the background-color of the image only after the
|
||||
// image has been decoded to prevent flashes of just the
|
||||
// background-color.
|
||||
classList->Add(NS_LITERAL_STRING("decoded"), rv);
|
||||
}
|
||||
|
||||
void
|
||||
ImageDocument::RemoveDecodedClass()
|
||||
{
|
||||
if (!mImageContent || nsContentUtils::IsChildOfSameType(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDOMTokenList* classList = mImageContent->AsElement()->ClassList();
|
||||
mozilla::ErrorResult rv;
|
||||
// Remove any decoded-related styling when the image is unloaded.
|
||||
classList->Remove(NS_LITERAL_STRING("decoded"), rv);
|
||||
}
|
||||
|
||||
void
|
||||
ImageDocument::SetModeClass(eModeClasses mode)
|
||||
{
|
||||
|
@ -51,6 +51,9 @@ public:
|
||||
|
||||
void DefaultCheckOverflowing() { CheckOverflowing(mResizeImageByDefault); }
|
||||
|
||||
void AddDecodedClass();
|
||||
void RemoveDecodedClass();
|
||||
|
||||
// WebIDL API
|
||||
virtual JSObject* WrapNode(JSContext* aCx)
|
||||
MOZ_OVERRIDE;
|
||||
|
@ -16,6 +16,12 @@ namespace gmp {
|
||||
static MessageLoop* sMainLoop = nullptr;
|
||||
static GMPChild* sChild = nullptr;
|
||||
|
||||
static bool
|
||||
IsOnChildMainThread()
|
||||
{
|
||||
return sMainLoop && sMainLoop == MessageLoop::current();
|
||||
}
|
||||
|
||||
// We just need a refcounted wrapper for GMPTask objects.
|
||||
class Runnable MOZ_FINAL
|
||||
{
|
||||
@ -64,7 +70,7 @@ public:
|
||||
// 1) Nobody should be blocking the main thread.
|
||||
// 2) This prevents deadlocks when doing sync calls to main which if the
|
||||
// main thread tries to do a sync call back to the calling thread.
|
||||
MOZ_ASSERT(MessageLoop::current() != sMainLoop);
|
||||
MOZ_ASSERT(!IsOnChildMainThread());
|
||||
|
||||
mMessageLoop->PostTask(FROM_HERE, NewRunnableMethod(this, &SyncRunnable::Run));
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
@ -122,7 +128,7 @@ RunOnMainThread(GMPTask* aTask)
|
||||
GMPErr
|
||||
SyncRunOnMainThread(GMPTask* aTask)
|
||||
{
|
||||
if (!aTask || !sMainLoop || sMainLoop == MessageLoop::current()) {
|
||||
if (!aTask || !sMainLoop || IsOnChildMainThread()) {
|
||||
return GMPGenericErr;
|
||||
}
|
||||
|
||||
@ -148,6 +154,9 @@ CreateMutex(GMPMutex** aMutex)
|
||||
GMPErr
|
||||
SetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS)
|
||||
{
|
||||
if (!aTask || !sMainLoop || !IsOnChildMainThread()) {
|
||||
return GMPGenericErr;
|
||||
}
|
||||
GMPTimerChild* timers = sChild->GetGMPTimers();
|
||||
NS_ENSURE_TRUE(timers, GMPGenericErr);
|
||||
return timers->SetTimer(aTask, aTimeoutMS);
|
||||
|
@ -30,10 +30,29 @@ struct JSContext;
|
||||
class JSObject;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* gMediaSourceLog;
|
||||
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
PRLogModuleInfo* GetMediaSourceLog()
|
||||
{
|
||||
static PRLogModuleInfo* sLogModule;
|
||||
if (!sLogModule) {
|
||||
sLogModule = PR_NewLogModule("MediaSource");
|
||||
}
|
||||
return sLogModule;
|
||||
}
|
||||
|
||||
PRLogModuleInfo* GetMediaSourceAPILog()
|
||||
{
|
||||
static PRLogModuleInfo* sLogModule;
|
||||
if (!sLogModule) {
|
||||
sLogModule = PR_NewLogModule("MediaSource");
|
||||
}
|
||||
return sLogModule;
|
||||
}
|
||||
|
||||
#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define MSE_DEBUG(...)
|
||||
#define MSE_API(...)
|
||||
#endif
|
||||
|
||||
// Arbitrary limit.
|
||||
@ -108,11 +127,14 @@ MediaSource::Constructor(const GlobalObject& aGlobal,
|
||||
|
||||
MediaSource::~MediaSource()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("MediaSource(%p)::~MediaSource()", this);
|
||||
}
|
||||
|
||||
SourceBufferList*
|
||||
MediaSource::SourceBuffers()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT_IF(mReadyState == MediaSourceReadyState::Closed, mSourceBuffers->IsEmpty());
|
||||
return mSourceBuffers;
|
||||
}
|
||||
@ -120,6 +142,7 @@ MediaSource::SourceBuffers()
|
||||
SourceBufferList*
|
||||
MediaSource::ActiveSourceBuffers()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT_IF(mReadyState == MediaSourceReadyState::Closed, mActiveSourceBuffers->IsEmpty());
|
||||
return mActiveSourceBuffers;
|
||||
}
|
||||
@ -127,12 +150,14 @@ MediaSource::ActiveSourceBuffers()
|
||||
MediaSourceReadyState
|
||||
MediaSource::ReadyState()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mReadyState;
|
||||
}
|
||||
|
||||
double
|
||||
MediaSource::Duration()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mReadyState == MediaSourceReadyState::Closed) {
|
||||
return UnspecifiedNaN<double>();
|
||||
}
|
||||
@ -142,6 +167,8 @@ MediaSource::Duration()
|
||||
void
|
||||
MediaSource::SetDuration(double aDuration, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("MediaSource(%p)::SetDuration(aDuration=%f)", this, aDuration);
|
||||
if (aDuration < 0 || IsNaN(aDuration)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return;
|
||||
@ -157,8 +184,11 @@ MediaSource::SetDuration(double aDuration, ErrorResult& aRv)
|
||||
already_AddRefed<SourceBuffer>
|
||||
MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsresult rv = mozilla::IsTypeSupported(aType);
|
||||
MSE_DEBUG("MediaSource::AddSourceBuffer(Type=%s) -> %x", NS_ConvertUTF16toUTF8(aType).get(), rv);
|
||||
MSE_API("MediaSource(%p)::AddSourceBuffer(aType=%s)%s",
|
||||
this, NS_ConvertUTF16toUTF8(aType).get(),
|
||||
rv == NS_OK ? "" : " [not supported]");
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
@ -185,16 +215,16 @@ MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
|
||||
}
|
||||
mSourceBuffers->Append(sourceBuffer);
|
||||
mActiveSourceBuffers->Append(sourceBuffer);
|
||||
MSE_DEBUG("%p AddSourceBuffer(Type=%s) -> %p", this,
|
||||
NS_ConvertUTF16toUTF8(mimeType).get(), sourceBuffer.get());
|
||||
MSE_DEBUG("MediaSource(%p)::AddSourceBuffer() sourceBuffer=%p", this, sourceBuffer.get());
|
||||
return sourceBuffer.forget();
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
SourceBuffer* sourceBuffer = &aSourceBuffer;
|
||||
MSE_DEBUG("%p RemoveSourceBuffer(Buffer=%p)", this, sourceBuffer);
|
||||
MSE_API("MediaSource(%p)::RemoveSourceBuffer(aSourceBuffer=%p)", this, sourceBuffer);
|
||||
if (!mSourceBuffers->Contains(sourceBuffer)) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
return;
|
||||
@ -223,7 +253,9 @@ MediaSource::RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv)
|
||||
void
|
||||
MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv)
|
||||
{
|
||||
MSE_DEBUG("%p EndOfStream(Error=%u)", this, aError.WasPassed() ? uint32_t(aError.Value()) : 0);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("MediaSource(%p)::EndOfStream(aError=%d)",
|
||||
this, aError.WasPassed() ? uint32_t(aError.Value()) : 0);
|
||||
if (mReadyState != MediaSourceReadyState::Open ||
|
||||
mSourceBuffers->AnyUpdating()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
@ -233,13 +265,12 @@ MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, Er
|
||||
SetReadyState(MediaSourceReadyState::Ended);
|
||||
mSourceBuffers->Ended();
|
||||
if (!aError.WasPassed()) {
|
||||
DurationChange(mSourceBuffers->GetHighestBufferedEndTime(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
// TODO:
|
||||
// Run duration change algorithm.
|
||||
// DurationChange(highestDurationOfSourceBuffers, aRv);
|
||||
// if (aRv.Failed()) {
|
||||
// return;
|
||||
// }
|
||||
// Notify media element that all data is now available.
|
||||
// Notify media element that all data is now available.
|
||||
return;
|
||||
}
|
||||
switch (aError.Value()) {
|
||||
@ -261,20 +292,18 @@ MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, Er
|
||||
/* static */ bool
|
||||
MediaSource::IsTypeSupported(const GlobalObject&, const nsAString& aType)
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
if (!gMediaSourceLog) {
|
||||
gMediaSourceLog = PR_NewLogModule("MediaSource");
|
||||
}
|
||||
#endif
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsresult rv = mozilla::IsTypeSupported(aType);
|
||||
MSE_DEBUG("MediaSource::IsTypeSupported(Type=%s) -> %x", NS_ConvertUTF16toUTF8(aType).get(), rv);
|
||||
MSE_API("MediaSource::IsTypeSupported(aType=%s)%s",
|
||||
NS_ConvertUTF16toUTF8(aType).get(), rv == NS_OK ? "" : " [not supported]");
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
bool
|
||||
MediaSource::Attach(MediaSourceDecoder* aDecoder)
|
||||
{
|
||||
MSE_DEBUG("%p Attaching decoder %p owner %p", this, aDecoder, aDecoder->GetOwner());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("MediaSource(%p)::Attach(aDecoder=%p) owner=%p", this, aDecoder, aDecoder->GetOwner());
|
||||
MOZ_ASSERT(aDecoder);
|
||||
if (mReadyState != MediaSourceReadyState::Closed) {
|
||||
return false;
|
||||
@ -288,7 +317,9 @@ MediaSource::Attach(MediaSourceDecoder* aDecoder)
|
||||
void
|
||||
MediaSource::Detach()
|
||||
{
|
||||
MSE_DEBUG("%p Detaching decoder %p owner %p", this, mDecoder.get(), mDecoder->GetOwner());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("MediaSource(%p)::Detach() mDecoder=%p owner=%p",
|
||||
this, mDecoder.get(), mDecoder->GetOwner());
|
||||
MOZ_ASSERT(mDecoder);
|
||||
mDecoder->DetachMediaSource();
|
||||
mDecoder = nullptr;
|
||||
@ -305,21 +336,19 @@ MediaSource::MediaSource(nsPIDOMWindow* aWindow)
|
||||
, mReadyState(MediaSourceReadyState::Closed)
|
||||
, mWaitForDataMonitor("MediaSource.WaitForData.Monitor")
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mSourceBuffers = new SourceBufferList(this);
|
||||
mActiveSourceBuffers = new SourceBufferList(this);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
if (!gMediaSourceLog) {
|
||||
gMediaSourceLog = PR_NewLogModule("MediaSource");
|
||||
}
|
||||
#endif
|
||||
MSE_API("MediaSource(%p)::MediaSource(aWindow=%p) mSourceBuffers=%p mActiveSourceBuffers=%p",
|
||||
this, aWindow, mSourceBuffers.get(), mActiveSourceBuffers.get());
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::SetReadyState(MediaSourceReadyState aState)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aState != mReadyState);
|
||||
MSE_DEBUG("%p SetReadyState old=%d new=%d", this, mReadyState, aState);
|
||||
MSE_DEBUG("MediaSource(%p)::SetReadyState(aState=%d) mReadyState=%d", this, aState, mReadyState);
|
||||
|
||||
MediaSourceReadyState oldState = mReadyState;
|
||||
mReadyState = aState;
|
||||
@ -350,14 +379,15 @@ MediaSource::SetReadyState(MediaSourceReadyState aState)
|
||||
void
|
||||
MediaSource::DispatchSimpleEvent(const char* aName)
|
||||
{
|
||||
MSE_DEBUG("%p Dispatching event %s to MediaSource", this, aName);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("MediaSource(%p) Dispatch event '%s'", this, aName);
|
||||
DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::QueueAsyncSimpleEvent(const char* aName)
|
||||
{
|
||||
MSE_DEBUG("%p Queuing event %s to MediaSource", this, aName);
|
||||
MSE_DEBUG("MediaSource(%p) Queuing event '%s'", this, aName);
|
||||
nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
@ -365,6 +395,8 @@ MediaSource::QueueAsyncSimpleEvent(const char* aName)
|
||||
void
|
||||
MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("MediaSource(%p)::DurationChange(aNewDuration=%f)", this, aNewDuration);
|
||||
if (mDuration == aNewDuration) {
|
||||
return;
|
||||
}
|
||||
@ -380,6 +412,32 @@ MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
|
||||
// TODO: Update media element's duration and run element's duration change algorithm.
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::NotifyEvicted(double aStart, double aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("MediaSource(%p)::NotifyEvicted(aStart=%f, aEnd=%f)", this, aStart, aEnd);
|
||||
// Cycle through all SourceBuffers and tell them to evict data in
|
||||
// the given range.
|
||||
mSourceBuffers->Evict(aStart, aEnd);
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::WaitForData()
|
||||
{
|
||||
MSE_DEBUG("MediaSource(%p)::WaitForData()", this);
|
||||
MonitorAutoLock mon(mWaitForDataMonitor);
|
||||
mon.Wait();
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::NotifyGotData()
|
||||
{
|
||||
MSE_DEBUG("MediaSource(%p)::NotifyGotData()", this);
|
||||
MonitorAutoLock mon(mWaitForDataMonitor);
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
MediaSource::GetParentObject() const
|
||||
{
|
||||
@ -392,28 +450,6 @@ MediaSource::WrapObject(JSContext* aCx)
|
||||
return MediaSourceBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::NotifyEvicted(double aStart, double aEnd)
|
||||
{
|
||||
// Cycle through all SourceBuffers and tell them to evict data in
|
||||
// the given range.
|
||||
mSourceBuffers->Evict(aStart, aEnd);
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::WaitForData()
|
||||
{
|
||||
MonitorAutoLock lock(mWaitForDataMonitor);
|
||||
lock.Wait();
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::NotifyGotData()
|
||||
{
|
||||
MonitorAutoLock lock(mWaitForDataMonitor);
|
||||
lock.NotifyAll();
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaSource, DOMEventTargetHelper,
|
||||
mSourceBuffers, mActiveSourceBuffers)
|
||||
|
||||
|
@ -30,20 +30,20 @@
|
||||
#endif
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gMediaSourceLog;
|
||||
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
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 {
|
||||
|
||||
namespace dom {
|
||||
|
||||
class TimeRanges;
|
||||
|
||||
} // namespace dom
|
||||
|
||||
class MediaSourceReader : public MediaDecoderReader
|
||||
{
|
||||
public:
|
||||
@ -73,7 +73,7 @@ public:
|
||||
void RequestAudioData() MOZ_OVERRIDE
|
||||
{
|
||||
if (!GetAudioReader()) {
|
||||
MSE_DEBUG("%p MSR::RequestAudioData called with no audio reader", this);
|
||||
MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
|
||||
MOZ_ASSERT(mPendingDecoders.IsEmpty());
|
||||
GetCallback()->OnDecodeError();
|
||||
return;
|
||||
@ -88,7 +88,7 @@ public:
|
||||
|
||||
void OnAudioEOS()
|
||||
{
|
||||
MSE_DEBUG("%p OnAudioEOS %d (%p) EOS (readers=%u)",
|
||||
MSE_DEBUG("MediaSourceReader(%p)::OnAudioEOS %d (%p) EOS (readers=%u)",
|
||||
this, mActiveAudioDecoder, mDecoders[mActiveAudioDecoder].get(), mDecoders.Length());
|
||||
GetCallback()->OnAudioEOS();
|
||||
}
|
||||
@ -96,7 +96,7 @@ public:
|
||||
void RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE
|
||||
{
|
||||
if (!GetVideoReader()) {
|
||||
MSE_DEBUG("%p MSR::RequestVideoData called with no video reader", this);
|
||||
MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called with no video reader", this);
|
||||
MOZ_ASSERT(mPendingDecoders.IsEmpty());
|
||||
GetCallback()->OnDecodeError();
|
||||
return;
|
||||
@ -110,7 +110,7 @@ public:
|
||||
{
|
||||
if (mDropVideoBeforeThreshold) {
|
||||
if (aSample->mTime < mTimeThreshold) {
|
||||
MSE_DEBUG("%p MSR::OnVideoDecoded VideoData mTime %lld below mTimeThreshold %lld",
|
||||
MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld",
|
||||
this, aSample->mTime, mTimeThreshold);
|
||||
delete aSample;
|
||||
GetVideoReader()->RequestVideoData(false, mTimeThreshold);
|
||||
@ -124,14 +124,14 @@ public:
|
||||
void OnVideoEOS()
|
||||
{
|
||||
// End of stream. See if we can switch to another video decoder.
|
||||
MSE_DEBUG("%p MSR::OnVideoEOS %d (%p) (readers=%u)",
|
||||
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS %d (%p) (readers=%u)",
|
||||
this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
|
||||
if (SwitchVideoReaders(SWITCH_FORCED)) {
|
||||
// Success! Resume decoding with next video decoder.
|
||||
RequestVideoData(false, mTimeThreshold);
|
||||
} else {
|
||||
// End of stream.
|
||||
MSE_DEBUG("%p MSR::OnVideoEOS %d (%p) EOS (readers=%u)",
|
||||
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS %d (%p) EOS (readers=%u)",
|
||||
this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
|
||||
GetCallback()->OnVideoEOS();
|
||||
}
|
||||
@ -200,16 +200,31 @@ private:
|
||||
|
||||
for (uint32_t i = mActiveVideoDecoder + 1; i < mDecoders.Length(); ++i) {
|
||||
SubBufferDecoder* decoder = mDecoders[i];
|
||||
if (decoder->IsDiscarded() || !decoder->GetReader()->GetMediaInfo().HasVideo()) {
|
||||
|
||||
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
|
||||
decoder->GetBuffered(ranges);
|
||||
|
||||
MSE_DEBUGV("MediaDecoderReader(%p)::SwitchVideoReaders(%d) decoder=%u (%p) discarded=%d"
|
||||
" hasVideo=%d timeThreshold=%f startTime=%f endTime=%f length=%u",
|
||||
this, aType, i, decoder, decoder->IsDiscarded(),
|
||||
decoder->GetReader()->GetMediaInfo().HasVideo(),
|
||||
double(mTimeThreshold) / USECS_PER_S,
|
||||
ranges->GetStartTime(), ranges->GetEndTime(), ranges->Length());
|
||||
|
||||
if (decoder->IsDiscarded() ||
|
||||
!decoder->GetReader()->GetMediaInfo().HasVideo() ||
|
||||
ranges->Length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (aType == SWITCH_FORCED || mTimeThreshold >= mDecoders[i]->GetMediaStartTime()) {
|
||||
if (aType == SWITCH_FORCED ||
|
||||
ranges->Find(double(mTimeThreshold) / USECS_PER_S) != dom::TimeRanges::NoIndex) {
|
||||
GetVideoReader()->SetIdle();
|
||||
|
||||
mActiveVideoDecoder = i;
|
||||
mDropVideoBeforeThreshold = true;
|
||||
MSE_DEBUG("%p MSR::SwitchVideoReaders(%d) switching to %d", this, aType, mActiveVideoDecoder);
|
||||
MSE_DEBUG("MediaDecoderReader(%p)::SwitchVideoReaders(%d) switching to %d (%p)",
|
||||
this, aType, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -260,7 +275,8 @@ public:
|
||||
if (!mReader) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<MediaSourceReader*>(mReader.get())->CreateSubDecoder(aType, aParentDecoder, mDecodeTaskQueue);
|
||||
MediaSourceReader* reader = static_cast<MediaSourceReader*>(mReader.get());
|
||||
return reader->CreateSubDecoder(aType, aParentDecoder, mDecodeTaskQueue);
|
||||
}
|
||||
|
||||
nsresult EnqueueDecoderInitialization() {
|
||||
@ -268,8 +284,9 @@ public:
|
||||
if (!mReader) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return mDecodeTaskQueue->Dispatch(NS_NewRunnableMethod(this,
|
||||
&MediaSourceStateMachine::InitializePendingDecoders));
|
||||
RefPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this, &MediaSourceStateMachine::InitializePendingDecoders);
|
||||
return mDecodeTaskQueue->Dispatch(task);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -277,7 +294,8 @@ private:
|
||||
if (!mReader) {
|
||||
return;
|
||||
}
|
||||
static_cast<MediaSourceReader*>(mReader.get())->InitializePendingDecoders();
|
||||
MediaSourceReader* reader = static_cast<MediaSourceReader*>(mReader.get());
|
||||
reader->InitializePendingDecoders();
|
||||
}
|
||||
};
|
||||
|
||||
@ -333,6 +351,8 @@ MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable)
|
||||
} else {
|
||||
aSeekable->Add(0, duration);
|
||||
}
|
||||
MSE_DEBUG("MediaSourceDecoder(%p)::GetSeekable startTime=%f endTime=%f",
|
||||
this, aSeekable->GetStartTime(), aSeekable->GetEndTime());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -362,7 +382,8 @@ MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
|
||||
if (!mDecoderStateMachine) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get())->CreateSubDecoder(aType, this);
|
||||
MediaSourceStateMachine* sm = static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get());
|
||||
return sm->CreateSubDecoder(aType, this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -371,7 +392,8 @@ MediaSourceDecoder::EnqueueDecoderInitialization()
|
||||
if (!mDecoderStateMachine) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get())->EnqueueDecoderInitialization();
|
||||
MediaSourceStateMachine* sm = static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get());
|
||||
return sm->EnqueueDecoderInitialization();
|
||||
}
|
||||
|
||||
class ReleaseDecodersTask : public nsRunnable {
|
||||
@ -397,38 +419,43 @@ MediaSourceReader::InitializePendingDecoders()
|
||||
for (uint32_t i = 0; i < mPendingDecoders.Length(); ++i) {
|
||||
nsRefPtr<SubBufferDecoder> decoder = mPendingDecoders[i];
|
||||
MediaDecoderReader* reader = decoder->GetReader();
|
||||
MSE_DEBUG("%p: Initializating subdecoder %p reader %p", this, decoder.get(), reader);
|
||||
MSE_DEBUG("MediaSourceReader(%p): Initializing subdecoder %p reader %p",
|
||||
this, decoder.get(), reader);
|
||||
|
||||
MediaInfo mi;
|
||||
nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
|
||||
nsresult rv;
|
||||
int64_t startTime = 0;
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
reader->FindStartTime(startTime);
|
||||
}
|
||||
}
|
||||
reader->SetIdle();
|
||||
if (NS_FAILED(rv)) {
|
||||
// XXX: Need to signal error back to owning SourceBuffer.
|
||||
MSE_DEBUG("%p: Reader %p failed to initialize, rv=%x", this, reader, rv);
|
||||
MSE_DEBUG("MediaSourceReader(%p): Reader %p failed to initialize rv=%x",
|
||||
this, reader, rv);
|
||||
continue;
|
||||
}
|
||||
decoder->SetMediaStartTime(startTime);
|
||||
|
||||
bool active = false;
|
||||
if (mi.HasVideo() || mi.HasAudio()) {
|
||||
MSE_DEBUG("%p: Reader %p has video=%d audio=%d startTime=%lld",
|
||||
this, reader, mi.HasVideo(), mi.HasAudio(), startTime);
|
||||
MSE_DEBUG("MediaSourceReader(%p): Reader %p has video=%d audio=%d",
|
||||
this, reader, mi.HasVideo(), mi.HasAudio());
|
||||
if (mi.HasVideo()) {
|
||||
MSE_DEBUG("MediaSourceReader(%p): Reader %p video resolution=%dx%d",
|
||||
this, reader, mi.mVideo.mDisplay.width, mi.mVideo.mDisplay.height);
|
||||
}
|
||||
if (mi.HasAudio()) {
|
||||
MSE_DEBUG("MediaSourceReader(%p): Reader %p audio sampleRate=%d channels=%d",
|
||||
this, reader, mi.mAudio.mRate, mi.mAudio.mChannels);
|
||||
}
|
||||
active = true;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
mDecoders.AppendElement(decoder);
|
||||
} else {
|
||||
MSE_DEBUG("%p: Reader %p not activated", this, reader);
|
||||
MSE_DEBUG("MediaSourceReader(%p): Reader %p not activated", this, reader);
|
||||
}
|
||||
}
|
||||
NS_DispatchToMainThread(new ReleaseDecodersTask(mPendingDecoders));
|
||||
@ -474,11 +501,12 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType,
|
||||
reader->SetTaskQueue(aTaskQueue);
|
||||
reader->Init(nullptr);
|
||||
ReentrantMonitorAutoEnter mon(aParentDecoder->GetReentrantMonitor());
|
||||
MSE_DEBUG("Registered subdecoder %p subreader %p", decoder.get(), reader.get());
|
||||
MSE_DEBUG("MediaSourceReader(%p)::CreateSubDecoder subdecoder %p subreader %p",
|
||||
this, decoder.get(), reader.get());
|
||||
decoder->SetReader(reader);
|
||||
mPendingDecoders.AppendElement(decoder);
|
||||
if (NS_FAILED(static_cast<MediaSourceDecoder*>(mDecoder)->EnqueueDecoderInitialization())) {
|
||||
MSE_DEBUG("%p: Failed to enqueue decoder initialization task", this);
|
||||
MSE_DEBUG("MediaSourceReader(%p): Failed to enqueue decoder initialization task", this);
|
||||
return nullptr;
|
||||
}
|
||||
mDecoder->NotifyWaitingForResourcesStatusChanged();
|
||||
@ -510,8 +538,11 @@ nsresult
|
||||
MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
|
||||
int64_t aCurrentTime)
|
||||
{
|
||||
MSE_DEBUG("MediaSourceReader(%p)::Seek(aTime=%lld, aStart=%lld, aEnd=%lld, aCurrent=%lld)",
|
||||
this, aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
double target = static_cast<double>(aTime) / USECS_PER_S;
|
||||
if (!mMediaSource->ActiveSourceBuffers()->AllContainsTime(target)) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::Seek no active buffer contains target=%f", this, target);
|
||||
NS_DispatchToMainThread(new ChangeToHaveMetadata(mDecoder));
|
||||
}
|
||||
|
||||
@ -521,6 +552,7 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
|
||||
// we'll remove this for an async approach based on that in bug XXXXXXX.
|
||||
while (!mMediaSource->ActiveSourceBuffers()->AllContainsTime(target)
|
||||
&& !IsShutdown()) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::Seek waiting for target=%f", this, target);
|
||||
mMediaSource->WaitForData();
|
||||
SwitchVideoReaders(SWITCH_FORCED);
|
||||
}
|
||||
@ -551,20 +583,23 @@ MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
|
||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
||||
nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
|
||||
mDecoders[i]->GetBuffered(r);
|
||||
MSE_DEBUGV("MediaSourceReader(%p)::GetBuffered i=%u start=%f end=%f%s",
|
||||
this, i, r->GetStartTime(), r->GetEndTime(),
|
||||
mDecoders[i]->IsDiscarded() ? " [discarded]" : "");
|
||||
aBuffered->Add(r->GetStartTime(), r->GetEndTime());
|
||||
}
|
||||
aBuffered->Normalize();
|
||||
MSE_DEBUGV("MediaSourceReader(%p)::GetBuffered start=%f end=%f ranges=%u",
|
||||
this, aBuffered->GetStartTime(), aBuffered->GetEndTime(), aBuffered->Length());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
{
|
||||
MSE_DEBUG("%p: MSR::ReadMetadata pending=%u", this, mPendingDecoders.Length());
|
||||
|
||||
InitializePendingDecoders();
|
||||
|
||||
MSE_DEBUG("%p: MSR::ReadMetadata decoders=%u", this, mDecoders.Length());
|
||||
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata decoders=%u", this, mDecoders.Length());
|
||||
|
||||
// XXX: Make subdecoder setup async, so that use cases like bug 989888 can
|
||||
// work. This will require teaching the state machine about dynamic track
|
||||
@ -584,14 +619,16 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
mActiveVideoDecoder = i;
|
||||
mInfo.mVideo = mi.mVideo;
|
||||
maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
|
||||
MSE_DEBUG("%p: MSR::ReadMetadata video decoder=%u maxDuration=%lld", this, i, maxDuration);
|
||||
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video decoder=%u maxDuration=%lld",
|
||||
this, i, maxDuration);
|
||||
}
|
||||
if (mi.HasAudio() && !mInfo.HasAudio()) {
|
||||
MOZ_ASSERT(mActiveAudioDecoder == -1);
|
||||
mActiveAudioDecoder = i;
|
||||
mInfo.mAudio = mi.mAudio;
|
||||
maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
|
||||
MSE_DEBUG("%p: MSR::ReadMetadata audio decoder=%u maxDuration=%lld", this, i, maxDuration);
|
||||
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio decoder=%u maxDuration=%lld",
|
||||
this, i, maxDuration);
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,7 +636,8 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mDecoder->SetMediaDuration(maxDuration);
|
||||
nsRefPtr<nsIRunnable> task (
|
||||
NS_NewRunnableMethodWithArg<double>(this, &MediaSourceReader::SetMediaSourceDuration, maxDuration));
|
||||
NS_NewRunnableMethodWithArg<double>(this, &MediaSourceReader::SetMediaSourceDuration,
|
||||
static_cast<double>(maxDuration) / USECS_PER_S));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
|
@ -27,105 +27,30 @@ struct JSContext;
|
||||
class JSObject;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gMediaSourceLog;
|
||||
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetMediaSourceLog();
|
||||
extern PRLogModuleInfo* GetMediaSourceAPILog();
|
||||
|
||||
#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define MSE_DEBUG(...)
|
||||
#define MSE_API(...)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaResource;
|
||||
class ReentrantMonitor;
|
||||
|
||||
namespace layers {
|
||||
|
||||
class ImageContainer;
|
||||
|
||||
} // namespace layers
|
||||
|
||||
ReentrantMonitor&
|
||||
SubBufferDecoder::GetReentrantMonitor()
|
||||
{
|
||||
return mParentDecoder->GetReentrantMonitor();
|
||||
}
|
||||
|
||||
bool
|
||||
SubBufferDecoder::OnStateMachineThread() const
|
||||
{
|
||||
return mParentDecoder->OnStateMachineThread();
|
||||
}
|
||||
|
||||
bool
|
||||
SubBufferDecoder::OnDecodeThread() const
|
||||
{
|
||||
return mParentDecoder->OnDecodeThread();
|
||||
}
|
||||
|
||||
SourceBufferResource*
|
||||
SubBufferDecoder::GetResource() const
|
||||
{
|
||||
return static_cast<SourceBufferResource*>(mResource.get());
|
||||
}
|
||||
|
||||
void
|
||||
SubBufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded)
|
||||
{
|
||||
return mParentDecoder->NotifyDecodedFrames(aParsed, aDecoded);
|
||||
}
|
||||
|
||||
void
|
||||
SubBufferDecoder::SetMediaDuration(int64_t aDuration)
|
||||
{
|
||||
mMediaDuration = aDuration;
|
||||
}
|
||||
|
||||
void
|
||||
SubBufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
{
|
||||
//mParentDecoder->UpdateEstimatedMediaDuration(aDuration);
|
||||
}
|
||||
|
||||
void
|
||||
SubBufferDecoder::SetMediaSeekable(bool aMediaSeekable)
|
||||
{
|
||||
//mParentDecoder->SetMediaSeekable(aMediaSeekable);
|
||||
}
|
||||
|
||||
layers::ImageContainer*
|
||||
SubBufferDecoder::GetImageContainer()
|
||||
{
|
||||
return mParentDecoder->GetImageContainer();
|
||||
}
|
||||
|
||||
MediaDecoderOwner*
|
||||
SubBufferDecoder::GetOwner()
|
||||
{
|
||||
return mParentDecoder->GetOwner();
|
||||
}
|
||||
|
||||
int64_t
|
||||
SubBufferDecoder::ConvertToByteOffset(double aTime)
|
||||
{
|
||||
// Uses a conversion based on (aTime/duration) * length. For the
|
||||
// purposes of eviction this should be adequate since we have the
|
||||
// byte threshold as well to ensure data actually gets evicted and
|
||||
// we ensure we don't evict before the current playable point.
|
||||
if (mMediaDuration == -1) {
|
||||
return -1;
|
||||
}
|
||||
int64_t length = GetResource()->GetLength();
|
||||
MOZ_ASSERT(length > 0);
|
||||
int64_t offset = (aTime / (double(mMediaDuration) / USECS_PER_S)) * length;
|
||||
return offset;
|
||||
}
|
||||
|
||||
class ContainerParser {
|
||||
public:
|
||||
virtual ~ContainerParser() {}
|
||||
|
||||
virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
MSE_DEBUG("ContainerParser: aLength=%u [%x%x%x%x]",
|
||||
aLength,
|
||||
aLength > 0 ? aData[0] : 0,
|
||||
aLength > 1 ? aData[1] : 0,
|
||||
aLength > 2 ? aData[2] : 0,
|
||||
aLength > 3 ? aData[3] : 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -136,6 +61,7 @@ class WebMContainerParser : public ContainerParser {
|
||||
public:
|
||||
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.
|
||||
@ -158,6 +84,7 @@ class MP4ContainerParser : public ContainerParser {
|
||||
public:
|
||||
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.
|
||||
@ -202,6 +129,8 @@ namespace dom {
|
||||
void
|
||||
SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBuffer(%p)::SetMode(aMode=%d)", this, aMode);
|
||||
if (!IsAttached() || mUpdating) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
@ -218,6 +147,8 @@ SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
|
||||
void
|
||||
SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBuffer(%p)::SetTimestampOffset(aTimestampOffset=%d)", this, aTimestampOffset);
|
||||
if (!IsAttached() || mUpdating) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
@ -234,6 +165,7 @@ SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
|
||||
already_AddRefed<TimeRanges>
|
||||
SourceBuffer::GetBuffered(ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!IsAttached()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
@ -243,12 +175,16 @@ SourceBuffer::GetBuffered(ErrorResult& aRv)
|
||||
mDecoder->GetBuffered(ranges);
|
||||
}
|
||||
ranges->Normalize();
|
||||
MSE_DEBUG("SourceBuffer(%p)::GetBuffered startTime=%f endTime=%f",
|
||||
this, ranges->GetStartTime(), ranges->GetEndTime());
|
||||
return ranges.forget();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBuffer(%p)::SetAppendWindowStart(aAppendWindowStart=%d)", this, aAppendWindowStart);
|
||||
if (!IsAttached() || mUpdating) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
@ -263,6 +199,8 @@ SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
|
||||
void
|
||||
SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBuffer(%p)::SetAppendWindowEnd(aAppendWindowEnd=%d)", this, aAppendWindowEnd);
|
||||
if (!IsAttached() || mUpdating) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
@ -278,23 +216,26 @@ SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
|
||||
void
|
||||
SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBuffer(%p)::AppendBuffer(ArrayBuffer)", this);
|
||||
aData.ComputeLengthAndData();
|
||||
|
||||
AppendData(aData.Data(), aData.Length(), aRv);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBuffer(%p)::AppendBuffer(ArrayBufferView)", this);
|
||||
aData.ComputeLengthAndData();
|
||||
|
||||
AppendData(aData.Data(), aData.Length(), aRv);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::Abort(ErrorResult& aRv)
|
||||
{
|
||||
MSE_DEBUG("%p Abort()", this);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBuffer(%p)::Abort()", this);
|
||||
if (!IsAttached()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
@ -311,14 +252,15 @@ SourceBuffer::Abort(ErrorResult& aRv)
|
||||
mAppendWindowStart = 0;
|
||||
mAppendWindowEnd = PositiveInfinity<double>();
|
||||
|
||||
MSE_DEBUG("%p Abort: Discarding decoder.", this);
|
||||
MSE_DEBUG("SourceBuffer(%p)::Abort() Discarding decoder", this);
|
||||
DiscardDecoder();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
|
||||
{
|
||||
MSE_DEBUG("%p Remove(Start=%f End=%f)", this, aStart, aEnd);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBuffer(%p)::Remove(aStart=%f, aEnd=%f)", this, aStart, aEnd);
|
||||
if (!IsAttached() || mUpdating ||
|
||||
mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
@ -337,6 +279,8 @@ SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
|
||||
void
|
||||
SourceBuffer::Detach()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBuffer(%p)::Detach", this);
|
||||
Ended();
|
||||
DiscardDecoder();
|
||||
mMediaSource = nullptr;
|
||||
@ -345,6 +289,8 @@ SourceBuffer::Detach()
|
||||
void
|
||||
SourceBuffer::Ended()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBuffer(%p)::Ended", this);
|
||||
if (mDecoder) {
|
||||
mDecoder->GetResource()->Ended();
|
||||
}
|
||||
@ -361,9 +307,10 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||
, mUpdating(false)
|
||||
, mDecoderInitialized(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aMediaSource);
|
||||
mParser = ContainerParser::CreateForMIMEType(aType);
|
||||
MSE_DEBUG("%p SourceBuffer: Creating initial decoder.", this);
|
||||
MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Creating initial decoder.", this);
|
||||
InitNewDecoder();
|
||||
}
|
||||
|
||||
@ -376,6 +323,8 @@ SourceBuffer::Create(MediaSource* aMediaSource, const nsACString& aType)
|
||||
|
||||
SourceBuffer::~SourceBuffer()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBuffer(%p)::~SourceBuffer", this);
|
||||
DiscardDecoder();
|
||||
}
|
||||
|
||||
@ -394,14 +343,15 @@ SourceBuffer::WrapObject(JSContext* aCx)
|
||||
void
|
||||
SourceBuffer::DispatchSimpleEvent(const char* aName)
|
||||
{
|
||||
MSE_DEBUG("%p Dispatching event %s to SourceBuffer", this, aName);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBuffer(%p) Dispatch event '%s'", this, aName);
|
||||
DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
|
||||
{
|
||||
MSE_DEBUG("%p Queuing event %s to SourceBuffer", this, aName);
|
||||
MSE_DEBUG("SourceBuffer(%p) Queuing event '%s'", this, aName);
|
||||
nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
@ -409,6 +359,8 @@ SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
|
||||
bool
|
||||
SourceBuffer::InitNewDecoder()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBuffer(%p)::InitNewDecoder", this);
|
||||
MOZ_ASSERT(!mDecoder);
|
||||
MediaSourceDecoder* parentDecoder = mMediaSource->GetDecoder();
|
||||
nsRefPtr<SubBufferDecoder> decoder = parentDecoder->CreateSubDecoder(mType);
|
||||
@ -423,6 +375,8 @@ SourceBuffer::InitNewDecoder()
|
||||
void
|
||||
SourceBuffer::DiscardDecoder()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBuffer(%p)::DiscardDecoder mDecoder=%p", this, mDecoder.get());
|
||||
if (mDecoder) {
|
||||
mDecoder->SetDiscarded();
|
||||
}
|
||||
@ -433,6 +387,7 @@ SourceBuffer::DiscardDecoder()
|
||||
void
|
||||
SourceBuffer::StartUpdating()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mUpdating);
|
||||
mUpdating = true;
|
||||
QueueAsyncSimpleEvent("updatestart");
|
||||
@ -441,6 +396,7 @@ SourceBuffer::StartUpdating()
|
||||
void
|
||||
SourceBuffer::StopUpdating()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mUpdating);
|
||||
mUpdating = false;
|
||||
QueueAsyncSimpleEvent("update");
|
||||
@ -450,6 +406,7 @@ SourceBuffer::StopUpdating()
|
||||
void
|
||||
SourceBuffer::AbortUpdating()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mUpdating);
|
||||
mUpdating = false;
|
||||
QueueAsyncSimpleEvent("abort");
|
||||
@ -459,7 +416,7 @@ SourceBuffer::AbortUpdating()
|
||||
void
|
||||
SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
|
||||
{
|
||||
MSE_DEBUG("%p AppendBuffer(Data=%u bytes)", this, aLength);
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
|
||||
if (!IsAttached() || mUpdating) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
@ -472,7 +429,7 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
StartUpdating();
|
||||
// TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
|
||||
if (mParser->IsInitSegmentPresent(aData, aLength)) {
|
||||
MSE_DEBUG("%p AppendBuffer: New initialization segment.", this);
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: New initialization segment.", this);
|
||||
if (mDecoderInitialized) {
|
||||
// Existing decoder has been used, time for a new one.
|
||||
DiscardDecoder();
|
||||
@ -484,10 +441,10 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
|
||||
return;
|
||||
}
|
||||
MSE_DEBUG("%p AppendBuffer: Decoder marked as initialized.", this);
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
|
||||
mDecoderInitialized = true;
|
||||
} else if (!mDecoderInitialized) {
|
||||
MSE_DEBUG("%p AppendBuffer: Non-initialization segment appended during initialization.");
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.");
|
||||
Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
|
||||
ErrorResult dummy;
|
||||
mMediaSource->EndOfStream(decodeError, dummy);
|
||||
@ -503,15 +460,20 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
// Eviction uses a byte threshold. If the buffer is greater than the
|
||||
// number of bytes then data is evicted. The time range for this
|
||||
// eviction is reported back to the media source. It will then
|
||||
// evict data before that range across all SourceBuffer's it knows
|
||||
// evict data before that range across all SourceBuffers it knows
|
||||
// about.
|
||||
const int evict_threshold = 1000000;
|
||||
// TODO: Make the eviction threshold smaller for audio-only streams.
|
||||
// TODO: Drive evictions off memory pressure notifications.
|
||||
const uint32_t evict_threshold = 75 * (1 << 20);
|
||||
bool evicted = mDecoder->GetResource()->EvictData(evict_threshold);
|
||||
if (evicted) {
|
||||
double start = 0.0;
|
||||
double end = 0.0;
|
||||
GetBufferedStartEndTime(&start, &end);
|
||||
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendBuffer Evict; current start=%f end=%f",
|
||||
this, start, end);
|
||||
|
||||
// We notify that we've evicted from the time range 0 through to
|
||||
// the current start point.
|
||||
mMediaSource->NotifyEvicted(0.0, start);
|
||||
@ -528,34 +490,42 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
void
|
||||
SourceBuffer::GetBufferedStartEndTime(double* aStart, double* aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ErrorResult dummy;
|
||||
nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
|
||||
if (!ranges || ranges->Length() == 0) {
|
||||
*aStart = *aEnd = 0.0;
|
||||
return;
|
||||
}
|
||||
*aStart = ranges->Start(0, dummy);
|
||||
*aEnd = ranges->End(ranges->Length() - 1, dummy);
|
||||
*aStart = ranges->GetStartTime();
|
||||
*aEnd = ranges->GetEndTime();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::Evict(double aStart, double aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBuffer(%p)::Evict(aStart=%f, aEnd=%f)", this, aStart, aEnd);
|
||||
if (!mDecoder) {
|
||||
return;
|
||||
}
|
||||
// Need to map time to byte offset then evict
|
||||
int64_t end = mDecoder->ConvertToByteOffset(aEnd);
|
||||
if (end > 0) {
|
||||
mDecoder->GetResource()->EvictBefore(end);
|
||||
} else {
|
||||
NS_WARNING("SourceBuffer::Evict failed");
|
||||
double currentTime = mMediaSource->GetDecoder()->GetCurrentTime();
|
||||
double evictTime = aEnd;
|
||||
const double safety_threshold = 5;
|
||||
if (currentTime + safety_threshold >= evictTime) {
|
||||
evictTime -= safety_threshold;
|
||||
}
|
||||
int64_t endOffset = mDecoder->ConvertToByteOffset(evictTime);
|
||||
if (endOffset > 0) {
|
||||
mDecoder->GetResource()->EvictBefore(endOffset);
|
||||
}
|
||||
MSE_DEBUG("SourceBuffer(%p)::Evict offset=%lld", this, endOffset);
|
||||
}
|
||||
|
||||
bool
|
||||
SourceBuffer::ContainsTime(double aTime)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ErrorResult dummy;
|
||||
nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
|
||||
if (!ranges || ranges->Length() == 0) {
|
||||
|
@ -111,6 +111,10 @@ public:
|
||||
// Returns true if the data in the source buffer contains the given time.
|
||||
bool ContainsTime(double aTime);
|
||||
|
||||
// Provide the minimum start time and maximum end time that is available
|
||||
// in the data buffered by this SourceBuffer.
|
||||
void GetBufferedStartEndTime(double* aStart, double* aEnd);
|
||||
|
||||
private:
|
||||
~SourceBuffer();
|
||||
|
||||
@ -135,10 +139,6 @@ private:
|
||||
// Shared implementation of AppendBuffer overloads.
|
||||
void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
|
||||
|
||||
// Provide the minimum start time and maximum end time that is available
|
||||
// in the data buffered by this SourceBuffer.
|
||||
void GetBufferedStartEndTime(double* aStart, double* aEnd);
|
||||
|
||||
nsRefPtr<MediaSource> mMediaSource;
|
||||
|
||||
const nsCString mType;
|
||||
|
@ -21,10 +21,14 @@ struct JSContext;
|
||||
class JSObject;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gMediaSourceLog;
|
||||
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetMediaSourceLog();
|
||||
extern PRLogModuleInfo* GetMediaSourceAPILog();
|
||||
|
||||
#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define MSE_DEBUG(...)
|
||||
#define MSE_API(...)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
@ -38,6 +42,7 @@ SourceBufferList::~SourceBufferList()
|
||||
SourceBuffer*
|
||||
SourceBufferList::IndexedGetter(uint32_t aIndex, bool& aFound)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aFound = aIndex < mSourceBuffers.Length();
|
||||
return aFound ? mSourceBuffers[aIndex] : nullptr;
|
||||
}
|
||||
@ -45,12 +50,14 @@ SourceBufferList::IndexedGetter(uint32_t aIndex, bool& aFound)
|
||||
uint32_t
|
||||
SourceBufferList::Length()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mSourceBuffers.Length();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferList::Append(SourceBuffer* aSourceBuffer)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mSourceBuffers.AppendElement(aSourceBuffer);
|
||||
QueueAsyncSimpleEvent("addsourcebuffer");
|
||||
}
|
||||
@ -58,6 +65,7 @@ SourceBufferList::Append(SourceBuffer* aSourceBuffer)
|
||||
void
|
||||
SourceBufferList::Remove(SourceBuffer* aSourceBuffer)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ALWAYS_TRUE(mSourceBuffers.RemoveElement(aSourceBuffer));
|
||||
aSourceBuffer->Detach();
|
||||
QueueAsyncSimpleEvent("removesourcebuffer");
|
||||
@ -66,12 +74,14 @@ SourceBufferList::Remove(SourceBuffer* aSourceBuffer)
|
||||
bool
|
||||
SourceBufferList::Contains(SourceBuffer* aSourceBuffer)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mSourceBuffers.Contains(aSourceBuffer);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferList::Clear()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
|
||||
mSourceBuffers[i]->Detach();
|
||||
}
|
||||
@ -82,12 +92,14 @@ SourceBufferList::Clear()
|
||||
bool
|
||||
SourceBufferList::IsEmpty()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mSourceBuffers.IsEmpty();
|
||||
}
|
||||
|
||||
bool
|
||||
SourceBufferList::AnyUpdating()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
|
||||
if (mSourceBuffers[i]->Updating()) {
|
||||
return true;
|
||||
@ -99,6 +111,8 @@ SourceBufferList::AnyUpdating()
|
||||
void
|
||||
SourceBufferList::Remove(double aStart, double aEnd, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBufferList(%p)::Remove(aStart=%f, aEnd=%f", this, aStart, aEnd);
|
||||
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
|
||||
mSourceBuffers[i]->Remove(aStart, aEnd, aRv);
|
||||
if (aRv.Failed()) {
|
||||
@ -110,6 +124,8 @@ SourceBufferList::Remove(double aStart, double aEnd, ErrorResult& aRv)
|
||||
void
|
||||
SourceBufferList::Evict(double aStart, double aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBufferList(%p)::Evict(aStart=%f, aEnd=%f)", this, aStart, aEnd);
|
||||
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
|
||||
mSourceBuffers[i]->Evict(aStart, aEnd);
|
||||
}
|
||||
@ -118,6 +134,7 @@ SourceBufferList::Evict(double aStart, double aEnd)
|
||||
bool
|
||||
SourceBufferList::AllContainsTime(double aTime)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
|
||||
if (!mSourceBuffers[i]->ContainsTime(aTime)) {
|
||||
return false;
|
||||
@ -129,22 +146,37 @@ SourceBufferList::AllContainsTime(double aTime)
|
||||
void
|
||||
SourceBufferList::Ended()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
|
||||
mSourceBuffers[i]->Ended();
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
SourceBufferList::GetHighestBufferedEndTime()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
double highestEnd = 0;
|
||||
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
|
||||
double start, end;
|
||||
mSourceBuffers[i]->GetBufferedStartEndTime(&start, &end);
|
||||
highestEnd = std::max(highestEnd, end);
|
||||
}
|
||||
return highestEnd;
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferList::DispatchSimpleEvent(const char* aName)
|
||||
{
|
||||
MSE_DEBUG("%p Dispatching event %s to SourceBufferList", this, aName);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("SourceBufferList(%p) Dispatch event '%s'", this, aName);
|
||||
DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferList::QueueAsyncSimpleEvent(const char* aName)
|
||||
{
|
||||
MSE_DEBUG("%p Queuing event %s to SourceBufferList", this, aName);
|
||||
MSE_DEBUG("SourceBufferList(%p) Queuing event '%s'", this, aName);
|
||||
nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBufferList>(this, aName);
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
|
@ -79,6 +79,9 @@ public:
|
||||
// Returns true if all SourceBuffers in the list contain data for the given time.
|
||||
bool AllContainsTime(double aTime);
|
||||
|
||||
// Returns the highest end time of any of the Sourcebuffers.
|
||||
double GetHighestBufferedEndTime();
|
||||
|
||||
private:
|
||||
~SourceBufferList();
|
||||
|
||||
|
@ -15,10 +15,20 @@
|
||||
#include "prlog.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gMediaSourceLog;
|
||||
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
PRLogModuleInfo* GetSourceBufferResourceLog()
|
||||
{
|
||||
static PRLogModuleInfo* sLogModule;
|
||||
if (!sLogModule) {
|
||||
sLogModule = PR_NewLogModule("SourceBufferResource");
|
||||
}
|
||||
return sLogModule;
|
||||
}
|
||||
|
||||
#define SBR_DEBUG(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#define SBR_DEBUGV(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
|
||||
#else
|
||||
#define MSE_DEBUG(...)
|
||||
#define SBR_DEBUG(...)
|
||||
#define SBR_DEBUGV(...)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
@ -33,7 +43,7 @@ nsresult
|
||||
SourceBufferResource::Close()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
MSE_DEBUG("%p SBR::Close", this);
|
||||
SBR_DEBUG("SourceBufferResource(%p)::Close", this);
|
||||
//MOZ_ASSERT(!mClosed);
|
||||
mClosed = true;
|
||||
mon.NotifyAll();
|
||||
@ -43,26 +53,24 @@ SourceBufferResource::Close()
|
||||
nsresult
|
||||
SourceBufferResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
|
||||
{
|
||||
SBR_DEBUGV("SourceBufferResource(%p)::Read(aBuffer=%p, aCount=%u, aBytes=%p)",
|
||||
this, aBytes, aCount, aBytes);
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
bool blockingRead = !!aBytes;
|
||||
|
||||
while (blockingRead &&
|
||||
!mEnded &&
|
||||
mOffset + aCount > static_cast<uint64_t>(GetLength())) {
|
||||
MSE_DEBUG("%p SBR::Read waiting for data", this);
|
||||
SBR_DEBUGV("SourceBufferResource(%p)::Read waiting for data", this);
|
||||
mon.Wait();
|
||||
}
|
||||
|
||||
uint32_t available = GetLength() - mOffset;
|
||||
uint32_t count = std::min(aCount, available);
|
||||
if (!PR_GetEnv("MOZ_QUIET")) {
|
||||
MSE_DEBUG("%p SBR::Read aCount=%u length=%u offset=%u "
|
||||
"available=%u count=%u, blocking=%d bufComplete=%d",
|
||||
this, aCount, GetLength(), mOffset, available, count,
|
||||
blockingRead, mEnded);
|
||||
}
|
||||
SBR_DEBUGV("SourceBufferResource(%p)::Read() mOffset=%llu GetLength()=%u available=%u count=%u mEnded=%d",
|
||||
this, mOffset, GetLength(), available, count, mEnded);
|
||||
if (available == 0) {
|
||||
MSE_DEBUG("%p SBR::Read EOF", this);
|
||||
SBR_DEBUGV("SourceBufferResource(%p)::Read() reached EOF", this);
|
||||
*aBytes = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -76,6 +84,8 @@ SourceBufferResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
|
||||
nsresult
|
||||
SourceBufferResource::ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes)
|
||||
{
|
||||
SBR_DEBUG("SourceBufferResource(%p)::ReadAt(aOffset=%lld, aBuffer=%p, aCount=%u, aBytes=%p)",
|
||||
this, aOffset, aBytes, aCount, aBytes);
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
nsresult rv = Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
|
||||
if (NS_FAILED(rv)) {
|
||||
@ -87,6 +97,7 @@ SourceBufferResource::ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, ui
|
||||
nsresult
|
||||
SourceBufferResource::Seek(int32_t aWhence, int64_t aOffset)
|
||||
{
|
||||
SBR_DEBUG("SourceBufferResource(%p)::Seek(aWhence=%d, aOffset=%lld)", this, aWhence, aOffset);
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
if (mClosed) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -105,6 +116,8 @@ SourceBufferResource::Seek(int32_t aWhence, int64_t aOffset)
|
||||
break;
|
||||
}
|
||||
|
||||
SBR_DEBUGV("SourceBufferResource(%p)::Seek() newOffset=%lld GetOffset()=%llu GetLength()=%llu)",
|
||||
this, newOffset, mInputBuffer.GetOffset(), GetLength());
|
||||
if (newOffset < 0 || uint64_t(newOffset) < mInputBuffer.GetOffset() || newOffset > GetLength()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -118,17 +131,18 @@ SourceBufferResource::Seek(int32_t aWhence, int64_t aOffset)
|
||||
nsresult
|
||||
SourceBufferResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
nsresult rv = Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return Read(aBuffer, aCount, nullptr);
|
||||
SBR_DEBUG("SourceBufferResource(%p)::ReadFromCache(aBuffer=%p, aOffset=%lld, aCount=%u)",
|
||||
this, aBuffer, aOffset, aCount);
|
||||
int64_t oldOffset = mOffset;
|
||||
nsresult rv = ReadAt(aOffset, aBuffer, aCount, nullptr);
|
||||
mOffset = oldOffset;
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
SourceBufferResource::EvictData(uint32_t aThreshold)
|
||||
{
|
||||
SBR_DEBUG("SourceBufferResource(%p)::EvictData(aThreshold=%u)", this, aThreshold);
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
return mInputBuffer.Evict(mOffset, aThreshold);
|
||||
}
|
||||
@ -136,6 +150,7 @@ SourceBufferResource::EvictData(uint32_t aThreshold)
|
||||
void
|
||||
SourceBufferResource::EvictBefore(uint64_t aOffset)
|
||||
{
|
||||
SBR_DEBUG("SourceBufferResource(%p)::EvictBefore(aOffset=%llu)", this, aOffset);
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
// If aOffset is past the current playback offset we don't evict.
|
||||
if (aOffset < mOffset) {
|
||||
@ -146,6 +161,7 @@ SourceBufferResource::EvictBefore(uint64_t aOffset)
|
||||
void
|
||||
SourceBufferResource::AppendData(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
SBR_DEBUG("SourceBufferResource(%p)::AppendData(aData=%p, aLength=%u)", this, aData, aLength);
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
mInputBuffer.PushBack(new ResourceItem(aData, aLength));
|
||||
mon.NotifyAll();
|
||||
@ -154,6 +170,7 @@ SourceBufferResource::AppendData(const uint8_t* aData, uint32_t aLength)
|
||||
void
|
||||
SourceBufferResource::Ended()
|
||||
{
|
||||
SBR_DEBUG("SourceBufferResource(%p)::Ended()", this);
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
mEnded = true;
|
||||
mon.NotifyAll();
|
||||
@ -161,8 +178,8 @@ SourceBufferResource::Ended()
|
||||
|
||||
SourceBufferResource::~SourceBufferResource()
|
||||
{
|
||||
SBR_DEBUG("SourceBufferResource(%p)::~SourceBufferResource()", this);
|
||||
MOZ_COUNT_DTOR(SourceBufferResource);
|
||||
MSE_DEBUG("%p SBR::~SBR", this);
|
||||
}
|
||||
|
||||
SourceBufferResource::SourceBufferResource(nsIPrincipal* aPrincipal,
|
||||
@ -174,8 +191,9 @@ SourceBufferResource::SourceBufferResource(nsIPrincipal* aPrincipal,
|
||||
, mClosed(false)
|
||||
, mEnded(false)
|
||||
{
|
||||
SBR_DEBUG("SourceBufferResource(%p)::SourceBufferResource(aPrincipal=%p, aType=%s)",
|
||||
this, aPrincipal, nsCString(aType).get());
|
||||
MOZ_COUNT_CTOR(SourceBufferResource);
|
||||
MSE_DEBUG("%p SBR::SBR()", this);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
127
content/media/mediasource/SubBufferDecoder.cpp
Normal file
127
content/media/mediasource/SubBufferDecoder.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
/* 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 "SubBufferDecoder.h"
|
||||
#include "MediaSourceDecoder.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* GetMediaSourceLog();
|
||||
extern PRLogModuleInfo* GetMediaSourceAPILog();
|
||||
|
||||
#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define MSE_DEBUG(...)
|
||||
#define MSE_API(...)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ReentrantMonitor;
|
||||
|
||||
namespace layers {
|
||||
|
||||
class ImageContainer;
|
||||
|
||||
} // namespace layers
|
||||
|
||||
|
||||
ReentrantMonitor&
|
||||
SubBufferDecoder::GetReentrantMonitor()
|
||||
{
|
||||
return mParentDecoder->GetReentrantMonitor();
|
||||
}
|
||||
|
||||
bool
|
||||
SubBufferDecoder::OnStateMachineThread() const
|
||||
{
|
||||
return mParentDecoder->OnStateMachineThread();
|
||||
}
|
||||
|
||||
bool
|
||||
SubBufferDecoder::OnDecodeThread() const
|
||||
{
|
||||
return mParentDecoder->OnDecodeThread();
|
||||
}
|
||||
|
||||
SourceBufferResource*
|
||||
SubBufferDecoder::GetResource() const
|
||||
{
|
||||
return static_cast<SourceBufferResource*>(mResource.get());
|
||||
}
|
||||
|
||||
void
|
||||
SubBufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded)
|
||||
{
|
||||
return mParentDecoder->NotifyDecodedFrames(aParsed, aDecoded);
|
||||
}
|
||||
|
||||
void
|
||||
SubBufferDecoder::SetMediaDuration(int64_t aDuration)
|
||||
{
|
||||
mMediaDuration = aDuration;
|
||||
}
|
||||
|
||||
void
|
||||
SubBufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
{
|
||||
MSE_DEBUG("SubBufferDecoder(%p)::UpdateEstimatedMediaDuration(aDuration=%lld)", this, aDuration);
|
||||
}
|
||||
|
||||
void
|
||||
SubBufferDecoder::SetMediaSeekable(bool aMediaSeekable)
|
||||
{
|
||||
MSE_DEBUG("SubBufferDecoder(%p)::SetMediaSeekable(aMediaSeekable=%d)", this, aMediaSeekable);
|
||||
}
|
||||
|
||||
layers::ImageContainer*
|
||||
SubBufferDecoder::GetImageContainer()
|
||||
{
|
||||
return mParentDecoder->GetImageContainer();
|
||||
}
|
||||
|
||||
MediaDecoderOwner*
|
||||
SubBufferDecoder::GetOwner()
|
||||
{
|
||||
return mParentDecoder->GetOwner();
|
||||
}
|
||||
|
||||
void
|
||||
SubBufferDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
|
||||
// XXX: Params make no sense to parent decoder as it relates to a
|
||||
// specific SubBufferDecoder's data stream. Pass bogus values here to
|
||||
// force parent decoder's state machine to recompute end time for
|
||||
// infinite length media.
|
||||
mParentDecoder->NotifyDataArrived(nullptr, 0, 0);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SubBufferDecoder::GetBuffered(dom::TimeRanges* aBuffered)
|
||||
{
|
||||
// XXX: Need mStartTime (from StateMachine) instead of passing 0.
|
||||
return mReader->GetBuffered(aBuffered, 0);
|
||||
}
|
||||
|
||||
int64_t
|
||||
SubBufferDecoder::ConvertToByteOffset(double aTime)
|
||||
{
|
||||
// Uses a conversion based on (aTime/duration) * length. For the
|
||||
// purposes of eviction this should be adequate since we have the
|
||||
// byte threshold as well to ensure data actually gets evicted and
|
||||
// we ensure we don't evict before the current playable point.
|
||||
if (mMediaDuration == -1) {
|
||||
return -1;
|
||||
}
|
||||
int64_t length = GetResource()->GetLength();
|
||||
MOZ_ASSERT(length > 0);
|
||||
int64_t offset = (aTime / (double(mMediaDuration) / USECS_PER_S)) * length;
|
||||
return offset;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -12,7 +12,15 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaResource;
|
||||
class MediaSourceDecoder;
|
||||
class MediaDecoderReader;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class TimeRanges;
|
||||
|
||||
} // namespace dom
|
||||
|
||||
class SubBufferDecoder : public BufferDecoder
|
||||
{
|
||||
@ -21,7 +29,7 @@ public:
|
||||
// of the caller to manage the memory of the MediaResource object.
|
||||
SubBufferDecoder(MediaResource* aResource, MediaSourceDecoder* aParentDecoder)
|
||||
: BufferDecoder(aResource), mParentDecoder(aParentDecoder), mReader(nullptr)
|
||||
, mMediaDuration(-1), mMediaStartTime(0), mDiscarded(false)
|
||||
, mMediaDuration(-1), mDiscarded(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -47,22 +55,10 @@ public:
|
||||
virtual layers::ImageContainer* GetImageContainer() MOZ_OVERRIDE;
|
||||
virtual MediaDecoderOwner* GetOwner() MOZ_OVERRIDE;
|
||||
|
||||
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
|
||||
// XXX: Params make no sense to parent decoder as it relates to a
|
||||
// specific SubBufferDecoder's data stream. Pass bogus values here to
|
||||
// force parent decoder's state machine to recompute end time for
|
||||
// infinite length media.
|
||||
mParentDecoder->NotifyDataArrived(nullptr, 0, 0);
|
||||
}
|
||||
|
||||
nsresult GetBuffered(dom::TimeRanges* aBuffered)
|
||||
{
|
||||
// XXX: Need mStartTime (from StateMachine) instead of passing 0.
|
||||
return mReader->GetBuffered(aBuffered, 0);
|
||||
}
|
||||
// Warning: these mirror calls from MediaDecoder, but this class's base is
|
||||
// AbstractMediaDecoder, which does not supply this interface.
|
||||
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
|
||||
nsresult GetBuffered(dom::TimeRanges* aBuffered);
|
||||
|
||||
// Given a time convert it into an approximate byte offset from the
|
||||
// cached data. Returns -1 if no such value is computable.
|
||||
@ -73,16 +69,6 @@ public:
|
||||
return mMediaDuration;
|
||||
}
|
||||
|
||||
int64_t GetMediaStartTime()
|
||||
{
|
||||
return mMediaStartTime;
|
||||
}
|
||||
|
||||
void SetMediaStartTime(int64_t aMediaStartTime)
|
||||
{
|
||||
mMediaStartTime = aMediaStartTime;
|
||||
}
|
||||
|
||||
bool IsDiscarded()
|
||||
{
|
||||
return mDiscarded;
|
||||
@ -98,7 +84,6 @@ private:
|
||||
MediaSourceDecoder* mParentDecoder;
|
||||
nsRefPtr<MediaDecoderReader> mReader;
|
||||
int64_t mMediaDuration;
|
||||
int64_t mMediaStartTime;
|
||||
bool mDiscarded;
|
||||
};
|
||||
|
||||
|
@ -22,6 +22,7 @@ UNIFIED_SOURCES += [
|
||||
'SourceBuffer.cpp',
|
||||
'SourceBufferList.cpp',
|
||||
'SourceBufferResource.cpp',
|
||||
'SubBufferDecoder.cpp',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -32,8 +32,8 @@ VIntLength(unsigned char aFirstByte, uint32_t* aMask)
|
||||
}
|
||||
|
||||
void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
||||
nsTArray<WebMTimeDataOffset>& aMapping,
|
||||
ReentrantMonitor& aReentrantMonitor)
|
||||
nsTArray<WebMTimeDataOffset>& aMapping,
|
||||
ReentrantMonitor& aReentrantMonitor)
|
||||
{
|
||||
static const unsigned char CLUSTER_ID[] = { 0x1f, 0x43, 0xb6, 0x75 };
|
||||
static const unsigned char TIMECODE_ID = 0xe7;
|
||||
@ -60,6 +60,7 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
||||
// and return to CLUSTER_SYNC.
|
||||
if (mClusterIDPos == sizeof(CLUSTER_ID)) {
|
||||
mClusterIDPos = 0;
|
||||
mClusterOffset = mCurrentOffset + (p - aBuffer) - 1;
|
||||
mState = READ_VINT;
|
||||
mNextState = TIMECODE_SYNC;
|
||||
}
|
||||
@ -140,9 +141,14 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(aReentrantMonitor);
|
||||
uint32_t idx = aMapping.IndexOfFirstElementGt(mBlockOffset);
|
||||
if (idx == 0 || !(aMapping[idx-1] == mBlockOffset)) {
|
||||
WebMTimeDataOffset entry(mBlockOffset, mClusterTimecode + mBlockTimecode);
|
||||
aMapping.InsertElementAt(idx, entry);
|
||||
if (idx == 0 || !(aMapping[idx - 1] == mBlockOffset)) {
|
||||
// Don't insert invalid negative timecodes.
|
||||
if (mBlockOffset > 0 || mClusterTimecode > uint16_t(abs(mBlockOffset))) {
|
||||
WebMTimeDataOffset entry(mBlockOffset,
|
||||
mClusterTimecode + mBlockTimecode,
|
||||
mClusterOffset);
|
||||
aMapping.InsertElementAt(idx, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,18 +183,18 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
||||
}
|
||||
|
||||
bool WebMBufferedState::CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
|
||||
uint64_t* aStartTime, uint64_t* aEndTime)
|
||||
uint64_t* aStartTime, uint64_t* aEndTime)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
// Find the first WebMTimeDataOffset at or after aStartOffset.
|
||||
uint32_t start = mTimeMapping.IndexOfFirstElementGt(aStartOffset-1);
|
||||
uint32_t start = mTimeMapping.IndexOfFirstElementGt(aStartOffset - 1);
|
||||
if (start == mTimeMapping.Length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the first WebMTimeDataOffset at or before aEndOffset.
|
||||
uint32_t end = mTimeMapping.IndexOfFirstElementGt(aEndOffset-1);
|
||||
uint32_t end = mTimeMapping.IndexOfFirstElementGt(aEndOffset - 1);
|
||||
if (end > 0) {
|
||||
end -= 1;
|
||||
}
|
||||
@ -216,13 +222,14 @@ bool WebMBufferedState::CalculateBufferedForRange(int64_t aStartOffset, int64_t
|
||||
|
||||
*aStartTime = mTimeMapping[start].mTimecode;
|
||||
*aEndTime = mTimeMapping[end].mTimecode;
|
||||
*aEndTime += mTimeMapping[end].mTimecode - mTimeMapping[end - 1].mTimecode;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebMBufferedState::GetOffsetForTime(uint64_t aTime, int64_t* aOffset)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
WebMTimeDataOffset result(0,0);
|
||||
WebMTimeDataOffset result(0, 0, 0);
|
||||
|
||||
for (uint32_t i = 0; i < mTimeMapping.Length(); ++i) {
|
||||
WebMTimeDataOffset o = mTimeMapping[i];
|
||||
|
@ -21,8 +21,8 @@ class TimeRanges;
|
||||
// scale before use.
|
||||
struct WebMTimeDataOffset
|
||||
{
|
||||
WebMTimeDataOffset(int64_t aOffset, uint64_t aTimecode)
|
||||
: mOffset(aOffset), mTimecode(aTimecode)
|
||||
WebMTimeDataOffset(int64_t aOffset, uint64_t aTimecode, int64_t aSyncOffset)
|
||||
: mOffset(aOffset), mTimecode(aTimecode), mSyncOffset(aSyncOffset)
|
||||
{}
|
||||
|
||||
bool operator==(int64_t aOffset) const {
|
||||
@ -35,6 +35,7 @@ struct WebMTimeDataOffset
|
||||
|
||||
int64_t mOffset;
|
||||
uint64_t mTimecode;
|
||||
int64_t mSyncOffset;
|
||||
};
|
||||
|
||||
// A simple WebM parser that produces data offset to timecode pairs as it
|
||||
@ -165,6 +166,11 @@ private:
|
||||
// Cluster-level timecode.
|
||||
uint64_t mClusterTimecode;
|
||||
|
||||
// Start offset of the cluster currently being parsed. Used as the sync
|
||||
// point offset for the offset-to-time mapping as each block timecode is
|
||||
// been parsed.
|
||||
int64_t mClusterOffset;
|
||||
|
||||
// Start offset of the block currently being parsed. Used as the byte
|
||||
// offset for the offset-to-time mapping once the block timecode has been
|
||||
// parsed.
|
||||
|
238
dom/animation/Animation.cpp
Normal file
238
dom/animation/Animation.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
||||
/* 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 "mozilla/dom/Animation.h"
|
||||
#include "mozilla/dom/AnimationBinding.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
ComputedTimingFunction::Init(const nsTimingFunction &aFunction)
|
||||
{
|
||||
mType = aFunction.mType;
|
||||
if (mType == nsTimingFunction::Function) {
|
||||
mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1,
|
||||
aFunction.mFunc.mX2, aFunction.mFunc.mY2);
|
||||
} else {
|
||||
mSteps = aFunction.mSteps;
|
||||
}
|
||||
}
|
||||
|
||||
static inline double
|
||||
StepEnd(uint32_t aSteps, double aPortion)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(0.0 <= aPortion && aPortion <= 1.0, "out of range");
|
||||
uint32_t step = uint32_t(aPortion * aSteps); // floor
|
||||
return double(step) / double(aSteps);
|
||||
}
|
||||
|
||||
double
|
||||
ComputedTimingFunction::GetValue(double aPortion) const
|
||||
{
|
||||
switch (mType) {
|
||||
case nsTimingFunction::Function:
|
||||
return mTimingFunction.GetSplineValue(aPortion);
|
||||
case nsTimingFunction::StepStart:
|
||||
// There are diagrams in the spec that seem to suggest this check
|
||||
// and the bounds point should not be symmetric with StepEnd, but
|
||||
// should actually step up at rather than immediately after the
|
||||
// fraction points. However, we rely on rounding negative values
|
||||
// up to zero, so we can't do that. And it's not clear the spec
|
||||
// really meant it.
|
||||
return 1.0 - StepEnd(mSteps, 1.0 - aPortion);
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(false, "bad type");
|
||||
// fall through
|
||||
case nsTimingFunction::StepEnd:
|
||||
return StepEnd(mSteps, aPortion);
|
||||
}
|
||||
}
|
||||
|
||||
// In the Web Animations model, the time fraction can be outside the range
|
||||
// [0.0, 1.0] but it shouldn't be Infinity.
|
||||
const double ComputedTiming::kNullTimeFraction = PositiveInfinity<double>();
|
||||
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Animation, mDocument)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Animation, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Animation, Release)
|
||||
|
||||
JSObject*
|
||||
Animation::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return AnimationBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
void
|
||||
Animation::SetParentTime(Nullable<TimeDuration> aParentTime)
|
||||
{
|
||||
mParentTime = aParentTime;
|
||||
}
|
||||
|
||||
ComputedTiming
|
||||
Animation::GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
|
||||
const AnimationTiming& aTiming)
|
||||
{
|
||||
const TimeDuration zeroDuration;
|
||||
|
||||
// Currently we expect negative durations to be picked up during CSS
|
||||
// parsing but when we start receiving timing parameters from other sources
|
||||
// we will need to clamp negative durations here.
|
||||
// For now, if we're hitting this it probably means we're overflowing
|
||||
// integer arithmetic in mozilla::TimeStamp.
|
||||
MOZ_ASSERT(aTiming.mIterationDuration >= zeroDuration,
|
||||
"Expecting iteration duration >= 0");
|
||||
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
ComputedTiming result;
|
||||
|
||||
result.mActiveDuration = ActiveDuration(aTiming);
|
||||
|
||||
// The default constructor for ComputedTiming sets all other members to
|
||||
// values consistent with an animation that has not been sampled.
|
||||
if (aLocalTime.IsNull()) {
|
||||
return result;
|
||||
}
|
||||
const TimeDuration& localTime = aLocalTime.Value();
|
||||
|
||||
// When we finish exactly at the end of an iteration we need to report
|
||||
// the end of the final iteration and not the start of the next iteration
|
||||
// so we set up a flag for that case.
|
||||
bool isEndOfFinalIteration = false;
|
||||
|
||||
// Get the normalized time within the active interval.
|
||||
TimeDuration activeTime;
|
||||
// FIXME: The following check that the active duration is not equal to Forever
|
||||
// is a temporary workaround to avoid overflow and should be removed once
|
||||
// bug 1039924 is fixed.
|
||||
if (result.mActiveDuration != TimeDuration::Forever() &&
|
||||
localTime >= aTiming.mDelay + result.mActiveDuration) {
|
||||
result.mPhase = ComputedTiming::AnimationPhase_After;
|
||||
if (!aTiming.FillsForwards()) {
|
||||
// The animation isn't active or filling at this time.
|
||||
result.mTimeFraction = ComputedTiming::kNullTimeFraction;
|
||||
return result;
|
||||
}
|
||||
activeTime = result.mActiveDuration;
|
||||
// Note that infinity == floor(infinity) so this will also be true when we
|
||||
// have finished an infinitely repeating animation of zero duration.
|
||||
isEndOfFinalIteration =
|
||||
aTiming.mIterationCount != 0.0 &&
|
||||
aTiming.mIterationCount == floor(aTiming.mIterationCount);
|
||||
} else if (localTime < aTiming.mDelay) {
|
||||
result.mPhase = ComputedTiming::AnimationPhase_Before;
|
||||
if (!aTiming.FillsBackwards()) {
|
||||
// The animation isn't active or filling at this time.
|
||||
result.mTimeFraction = ComputedTiming::kNullTimeFraction;
|
||||
return result;
|
||||
}
|
||||
// activeTime is zero
|
||||
} else {
|
||||
MOZ_ASSERT(result.mActiveDuration != zeroDuration,
|
||||
"How can we be in the middle of a zero-duration interval?");
|
||||
result.mPhase = ComputedTiming::AnimationPhase_Active;
|
||||
activeTime = localTime - aTiming.mDelay;
|
||||
}
|
||||
|
||||
// Get the position within the current iteration.
|
||||
TimeDuration iterationTime;
|
||||
if (aTiming.mIterationDuration != zeroDuration) {
|
||||
iterationTime = isEndOfFinalIteration
|
||||
? aTiming.mIterationDuration
|
||||
: activeTime % aTiming.mIterationDuration;
|
||||
} /* else, iterationTime is zero */
|
||||
|
||||
// Determine the 0-based index of the current iteration.
|
||||
if (isEndOfFinalIteration) {
|
||||
result.mCurrentIteration =
|
||||
aTiming.mIterationCount == NS_IEEEPositiveInfinity()
|
||||
? UINT64_MAX // FIXME: When we return this via the API we'll need
|
||||
// to make sure it ends up being infinity.
|
||||
: static_cast<uint64_t>(aTiming.mIterationCount) - 1;
|
||||
} else if (activeTime == zeroDuration) {
|
||||
// If the active time is zero we're either in the first iteration
|
||||
// (including filling backwards) or we have finished an animation with an
|
||||
// iteration duration of zero that is filling forwards (but we're not at
|
||||
// the exact end of an iteration since we deal with that above).
|
||||
result.mCurrentIteration =
|
||||
result.mPhase == ComputedTiming::AnimationPhase_After
|
||||
? static_cast<uint64_t>(aTiming.mIterationCount) // floor
|
||||
: 0;
|
||||
} else {
|
||||
result.mCurrentIteration =
|
||||
static_cast<uint64_t>(activeTime / aTiming.mIterationDuration); // floor
|
||||
}
|
||||
|
||||
// Normalize the iteration time into a fraction of the iteration duration.
|
||||
if (result.mPhase == ComputedTiming::AnimationPhase_Before) {
|
||||
result.mTimeFraction = 0.0;
|
||||
} else if (result.mPhase == ComputedTiming::AnimationPhase_After) {
|
||||
result.mTimeFraction = isEndOfFinalIteration
|
||||
? 1.0
|
||||
: fmod(aTiming.mIterationCount, 1.0f);
|
||||
} else {
|
||||
// We are in the active phase so the iteration duration can't be zero.
|
||||
MOZ_ASSERT(aTiming.mIterationDuration != zeroDuration,
|
||||
"In the active phase of a zero-duration animation?");
|
||||
result.mTimeFraction =
|
||||
aTiming.mIterationDuration == TimeDuration::Forever()
|
||||
? 0.0
|
||||
: iterationTime / aTiming.mIterationDuration;
|
||||
}
|
||||
|
||||
bool thisIterationReverse = false;
|
||||
switch (aTiming.mDirection) {
|
||||
case NS_STYLE_ANIMATION_DIRECTION_NORMAL:
|
||||
thisIterationReverse = false;
|
||||
break;
|
||||
case NS_STYLE_ANIMATION_DIRECTION_REVERSE:
|
||||
thisIterationReverse = true;
|
||||
break;
|
||||
case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE:
|
||||
thisIterationReverse = (result.mCurrentIteration & 1) == 1;
|
||||
break;
|
||||
case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE:
|
||||
thisIterationReverse = (result.mCurrentIteration & 1) == 0;
|
||||
break;
|
||||
}
|
||||
if (thisIterationReverse) {
|
||||
result.mTimeFraction = 1.0 - result.mTimeFraction;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TimeDuration
|
||||
Animation::ActiveDuration(const AnimationTiming& aTiming)
|
||||
{
|
||||
if (aTiming.mIterationCount == mozilla::PositiveInfinity<float>()) {
|
||||
// An animation that repeats forever has an infinite active duration
|
||||
// unless its iteration duration is zero, in which case it has a zero
|
||||
// active duration.
|
||||
const TimeDuration zeroDuration;
|
||||
return aTiming.mIterationDuration == zeroDuration
|
||||
? zeroDuration
|
||||
: TimeDuration::Forever();
|
||||
}
|
||||
return aTiming.mIterationDuration.MultDouble(aTiming.mIterationCount);
|
||||
}
|
||||
|
||||
bool
|
||||
Animation::HasAnimationOfProperty(nsCSSProperty aProperty) const
|
||||
{
|
||||
for (size_t propIdx = 0, propEnd = mProperties.Length();
|
||||
propIdx != propEnd; ++propIdx) {
|
||||
if (aProperty == mProperties[propIdx].mProperty) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
259
dom/animation/Animation.h
Normal file
259
dom/animation/Animation.h
Normal file
@ -0,0 +1,259 @@
|
||||
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
||||
/* 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_dom_Animation_h
|
||||
#define mozilla_dom_Animation_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "nsSMILKeySpline.h"
|
||||
#include "nsStyleStruct.h" // for nsTimingFunction
|
||||
|
||||
struct JSContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Input timing parameters.
|
||||
*
|
||||
* Eventually this will represent all the input timing parameters specified
|
||||
* by content but for now it encapsulates just the subset of those
|
||||
* parameters passed to GetPositionInIteration.
|
||||
*/
|
||||
struct AnimationTiming
|
||||
{
|
||||
TimeDuration mIterationDuration;
|
||||
TimeDuration mDelay;
|
||||
float mIterationCount; // mozilla::PositiveInfinity<float>() means infinite
|
||||
uint8_t mDirection;
|
||||
uint8_t mFillMode;
|
||||
|
||||
bool FillsForwards() const {
|
||||
return mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BOTH ||
|
||||
mFillMode == NS_STYLE_ANIMATION_FILL_MODE_FORWARDS;
|
||||
}
|
||||
bool FillsBackwards() const {
|
||||
return mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BOTH ||
|
||||
mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BACKWARDS;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores the results of calculating the timing properties of an animation
|
||||
* at a given sample time.
|
||||
*/
|
||||
struct ComputedTiming
|
||||
{
|
||||
ComputedTiming()
|
||||
: mTimeFraction(kNullTimeFraction)
|
||||
, mCurrentIteration(0)
|
||||
, mPhase(AnimationPhase_Null)
|
||||
{ }
|
||||
|
||||
static const double kNullTimeFraction;
|
||||
|
||||
// The total duration of the animation including all iterations.
|
||||
// Will equal TimeDuration::Forever() if the animation repeats indefinitely.
|
||||
TimeDuration mActiveDuration;
|
||||
|
||||
// Will be kNullTimeFraction if the animation is neither animating nor
|
||||
// filling at the sampled time.
|
||||
double mTimeFraction;
|
||||
|
||||
// Zero-based iteration index (meaningless if mTimeFraction is
|
||||
// kNullTimeFraction).
|
||||
uint64_t mCurrentIteration;
|
||||
|
||||
enum {
|
||||
// Not sampled (null sample time)
|
||||
AnimationPhase_Null,
|
||||
// Sampled prior to the start of the active interval
|
||||
AnimationPhase_Before,
|
||||
// Sampled within the active interval
|
||||
AnimationPhase_Active,
|
||||
// Sampled after (or at) the end of the active interval
|
||||
AnimationPhase_After
|
||||
} mPhase;
|
||||
};
|
||||
|
||||
class ComputedTimingFunction
|
||||
{
|
||||
public:
|
||||
typedef nsTimingFunction::Type Type;
|
||||
void Init(const nsTimingFunction &aFunction);
|
||||
double GetValue(double aPortion) const;
|
||||
const nsSMILKeySpline* GetFunction() const {
|
||||
NS_ASSERTION(mType == nsTimingFunction::Function, "Type mismatch");
|
||||
return &mTimingFunction;
|
||||
}
|
||||
Type GetType() const { return mType; }
|
||||
uint32_t GetSteps() const { return mSteps; }
|
||||
|
||||
private:
|
||||
Type mType;
|
||||
nsSMILKeySpline mTimingFunction;
|
||||
uint32_t mSteps;
|
||||
};
|
||||
|
||||
struct AnimationPropertySegment
|
||||
{
|
||||
float mFromKey, mToKey;
|
||||
StyleAnimationValue mFromValue, mToValue;
|
||||
ComputedTimingFunction mTimingFunction;
|
||||
};
|
||||
|
||||
struct AnimationProperty
|
||||
{
|
||||
nsCSSProperty mProperty;
|
||||
InfallibleTArray<AnimationPropertySegment> mSegments;
|
||||
};
|
||||
|
||||
struct ElementPropertyTransition;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class Animation : public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
Animation(nsIDocument* aDocument, const AnimationTiming &aTiming)
|
||||
: mDocument(aDocument)
|
||||
, mTiming(aTiming)
|
||||
, mIsFinishedTransition(false)
|
||||
, mLastNotification(LAST_NOTIFICATION_NONE)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Animation)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(Animation)
|
||||
|
||||
nsIDocument* GetParentObject() const { return mDocument; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
// FIXME: If we succeed in moving transition-specific code to a type of
|
||||
// AnimationEffect (as per the Web Animations API) we should remove these
|
||||
// virtual methods.
|
||||
virtual ElementPropertyTransition* AsTransition() { return nullptr; }
|
||||
virtual const ElementPropertyTransition* AsTransition() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SetParentTime(Nullable<TimeDuration> aParentTime);
|
||||
|
||||
const AnimationTiming& Timing() const {
|
||||
return mTiming;
|
||||
}
|
||||
AnimationTiming& Timing() {
|
||||
return mTiming;
|
||||
}
|
||||
|
||||
// Return the duration from the start the active interval to the point where
|
||||
// the animation begins playback. This is zero unless the animation has
|
||||
// a negative delay in which case it is the absolute value of the delay.
|
||||
// This is used for setting the elapsedTime member of CSS AnimationEvents.
|
||||
TimeDuration InitialAdvance() const {
|
||||
return std::max(TimeDuration(), mTiming.mDelay * -1);
|
||||
}
|
||||
|
||||
Nullable<TimeDuration> GetLocalTime() const {
|
||||
// Since the *animation* start time is currently always zero, the local
|
||||
// time is equal to the parent time.
|
||||
return mParentTime;
|
||||
}
|
||||
|
||||
// This function takes as input the timing parameters of an animation and
|
||||
// returns the computed timing at the specified local time.
|
||||
//
|
||||
// The local time may be null in which case only static parameters such as the
|
||||
// active duration are calculated. All other members of the returned object
|
||||
// are given a null/initial value.
|
||||
//
|
||||
// This function returns ComputedTiming::kNullTimeFraction for the
|
||||
// mTimeFraction member of the return value if the animation should not be
|
||||
// run (because it is not currently active and is not filling at this time).
|
||||
static ComputedTiming
|
||||
GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
|
||||
const AnimationTiming& aTiming);
|
||||
|
||||
// Shortcut for that gets the computed timing using the current local time as
|
||||
// calculated from the timeline time.
|
||||
ComputedTiming GetComputedTiming(const AnimationTiming* aTiming
|
||||
= nullptr) const {
|
||||
return GetComputedTimingAt(GetLocalTime(), aTiming ? *aTiming : mTiming);
|
||||
}
|
||||
|
||||
// Return the duration of the active interval for the given timing parameters.
|
||||
static TimeDuration ActiveDuration(const AnimationTiming& aTiming);
|
||||
|
||||
// After transitions finish they need to be retained for one throttle-able
|
||||
// cycle (for reasons see explanation in
|
||||
// layout/style/nsTransitionManager.cpp).
|
||||
// In the meantime, however, they should be ignored.
|
||||
bool IsFinishedTransition() const {
|
||||
return mIsFinishedTransition;
|
||||
}
|
||||
|
||||
void SetIsFinishedTransition() {
|
||||
MOZ_ASSERT(AsTransition(),
|
||||
"Calling SetIsFinishedTransition but it's not a transition");
|
||||
mIsFinishedTransition = true;
|
||||
}
|
||||
|
||||
bool IsCurrent() const {
|
||||
if (IsFinishedTransition()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
return computedTiming.mPhase == ComputedTiming::AnimationPhase_Before ||
|
||||
computedTiming.mPhase == ComputedTiming::AnimationPhase_Active;
|
||||
}
|
||||
|
||||
enum {
|
||||
LAST_NOTIFICATION_NONE = uint64_t(-1),
|
||||
LAST_NOTIFICATION_END = uint64_t(-2)
|
||||
};
|
||||
uint64_t LastNotification() const { return mLastNotification; }
|
||||
void SetLastNotification(uint64_t aLastNotification) {
|
||||
mLastNotification = aLastNotification;
|
||||
}
|
||||
|
||||
bool HasAnimationOfProperty(nsCSSProperty aProperty) const;
|
||||
const InfallibleTArray<AnimationProperty>& Properties() const {
|
||||
return mProperties;
|
||||
}
|
||||
InfallibleTArray<AnimationProperty>& Properties() {
|
||||
return mProperties;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~Animation() { }
|
||||
|
||||
// We use a document for a parent object since the other likely candidate,
|
||||
// the target element, can be empty.
|
||||
nsRefPtr<nsIDocument> mDocument;
|
||||
Nullable<TimeDuration> mParentTime;
|
||||
|
||||
AnimationTiming mTiming;
|
||||
// A flag to mark transitions that have finished and are due to
|
||||
// be removed on the next throttle-able cycle.
|
||||
bool mIsFinishedTransition;
|
||||
// One of the LAST_NOTIFICATION_* constants, or an integer for the iteration
|
||||
// whose start we last notified on.
|
||||
uint64_t mLastNotification;
|
||||
|
||||
InfallibleTArray<AnimationProperty> mProperties;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Animation_h
|
91
dom/animation/AnimationPlayer.cpp
Normal file
91
dom/animation/AnimationPlayer.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
||||
/* 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 "AnimationPlayer.h"
|
||||
#include "mozilla/dom/AnimationPlayerBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline, mSource)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationPlayer, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationPlayer, Release)
|
||||
|
||||
JSObject*
|
||||
AnimationPlayer::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return dom::AnimationPlayerBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
double
|
||||
AnimationPlayer::StartTime() const
|
||||
{
|
||||
Nullable<double> startTime = mTimeline->ToTimelineTime(mStartTime);
|
||||
return startTime.IsNull() ? 0.0 : startTime.Value();
|
||||
}
|
||||
|
||||
double
|
||||
AnimationPlayer::CurrentTime() const
|
||||
{
|
||||
Nullable<TimeDuration> currentTime = GetCurrentTimeDuration();
|
||||
|
||||
// The current time is currently only going to be null when don't have a
|
||||
// refresh driver (e.g. because we are in a display:none iframe).
|
||||
//
|
||||
// Web Animations says that in this case we should use a timeline time of
|
||||
// 0 (the "effective timeline time") and calculate the current time from that.
|
||||
// Doing that, however, requires storing the start time as an offset rather
|
||||
// than a timestamp so for now we just return 0.
|
||||
//
|
||||
// FIXME: Store player start time and pause start as offsets rather than
|
||||
// timestamps and return the appropriate current time when the timeline time
|
||||
// is null.
|
||||
if (currentTime.IsNull()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return currentTime.Value().ToMilliseconds();
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::SetSource(Animation* aSource)
|
||||
{
|
||||
if (mSource) {
|
||||
mSource->SetParentTime(Nullable<TimeDuration>());
|
||||
}
|
||||
mSource = aSource;
|
||||
if (mSource) {
|
||||
mSource->SetParentTime(GetCurrentTimeDuration());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::Tick()
|
||||
{
|
||||
if (mSource) {
|
||||
mSource->SetParentTime(GetCurrentTimeDuration());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AnimationPlayer::IsRunning() const
|
||||
{
|
||||
if (IsPaused() || !GetSource() || GetSource()->IsFinishedTransition()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ComputedTiming computedTiming = GetSource()->GetComputedTiming();
|
||||
return computedTiming.mPhase == ComputedTiming::AnimationPhase_Active;
|
||||
}
|
||||
|
||||
bool
|
||||
AnimationPlayer::IsCurrent() const
|
||||
{
|
||||
return GetSource() && GetSource()->IsCurrent();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
98
dom/animation/AnimationPlayer.h
Normal file
98
dom/animation/AnimationPlayer.h
Normal file
@ -0,0 +1,98 @@
|
||||
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
||||
/* 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_dom_AnimationPlayer_h
|
||||
#define mozilla_dom_AnimationPlayer_h
|
||||
|
||||
#include <algorithm> // for std::max
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
|
||||
#include "mozilla/dom/Animation.h" // for Animation
|
||||
#include "mozilla/dom/AnimationTimeline.h" // for AnimationTimeline
|
||||
#include "nsCSSProperty.h" // for nsCSSProperty
|
||||
|
||||
// X11 has a #define for CurrentTime.
|
||||
#ifdef CurrentTime
|
||||
#undef CurrentTime
|
||||
#endif
|
||||
|
||||
struct JSContext;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AnimationPlayer MOZ_FINAL : public nsWrapperCache
|
||||
{
|
||||
protected:
|
||||
virtual ~AnimationPlayer() { }
|
||||
|
||||
public:
|
||||
explicit AnimationPlayer(AnimationTimeline* aTimeline)
|
||||
: mIsRunningOnCompositor(false)
|
||||
, mTimeline(aTimeline)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationPlayer)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationPlayer)
|
||||
|
||||
AnimationTimeline* GetParentObject() const { return mTimeline; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
// AnimationPlayer methods
|
||||
Animation* GetSource() const { return mSource; }
|
||||
AnimationTimeline* Timeline() const { return mTimeline; }
|
||||
double StartTime() const;
|
||||
double CurrentTime() const;
|
||||
|
||||
void SetSource(Animation* aSource);
|
||||
void Tick();
|
||||
|
||||
bool IsPaused() const {
|
||||
return mPlayState == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED;
|
||||
}
|
||||
|
||||
bool IsRunning() const;
|
||||
bool IsCurrent() const;
|
||||
|
||||
// Return the duration since the start time of the player, taking into
|
||||
// account the pause state. May be negative.
|
||||
// Returns a null value if the timeline associated with this object has a
|
||||
// current timestamp that is null or if the start time of this object is
|
||||
// null.
|
||||
Nullable<TimeDuration> GetCurrentTimeDuration() const {
|
||||
const TimeStamp& timelineTime = mTimeline->GetCurrentTimeStamp();
|
||||
// FIXME: In order to support arbitrary timelines we will need to fix
|
||||
// the pause logic to handle the timeline time going backwards.
|
||||
MOZ_ASSERT(timelineTime.IsNull() || !IsPaused() ||
|
||||
timelineTime >= mPauseStart,
|
||||
"if paused, any non-null value of aTime must be at least"
|
||||
" mPauseStart");
|
||||
|
||||
Nullable<TimeDuration> result; // Initializes to null
|
||||
if (!timelineTime.IsNull() && !mStartTime.IsNull()) {
|
||||
result.SetValue((IsPaused() ? mPauseStart : timelineTime) - mStartTime);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nsString mName;
|
||||
// The beginning of the delay period.
|
||||
TimeStamp mStartTime;
|
||||
TimeStamp mPauseStart;
|
||||
uint8_t mPlayState;
|
||||
bool mIsRunningOnCompositor;
|
||||
|
||||
nsRefPtr<AnimationTimeline> mTimeline;
|
||||
nsRefPtr<Animation> mSource;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_AnimationPlayer_h
|
@ -7,10 +7,14 @@
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Animation.h',
|
||||
'AnimationPlayer.h',
|
||||
'AnimationTimeline.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'Animation.cpp',
|
||||
'AnimationPlayer.cpp',
|
||||
'AnimationTimeline.cpp',
|
||||
]
|
||||
|
||||
|
@ -136,11 +136,6 @@ DOMInterfaces = {
|
||||
'resultNotAddRefed': [ 'inputBuffer', 'outputBuffer' ],
|
||||
},
|
||||
|
||||
'AnimationPlayer' : {
|
||||
'nativeType': 'mozilla::ElementAnimation',
|
||||
'headerFile': 'AnimationCommon.h',
|
||||
},
|
||||
|
||||
'BarProp': {
|
||||
'headerFile': 'mozilla/dom/BarProps.h',
|
||||
},
|
||||
|
@ -2445,6 +2445,37 @@ class CGNativeProperties(CGList):
|
||||
def define(self):
|
||||
return CGList.define(self)
|
||||
|
||||
class CGJsonifyAttributesMethod(CGAbstractMethod):
|
||||
"""
|
||||
Generate the JsonifyAttributes method for an interface descriptor
|
||||
"""
|
||||
def __init__(self, descriptor):
|
||||
args = [Argument('JSContext*', 'aCx'),
|
||||
Argument('JS::Handle<JSObject*>', 'obj'),
|
||||
Argument('%s*' % descriptor.nativeType, 'self'),
|
||||
Argument('JS::Rooted<JSObject*>&', 'aResult')]
|
||||
CGAbstractMethod.__init__(self, descriptor, 'JsonifyAttributes', 'bool', args)
|
||||
|
||||
def definition_body(self):
|
||||
ret = ''
|
||||
interface = self.descriptor.interface
|
||||
for m in interface.members:
|
||||
if m.isAttr() and not m.isStatic() and m.type.isSerializable():
|
||||
ret += fill(
|
||||
"""
|
||||
{ // scope for "temp"
|
||||
JS::Rooted<JS::Value> temp(aCx);
|
||||
if (!get_${name}(aCx, obj, self, JSJitGetterCallArgs(&temp))) {
|
||||
return false;
|
||||
}
|
||||
if (!JS_DefineProperty(aCx, aResult, "${name}", temp, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
""",
|
||||
name=m.identifier.name)
|
||||
ret += 'return true;\n'
|
||||
return ret
|
||||
|
||||
class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
||||
"""
|
||||
@ -7244,28 +7275,24 @@ class CGJsonifierMethod(CGSpecializedMethod):
|
||||
}
|
||||
""")
|
||||
|
||||
jsonInterfaces = []
|
||||
interface = self.descriptor.interface
|
||||
while interface:
|
||||
descriptor = self.descriptor.getDescriptor(interface.identifier.name)
|
||||
if descriptor.operations['Jsonifier']:
|
||||
for m in interface.members:
|
||||
if m.isAttr() and not m.isStatic() and m.type.isSerializable():
|
||||
ret += fill(
|
||||
"""
|
||||
{ // scope for "temp"
|
||||
JS::Rooted<JS::Value> temp(cx);
|
||||
if (!${parentclass}::get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) {
|
||||
return false;
|
||||
}
|
||||
if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
""",
|
||||
parentclass=toBindingNamespace(interface.identifier.name),
|
||||
name=m.identifier.name)
|
||||
jsonInterfaces.append(interface)
|
||||
interface = interface.parent
|
||||
|
||||
# Iterate the array in reverse: oldest ancestor first
|
||||
for interface in jsonInterfaces[::-1]:
|
||||
ret += fill(
|
||||
"""
|
||||
if (!${parentclass}::JsonifyAttributes(cx, obj, self, result)) {
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
parentclass=toBindingNamespace(interface.identifier.name)
|
||||
)
|
||||
ret += ('args.rval().setObject(*result);\n'
|
||||
'return true;\n')
|
||||
return ret
|
||||
@ -10604,6 +10631,7 @@ class CGDescriptor(CGThing):
|
||||
hasLenientSetter = hasLenientSetter or props.isLenientSetter
|
||||
|
||||
if jsonifierMethod:
|
||||
cgThings.append(CGJsonifyAttributesMethod(descriptor))
|
||||
cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod))
|
||||
cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod))
|
||||
if hasMethod:
|
||||
|
@ -96,6 +96,17 @@ enum TelemetryAlgorithm {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR; \
|
||||
}
|
||||
|
||||
// Safety check for algorithms that use keys, suitable for constructors
|
||||
#define CHECK_KEY_ALGORITHM(keyAlg, algName) \
|
||||
{ \
|
||||
nsString keyAlgName; \
|
||||
keyAlg->GetName(keyAlgName); \
|
||||
if (!keyAlgName.EqualsLiteral(algName)) { \
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; \
|
||||
return; \
|
||||
} \
|
||||
}
|
||||
|
||||
class ClearException
|
||||
{
|
||||
public:
|
||||
@ -447,6 +458,8 @@ public:
|
||||
// Cache parameters depending on the specific algorithm
|
||||
TelemetryAlgorithm telemetryAlg;
|
||||
if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CBC);
|
||||
|
||||
mMechanism = CKM_AES_CBC_PAD;
|
||||
telemetryAlg = TA_AES_CBC;
|
||||
AesCbcParams params;
|
||||
@ -462,6 +475,8 @@ public:
|
||||
return;
|
||||
}
|
||||
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CTR);
|
||||
|
||||
mMechanism = CKM_AES_CTR;
|
||||
telemetryAlg = TA_AES_CTR;
|
||||
AesCtrParams params;
|
||||
@ -480,6 +495,8 @@ public:
|
||||
|
||||
mCounterLength = params.mLength.Value();
|
||||
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM);
|
||||
|
||||
mMechanism = CKM_AES_GCM;
|
||||
telemetryAlg = TA_AES_GCM;
|
||||
AesGcmParams params;
|
||||
@ -627,6 +644,8 @@ public:
|
||||
void Init(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, bool aEncrypt)
|
||||
{
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_KW);
|
||||
|
||||
nsString algName;
|
||||
mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
@ -754,6 +773,8 @@ public:
|
||||
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSAES_PKCS1);
|
||||
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSAES_PKCS1);
|
||||
|
||||
if (mEncrypt) {
|
||||
if (!mPubKey) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
@ -849,6 +870,8 @@ public:
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_OAEP);
|
||||
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP);
|
||||
|
||||
if (mEncrypt) {
|
||||
if (!mPubKey) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
@ -863,15 +886,19 @@ public:
|
||||
mStrength = PK11_GetPrivateModulusLen(mPrivKey);
|
||||
}
|
||||
|
||||
RootedDictionary<RsaOaepParams> params(aCx);
|
||||
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
// The algorithm could just be given as a string
|
||||
// in which case there would be no label specified.
|
||||
if (!aAlgorithm.IsString()) {
|
||||
RootedDictionary<RsaOaepParams> params(aCx);
|
||||
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.mLabel.WasPassed() && !params.mLabel.Value().IsNull()) {
|
||||
ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value().Value());
|
||||
if (params.mLabel.WasPassed() && !params.mLabel.Value().IsNull()) {
|
||||
ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value().Value());
|
||||
}
|
||||
}
|
||||
// Otherwise mLabel remains the empty octet string, as intended
|
||||
|
||||
@ -973,6 +1000,8 @@ public:
|
||||
, mSymKey(aKey.GetSymKey())
|
||||
, mSign(aSign)
|
||||
{
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HMAC);
|
||||
|
||||
ATTEMPT_BUFFER_INIT(mData, aData);
|
||||
if (!aSign) {
|
||||
ATTEMPT_BUFFER_INIT(mSignature, aSignature);
|
||||
@ -1080,6 +1109,8 @@ public:
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
|
||||
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
|
||||
|
||||
ATTEMPT_BUFFER_INIT(mData, aData);
|
||||
if (!aSign) {
|
||||
ATTEMPT_BUFFER_INIT(mSignature, aSignature);
|
||||
@ -2285,6 +2316,8 @@ public:
|
||||
void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
|
||||
uint32_t aLength)
|
||||
{
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_PBKDF2);
|
||||
|
||||
// Check that we got a symmetric key
|
||||
if (mSymKey.Length() == 0) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
@ -2442,6 +2475,8 @@ public:
|
||||
|
||||
void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey)
|
||||
{
|
||||
CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDH);
|
||||
|
||||
// Check that we have a private key.
|
||||
if (!mPrivKey) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
@ -2472,14 +2507,7 @@ public:
|
||||
}
|
||||
|
||||
nsRefPtr<KeyAlgorithm> publicAlgorithm = publicKey->Algorithm();
|
||||
|
||||
// Given public key must be an ECDH key.
|
||||
nsString algName;
|
||||
publicAlgorithm->GetName(algName);
|
||||
if (!algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
return;
|
||||
}
|
||||
CHECK_KEY_ALGORITHM(publicAlgorithm, WEBCRYPTO_ALG_ECDH);
|
||||
|
||||
// Both keys must use the same named curve.
|
||||
nsString curve1, curve2;
|
||||
|
@ -5,7 +5,7 @@ support-files =
|
||||
test-array.js
|
||||
test-vectors.js
|
||||
test_WebCrypto.css
|
||||
tests.js
|
||||
util.js
|
||||
|
||||
[test_WebCrypto.html]
|
||||
[test_WebCrypto_ECDH.html]
|
||||
|
@ -180,3 +180,31 @@ if (MOCHITEST) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function error(test) {
|
||||
return function(x) {
|
||||
console.log("ERROR :: " + x);
|
||||
test.complete(false);
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
|
||||
function complete(test, valid) {
|
||||
return function(x) {
|
||||
console.log("COMPLETE")
|
||||
console.log(x);
|
||||
if (valid) {
|
||||
test.complete(valid(x));
|
||||
} else {
|
||||
test.complete(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function memcmp_complete(test, value) {
|
||||
return function(x) {
|
||||
console.log("COMPLETE")
|
||||
console.log(x);
|
||||
test.memcmp_complete(value, x);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
444
dom/crypto/test/test_WebCrypto_ECDH.html
Normal file
444
dom/crypto/test/test_WebCrypto_ECDH.html
Normal file
@ -0,0 +1,444 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>WebCrypto Test Suite</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
<link rel="stylesheet" href="./test_WebCrypto.css"/>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<!-- Utilities for manipulating ABVs -->
|
||||
<script src="util.js"></script>
|
||||
|
||||
<!-- A simple wrapper around IndexedDB -->
|
||||
<script src="simpledb.js"></script>
|
||||
|
||||
<!-- Test vectors drawn from the literature -->
|
||||
<script src="./test-vectors.js"></script>
|
||||
|
||||
<!-- General testing framework -->
|
||||
<script src="./test-array.js"></script>
|
||||
|
||||
<script>/*<![CDATA[*/
|
||||
"use strict";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Generate an ECDH key for named curve P-256",
|
||||
function() {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
crypto.subtle.generateKey(alg, false, ["deriveKey", "deriveBits"]).then(
|
||||
complete(that, function(x) {
|
||||
return exists(x.publicKey) &&
|
||||
(x.publicKey.algorithm.name == alg.name) &&
|
||||
(x.publicKey.algorithm.namedCurve == alg.namedCurve) &&
|
||||
(x.publicKey.type == "public") &&
|
||||
x.publicKey.extractable &&
|
||||
(x.publicKey.usages.length == 0) &&
|
||||
exists(x.privateKey) &&
|
||||
(x.privateKey.algorithm.name == alg.name) &&
|
||||
(x.privateKey.algorithm.namedCurve == alg.namedCurve) &&
|
||||
(x.privateKey.type == "private") &&
|
||||
!x.privateKey.extractable &&
|
||||
(x.privateKey.usages.length == 2) &&
|
||||
(x.privateKey.usages[0] == "deriveKey") &&
|
||||
(x.privateKey.usages[1] == "deriveBits");
|
||||
}),
|
||||
error(that)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Generate an ECDH key and derive some bits",
|
||||
function() {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
|
||||
var pair;
|
||||
function setKeyPair(x) { pair = x; }
|
||||
|
||||
function doDerive(n) {
|
||||
return function (x) {
|
||||
var alg = { name: "ECDH", public: pair.publicKey };
|
||||
return crypto.subtle.deriveBits(alg, pair.privateKey, n * 8);
|
||||
}
|
||||
}
|
||||
|
||||
crypto.subtle.generateKey(alg, false, ["deriveBits"])
|
||||
.then(setKeyPair, error(that))
|
||||
.then(doDerive(2), error(that))
|
||||
.then(function (x) {
|
||||
// Deriving less bytes works.
|
||||
if (x.byteLength != 2) {
|
||||
throw "should have derived two bytes";
|
||||
}
|
||||
})
|
||||
// Deriving more than the curve yields doesn't.
|
||||
.then(doDerive(33), error(that))
|
||||
.then(error(that), complete(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Test that ECDH deriveBits() fails when the public key is not an ECDH key",
|
||||
function() {
|
||||
var that = this;
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x.publicKey; }
|
||||
function setPriv(x) { privKey = x.privateKey; }
|
||||
|
||||
function doGenerateP256() {
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
|
||||
}
|
||||
|
||||
function doGenerateRSA() {
|
||||
var alg = {
|
||||
name: "RSA-OAEP",
|
||||
hash: "SHA-256",
|
||||
modulusLength: 2048,
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01])
|
||||
};
|
||||
return crypto.subtle.generateKey(alg, false, ["encrypt"])
|
||||
}
|
||||
|
||||
function doDerive() {
|
||||
var alg = { name: "ECDH", public: pubKey };
|
||||
return crypto.subtle.deriveBits(alg, privKey, 16);
|
||||
}
|
||||
|
||||
doGenerateP256()
|
||||
.then(setPriv, error(that))
|
||||
.then(doGenerateRSA, error(that))
|
||||
.then(setPub, error(that))
|
||||
.then(doDerive, error(that))
|
||||
.then(error(that), complete(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Test that ECDH deriveBits() fails when the given keys' curves don't match",
|
||||
function() {
|
||||
var that = this;
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x.publicKey; }
|
||||
function setPriv(x) { privKey = x.privateKey; }
|
||||
|
||||
function doGenerateP256() {
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
|
||||
}
|
||||
|
||||
function doGenerateP384() {
|
||||
var alg = { name: "ECDH", namedCurve: "P-384" };
|
||||
return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
|
||||
}
|
||||
|
||||
function doDerive() {
|
||||
var alg = { name: "ECDH", public: pubKey };
|
||||
return crypto.subtle.deriveBits(alg, privKey, 16);
|
||||
}
|
||||
|
||||
doGenerateP256()
|
||||
.then(setPriv, error(that))
|
||||
.then(doGenerateP384, error(that))
|
||||
.then(setPub, error(that))
|
||||
.then(doDerive, error(that))
|
||||
.then(error(that), complete(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"JWK import an ECDH public and private key and derive bits (P-256)",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x; }
|
||||
function setPriv(x) { privKey = x; }
|
||||
|
||||
function doDerive() {
|
||||
var alg = { name: "ECDH", public: pubKey };
|
||||
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p256.secret.byteLength * 8);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, false, ["deriveBits"])
|
||||
.then(setPriv, error(that)),
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_pub, alg, false, ["deriveBits"])
|
||||
.then(setPub, error(that))
|
||||
]).then(doDerive, error(that))
|
||||
.then(memcmp_complete(that, tv.ecdh_p256.secret), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"JWK import an ECDH public and private key and derive bits (P-384)",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x; }
|
||||
function setPriv(x) { privKey = x; }
|
||||
|
||||
function doDerive() {
|
||||
var alg = { name: "ECDH", public: pubKey };
|
||||
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p384.secret.byteLength * 8);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p384.jwk_priv, alg, false, ["deriveBits"])
|
||||
.then(setPriv, error(that)),
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p384.jwk_pub, alg, false, ["deriveBits"])
|
||||
.then(setPub, error(that))
|
||||
]).then(doDerive, error(that))
|
||||
.then(memcmp_complete(that, tv.ecdh_p384.secret), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"JWK import an ECDH public and private key and derive bits (P-521)",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x; }
|
||||
function setPriv(x) { privKey = x; }
|
||||
|
||||
function doDerive() {
|
||||
var alg = { name: "ECDH", public: pubKey };
|
||||
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p521.secret.byteLength * 8);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveBits"])
|
||||
.then(setPriv, error(that)),
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveBits"])
|
||||
.then(setPub, error(that))
|
||||
]).then(doDerive, error(that))
|
||||
.then(memcmp_complete(that, tv.ecdh_p521.secret), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"JWK import/export roundtrip with ECDH (P-256)",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x; }
|
||||
function setPriv(x) { privKey = x; }
|
||||
|
||||
function doExportPub() {
|
||||
return crypto.subtle.exportKey("jwk", pubKey);
|
||||
}
|
||||
function doExportPriv() {
|
||||
return crypto.subtle.exportKey("jwk", privKey);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, true, ["deriveBits"])
|
||||
.then(setPriv, error(that)),
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_pub, alg, true, ["deriveBits"])
|
||||
.then(setPub, error(that))
|
||||
]).then(doExportPub, error(that))
|
||||
.then(function (x) {
|
||||
var tp = tv.ecdh_p256.jwk_pub;
|
||||
if ((tp.kty != x.kty) &&
|
||||
(tp.crv != x.crv) &&
|
||||
(tp.x != x.x) &&
|
||||
(tp.y != x.y)) {
|
||||
throw "exported public key doesn't match";
|
||||
}
|
||||
}, error(that))
|
||||
.then(doExportPriv, error(that))
|
||||
.then(complete(that, function (x) {
|
||||
var tp = tv.ecdh_p256.jwk_priv;
|
||||
return (tp.kty == x.kty) &&
|
||||
(tp.crv == x.crv) &&
|
||||
(tp.d == x.d) &&
|
||||
(tp.x == x.x) &&
|
||||
(tp.y == x.y);
|
||||
}), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Test that importing bad JWKs fails",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
var tvs = tv.ecdh_p256_negative;
|
||||
|
||||
function doTryImport(jwk) {
|
||||
return function () {
|
||||
return crypto.subtle.importKey("jwk", jwk, alg, false, ["deriveBits"]);
|
||||
}
|
||||
}
|
||||
|
||||
doTryImport(tvs.jwk_bad_crv)()
|
||||
.then(error(that), doTryImport(tvs.jwk_missing_crv))
|
||||
.then(error(that), doTryImport(tvs.jwk_missing_x))
|
||||
.then(error(that), doTryImport(tvs.jwk_missing_y))
|
||||
.then(error(that), complete(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Derive an HMAC key from two ECDH keys and test sign/verify",
|
||||
function() {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
var algDerived = { name: "HMAC", hash: {name: "SHA-1"} };
|
||||
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x; }
|
||||
function setPriv(x) { privKey = x; }
|
||||
|
||||
function doDerive() {
|
||||
var alg = { name: "ECDH", public: pubKey };
|
||||
return crypto.subtle.deriveKey(alg, privKey, algDerived, false, ["sign", "verify"])
|
||||
.then(function (x) {
|
||||
if (!hasKeyFields(x)) {
|
||||
throw "Invalid key; missing field(s)";
|
||||
}
|
||||
|
||||
// 512 bit is the default for HMAC-SHA1.
|
||||
if (x.algorithm.length != 512) {
|
||||
throw "Invalid key; incorrect length";
|
||||
}
|
||||
|
||||
return x;
|
||||
});
|
||||
}
|
||||
|
||||
function doSignAndVerify(x) {
|
||||
var data = crypto.getRandomValues(new Uint8Array(1024));
|
||||
return crypto.subtle.sign("HMAC", x, data)
|
||||
.then(function (sig) {
|
||||
return crypto.subtle.verify("HMAC", x, sig, data);
|
||||
});
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveBits"])
|
||||
.then(setPriv, error(that)),
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveBits"])
|
||||
.then(setPub, error(that))
|
||||
]).then(doDerive, error(that))
|
||||
.then(doSignAndVerify, error(that))
|
||||
.then(complete(that), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"SPKI import/export of public ECDH keys (P-256)",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
var keys = ["spki", "spki_id_ecpk"];
|
||||
|
||||
function doImport(key) {
|
||||
return crypto.subtle.importKey("spki", tv.ecdh_p256[key], alg, true, ["deriveBits"]);
|
||||
}
|
||||
|
||||
function doExport(x) {
|
||||
return crypto.subtle.exportKey("spki", x);
|
||||
}
|
||||
|
||||
function nextKey() {
|
||||
var key = keys.shift();
|
||||
var imported = doImport(key);
|
||||
var derived = imported.then(doExport);
|
||||
|
||||
return derived.then(function (x) {
|
||||
if (!util.memcmp(x, tv.ecdh_p256.spki)) {
|
||||
throw "exported key is invalid";
|
||||
}
|
||||
|
||||
if (keys.length) {
|
||||
return nextKey();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
nextKey().then(complete(that), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"SPKI/JWK import ECDH keys (P-256) and derive a known secret",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x; }
|
||||
function setPriv(x) { privKey = x; }
|
||||
|
||||
function doDerive() {
|
||||
var alg = { name: "ECDH", public: pubKey };
|
||||
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p256.secret.byteLength * 8);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
crypto.subtle.importKey("spki", tv.ecdh_p256.spki, alg, false, ["deriveBits"])
|
||||
.then(setPub),
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, false, ["deriveBits"])
|
||||
.then(setPriv)
|
||||
]).then(doDerive)
|
||||
.then(memcmp_complete(that, tv.ecdh_p256.secret), error(that));
|
||||
}
|
||||
);
|
||||
/*]]>*/</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<div id="head">
|
||||
<b>Web</b>Crypto<br>
|
||||
</div>
|
||||
|
||||
<div id="start" onclick="start();">RUN ALL</div>
|
||||
|
||||
<div id="resultDiv" class="content">
|
||||
Summary:
|
||||
<span class="pass"><span id="passN">0</span> passed, </span>
|
||||
<span class="fail"><span id="failN">0</span> failed, </span>
|
||||
<span class="pending"><span id="pendingN">0</span> pending.</span>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<table id="results">
|
||||
<tr>
|
||||
<th>Test</th>
|
||||
<th>Result</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="foot"></div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
@ -43,3 +43,35 @@ var util = {
|
||||
return abv;
|
||||
},
|
||||
};
|
||||
|
||||
function exists(x) {
|
||||
return (x !== undefined);
|
||||
}
|
||||
|
||||
function hasFields(object, fields) {
|
||||
return fields
|
||||
.map(x => exists(object[x]))
|
||||
.reduce((x,y) => (x && y));
|
||||
}
|
||||
|
||||
function hasKeyFields(x) {
|
||||
return hasFields(x, ["algorithm", "extractable", "type", "usages"]);
|
||||
}
|
||||
|
||||
function hasBaseJwkFields(x) {
|
||||
return hasFields(x, ["kty", "alg", "ext", "key_ops"]);
|
||||
}
|
||||
|
||||
function shallowArrayEquals(x, y) {
|
||||
if (x.length != y.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i in x) {
|
||||
if (x[i] != y[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -118,6 +118,8 @@ var interfaceNamesInGlobalScope =
|
||||
"AnalyserNode",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"AnimationEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "Animation", pref: "dom.animations-api.core.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "AnimationPlayer", pref: "dom.animations-api.core.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
15
dom/webidl/Animation.webidl
Normal file
15
dom/webidl/Animation.webidl
Normal file
@ -0,0 +1,15 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* http://dev.w3.org/fxtf/web-animations/#the-animation-interface
|
||||
*
|
||||
* Copyright © 2014 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.animations-api.core.enabled"]
|
||||
interface Animation {
|
||||
};
|
@ -12,6 +12,8 @@
|
||||
|
||||
[Pref="dom.animations-api.core.enabled"]
|
||||
interface AnimationPlayer {
|
||||
// Bug 1049975: Per spec, this should be a writeable AnimationNode? member
|
||||
[Pure] readonly attribute Animation? source;
|
||||
readonly attribute AnimationTimeline timeline;
|
||||
[Pure] readonly attribute double startTime;
|
||||
readonly attribute double currentTime;
|
||||
|
@ -21,6 +21,7 @@ WEBIDL_FILES = [
|
||||
'AlarmsManager.webidl',
|
||||
'AnalyserNode.webidl',
|
||||
'Animatable.webidl',
|
||||
'Animation.webidl',
|
||||
'AnimationEvent.webidl',
|
||||
'AnimationPlayer.webidl',
|
||||
'AnimationTimeline.webidl',
|
||||
|
@ -426,10 +426,6 @@ private:
|
||||
return aStatus;
|
||||
}
|
||||
|
||||
if (!aStringLen) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(aString, "This should never be null!");
|
||||
|
||||
// Make sure we're not seeing the result of a 404 or something by checking
|
||||
|
10
dom/workers/test/404_server.sjs
Normal file
10
dom/workers/test/404_server.sjs
Normal file
@ -0,0 +1,10 @@
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
response.setStatusLine(request.httpVersion, 404, "Not found");
|
||||
|
||||
// Any valid JS.
|
||||
if (request.queryString == 'js') {
|
||||
response.setHeader("Content-Type", "text/javascript", false);
|
||||
response.write('4 + 4');
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ support-files =
|
||||
console_worker.js
|
||||
consoleReplaceable_worker.js
|
||||
csp_worker.js
|
||||
404_server.sjs
|
||||
errorPropagation_iframe.html
|
||||
errorPropagation_worker.js
|
||||
errorwarning_worker.js
|
||||
@ -90,6 +91,7 @@ support-files =
|
||||
[test_bug1010784.html]
|
||||
[test_bug1014466.html]
|
||||
[test_bug1020226.html]
|
||||
[test_bug1036484.html]
|
||||
[test_bug998474.html]
|
||||
[test_chromeWorker.html]
|
||||
[test_clearTimeouts.html]
|
||||
|
54
dom/workers/test/test_bug1036484.html
Normal file
54
dom/workers/test/test_bug1036484.html
Normal file
@ -0,0 +1,54 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Tests of DOM Worker Threads: bug 1036484
|
||||
-->
|
||||
<head>
|
||||
<title>Test for DOM Worker Threads: bug 1036484</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function test(script) {
|
||||
var worker = new Worker(script);
|
||||
|
||||
worker.onmessage = function(event) {
|
||||
ok(false, "Shouldn't ever get a message!");
|
||||
}
|
||||
|
||||
worker.onerror = function(event) {
|
||||
is(event.target, worker);
|
||||
is(event.message, "Error: Script file not found: " + script);
|
||||
event.preventDefault();
|
||||
runTests();
|
||||
};
|
||||
|
||||
worker.postMessage("dummy");
|
||||
}
|
||||
|
||||
var tests = [ '404_server.sjs', '404_server.sjs?js' ];
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var script = tests.shift();
|
||||
test(script);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTests();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "Layers.h"
|
||||
#include <algorithm> // for max, min
|
||||
#include "AnimationCommon.h" // for ComputedTimingFunction
|
||||
#include "CompositableHost.h" // for CompositableHost
|
||||
#include "ImageContainer.h" // for ImageContainer, etc
|
||||
#include "ImageLayers.h" // for ImageLayer
|
||||
@ -20,6 +19,7 @@
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/DebugOnly.h" // for DebugOnly
|
||||
#include "mozilla/Telemetry.h" // for Accumulate
|
||||
#include "mozilla/dom/AnimationPlayer.h" // for ComputedTimingFunction
|
||||
#include "mozilla/gfx/2D.h" // for DrawTarget
|
||||
#include "mozilla/gfx/BaseSize.h" // for BaseSize
|
||||
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
|
||||
@ -394,12 +394,13 @@ Layer::SetAnimations(const AnimationArray& aAnimations)
|
||||
mAnimationData.Clear();
|
||||
for (uint32_t i = 0; i < mAnimations.Length(); i++) {
|
||||
AnimData* data = mAnimationData.AppendElement();
|
||||
InfallibleTArray<nsAutoPtr<css::ComputedTimingFunction> >& functions = data->mFunctions;
|
||||
InfallibleTArray<nsAutoPtr<ComputedTimingFunction> >& functions =
|
||||
data->mFunctions;
|
||||
const InfallibleTArray<AnimationSegment>& segments =
|
||||
mAnimations.ElementAt(i).segments();
|
||||
for (uint32_t j = 0; j < segments.Length(); j++) {
|
||||
TimingFunction tf = segments.ElementAt(j).sampleFn();
|
||||
css::ComputedTimingFunction* ctf = new css::ComputedTimingFunction();
|
||||
ComputedTimingFunction* ctf = new ComputedTimingFunction();
|
||||
switch (tf.type()) {
|
||||
case TimingFunction::TCubicBezierFunction: {
|
||||
CubicBezierFunction cbf = tf.get_CubicBezierFunction();
|
||||
|
@ -52,6 +52,7 @@ extern uint8_t gLayerManagerLayerBuilder;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ComputedTimingFunction;
|
||||
class FrameLayerBuilder;
|
||||
class StyleAnimationValue;
|
||||
class WebGLContext;
|
||||
@ -65,10 +66,6 @@ namespace gfx {
|
||||
class DrawTarget;
|
||||
}
|
||||
|
||||
namespace css {
|
||||
class ComputedTimingFunction;
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
class OverfillCallback;
|
||||
}
|
||||
@ -707,7 +704,7 @@ typedef InfallibleTArray<Animation> AnimationArray;
|
||||
struct AnimData {
|
||||
InfallibleTArray<mozilla::StyleAnimationValue> mStartValues;
|
||||
InfallibleTArray<mozilla::StyleAnimationValue> mEndValues;
|
||||
InfallibleTArray<nsAutoPtr<mozilla::css::ComputedTimingFunction> > mFunctions;
|
||||
InfallibleTArray<nsAutoPtr<mozilla::ComputedTimingFunction> > mFunctions;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <stdint.h> // for uint32_t, uint64_t
|
||||
#include <sys/types.h> // for int32_t
|
||||
#include <algorithm> // for max, min
|
||||
#include "AnimationCommon.h" // for ComputedTimingFunction
|
||||
#include "AsyncPanZoomController.h" // for AsyncPanZoomController, etc
|
||||
#include "Compositor.h" // for Compositor
|
||||
#include "CompositorParent.h" // for CompositorParent
|
||||
@ -32,6 +31,7 @@
|
||||
#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
|
||||
#include "mozilla/StaticPtr.h" // for StaticAutoPtr
|
||||
#include "mozilla/TimeStamp.h" // for TimeDuration, TimeStamp
|
||||
#include "mozilla/dom/AnimationPlayer.h" // for ComputedTimingFunction
|
||||
#include "mozilla/dom/Touch.h" // for Touch
|
||||
#include "mozilla/gfx/BasePoint.h" // for BasePoint
|
||||
#include "mozilla/gfx/BaseRect.h" // for BaseRect
|
||||
@ -347,7 +347,7 @@ static const double ALLOWED_DIRECT_PAN_ANGLE = M_PI / 3.0; // 60 degrees
|
||||
/**
|
||||
* Computed time function used for sampling frames of a zoom to animation.
|
||||
*/
|
||||
StaticAutoPtr<css::ComputedTimingFunction> gComputedTimingFunction;
|
||||
StaticAutoPtr<ComputedTimingFunction> gComputedTimingFunction;
|
||||
|
||||
/**
|
||||
* Maximum zoom amount, always used, even if a page asks for higher.
|
||||
@ -700,7 +700,7 @@ AsyncPanZoomController::InitializeGlobalState()
|
||||
return;
|
||||
sInitialized = true;
|
||||
|
||||
gComputedTimingFunction = new css::ComputedTimingFunction();
|
||||
gComputedTimingFunction = new ComputedTimingFunction();
|
||||
gComputedTimingFunction->Init(
|
||||
nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
|
||||
ClearOnShutdown(&gComputedTimingFunction);
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "mozilla/layers/AsyncCompositionManager.h"
|
||||
#include <stdint.h> // for uint32_t
|
||||
#include "AnimationCommon.h" // for ComputedTimingFunction
|
||||
#include "CompositorParent.h" // for CompositorParent, etc
|
||||
#include "FrameMetrics.h" // for FrameMetrics
|
||||
#include "LayerManagerComposite.h" // for LayerManagerComposite, etc
|
||||
@ -15,13 +14,13 @@
|
||||
#include "gfxPoint3D.h" // for gfxPoint3D
|
||||
#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
|
||||
#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
|
||||
#include "mozilla/dom/AnimationPlayer.h" // for AnimationPlayer
|
||||
#include "mozilla/gfx/BaseRect.h" // for BaseRect
|
||||
#include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped
|
||||
#include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped
|
||||
#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
|
||||
#include "mozilla/layers/AsyncPanZoomController.h"
|
||||
#include "mozilla/layers/Compositor.h" // for Compositor
|
||||
#include "nsAnimationManager.h" // for ElementAnimations
|
||||
#include "nsCSSPropList.h"
|
||||
#include "nsCoord.h" // for NSAppUnitsToFloatPixels, etc
|
||||
#include "nsDebug.h" // for NS_ASSERTION, etc
|
||||
@ -451,7 +450,7 @@ SampleAnimations(Layer* aLayer, TimeStamp aPoint)
|
||||
timing.mFillMode = NS_STYLE_ANIMATION_FILL_MODE_BOTH;
|
||||
|
||||
ComputedTiming computedTiming =
|
||||
ElementAnimation::GetComputedTimingAt(
|
||||
dom::Animation::GetComputedTimingAt(
|
||||
Nullable<TimeDuration>(elapsedDuration), timing);
|
||||
|
||||
NS_ABORT_IF_FALSE(0.0 <= computedTiming.mTimeFraction &&
|
||||
|
@ -320,6 +320,7 @@ selfhosting_srcs := \
|
||||
$(srcdir)/builtin/Iterator.js \
|
||||
$(srcdir)/builtin/Map.js \
|
||||
$(srcdir)/builtin/Number.js \
|
||||
$(srcdir)/builtin/Object.js \
|
||||
$(srcdir)/builtin/String.js \
|
||||
$(srcdir)/builtin/Set.js \
|
||||
$(srcdir)/builtin/TypedObject.js \
|
||||
|
@ -158,7 +158,8 @@ namespace JSC {
|
||||
* The user must check for a NULL return value, which means
|
||||
* no code was generated, or there was an OOM.
|
||||
*/
|
||||
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind)
|
||||
void* executableAllocAndCopy(js::jit::ExecutableAllocator* allocator,
|
||||
js::jit::ExecutablePool** poolp, js::jit::CodeKind kind)
|
||||
{
|
||||
if (m_oom || m_size == 0) {
|
||||
*poolp = NULL;
|
||||
@ -174,7 +175,7 @@ namespace JSC {
|
||||
}
|
||||
JS_ASSERT(*poolp);
|
||||
|
||||
ExecutableAllocator::makeWritable(result, m_size);
|
||||
js::jit::ExecutableAllocator::makeWritable(result, m_size);
|
||||
|
||||
return memcpy(result, m_buffer, m_size);
|
||||
}
|
||||
|
@ -3494,7 +3494,8 @@ public:
|
||||
return dst.m_offset - src.m_offset;
|
||||
}
|
||||
|
||||
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp, CodeKind kind)
|
||||
void* executableAllocAndCopy(js::jit::ExecutableAllocator* allocator,
|
||||
js::jit::ExecutablePool **poolp, js::jit::CodeKind kind)
|
||||
{
|
||||
return m_formatter.executableAllocAndCopy(allocator, poolp, kind);
|
||||
}
|
||||
@ -4014,7 +4015,8 @@ private:
|
||||
bool oom() const { return m_buffer.oom(); }
|
||||
bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); }
|
||||
void* data() const { return m_buffer.data(); }
|
||||
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind) {
|
||||
void* executableAllocAndCopy(js::jit::ExecutableAllocator* allocator,
|
||||
js::jit::ExecutablePool** poolp, js::jit::CodeKind kind) {
|
||||
return m_buffer.executableAllocAndCopy(allocator, poolp, kind);
|
||||
}
|
||||
|
||||
|
@ -1233,6 +1233,7 @@ const JSFunctionSpec js::object_static_methods[] = {
|
||||
* in a different array.
|
||||
*/
|
||||
const JSFunctionSpec js::object_static_selfhosted_methods[] = {
|
||||
JS_SELF_HOSTED_FN("assign", "ObjectStaticAssign", 2,0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
67
js/src/builtin/Object.js
Normal file
67
js/src/builtin/Object.js
Normal file
@ -0,0 +1,67 @@
|
||||
/* 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/. */
|
||||
|
||||
/* ES6 draft 2014-07-18 19.1.2.1. */
|
||||
function ObjectStaticAssign(target, firstSource) {
|
||||
// Steps 1-2.
|
||||
var to = ToObject(target);
|
||||
|
||||
// Step 3.
|
||||
if (arguments.length < 2)
|
||||
return to;
|
||||
|
||||
// Step 4.
|
||||
var i = 1;
|
||||
do {
|
||||
// Step 5.a-b.
|
||||
var nextSource = arguments[i];
|
||||
var from = ToObject(nextSource);
|
||||
|
||||
// Step 5.c-d.
|
||||
var keysArray = std_Object_getOwnPropertyNames(from);
|
||||
|
||||
// Steps 5.e-f.
|
||||
var len = keysArray.length;
|
||||
|
||||
// Step 5.h.
|
||||
var nextIndex = 0;
|
||||
|
||||
// Step 5.i (Modified a bit because we can't catch and store the
|
||||
// actual Completion Record). Instead we have a marker object.
|
||||
const MISSING = {};
|
||||
var pendingException = MISSING;
|
||||
|
||||
// Step 5.j.
|
||||
while (nextIndex < len) {
|
||||
// Step 5.j.i-ii.
|
||||
var nextKey = keysArray[nextIndex];
|
||||
|
||||
// Step 5.j.iii-v.
|
||||
try {
|
||||
// We'd like to use Object.propertyIsEnumerable here, but we
|
||||
// can't, because if a property doesn't exist, it won't properly
|
||||
// call getOwnPropertyDescriptor (important if |from| is a
|
||||
// proxy).
|
||||
var desc = std_Object_getOwnPropertyDescriptor(from, nextKey);
|
||||
if (desc !== undefined && desc.enumerable)
|
||||
to[nextKey] = from[nextKey];
|
||||
} catch (e) {
|
||||
if (pendingException === MISSING)
|
||||
pendingException = e;
|
||||
}
|
||||
|
||||
// Step 5.j.vi.
|
||||
nextIndex++;
|
||||
}
|
||||
|
||||
// Step 5.k.
|
||||
if (pendingException !== MISSING)
|
||||
throw pendingException;
|
||||
i++;
|
||||
} while (i < arguments.length);
|
||||
|
||||
// Step 6.
|
||||
return to;
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ var std_Object_create = Object.create;
|
||||
var std_Object_getOwnPropertyNames = Object.getOwnPropertyNames;
|
||||
var std_Object_hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
var std_Object_getPrototypeOf = Object.getPrototypeOf;
|
||||
var std_Object_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
||||
var std_RegExp_test = RegExp.prototype.test;
|
||||
var std_String_fromCharCode = String.fromCharCode;
|
||||
var std_String_charCodeAt = String.prototype.charCodeAt;
|
||||
|
@ -1360,16 +1360,20 @@ TokenStream::getTokenInternal(Modifier modifier)
|
||||
} else if (JS7_ISDEC(c)) {
|
||||
radix = 8;
|
||||
numStart = userbuf.addressOfNextRawChar() - 1; // one past the '0'
|
||||
|
||||
// Octal integer literals are not permitted in strict mode code.
|
||||
if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
|
||||
goto error;
|
||||
|
||||
while (JS7_ISDEC(c)) {
|
||||
// Even in sloppy mode, 08 or 09 is a syntax error.
|
||||
if (c >= '8') {
|
||||
reportError(JSMSG_BAD_OCTAL, c == '8' ? "8" : "9");
|
||||
// Octal integer literals are not permitted in strict mode code.
|
||||
if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
|
||||
goto error;
|
||||
|
||||
// Outside strict mode, we permit 08 and 09 as decimal numbers,
|
||||
// which makes our behaviour a superset of the ECMA numeric
|
||||
// grammar. We might not always be so permissive, so we warn
|
||||
// about it.
|
||||
if (c >= '8') {
|
||||
if (!reportWarning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) {
|
||||
goto error;
|
||||
}
|
||||
goto decimal; // use the decimal scanner for the rest of the number
|
||||
}
|
||||
c = getCharIgnoreEOL();
|
||||
}
|
||||
|
@ -442,7 +442,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("RegExp");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::REGEXP_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, REGEXP_CODE);
|
||||
if (!code)
|
||||
return RegExpCode();
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
for (var code of ["08", "09", "01238", "01239", "08e+1"]) {
|
||||
assertThrowsInstanceOf(() => Function(code), SyntaxError);
|
||||
assertThrowsInstanceOf(() => eval(code), SyntaxError);
|
||||
}
|
||||
|
||||
var ok = [0.8, 0.08, 0e8, 1e08, 1e+08];
|
@ -108,7 +108,7 @@ BaselineCompiler::compile()
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("Baseline");
|
||||
JitCode *code = linker.newCode<CanGC>(cx, JSC::BASELINE_CODE);
|
||||
JitCode *code = linker.newCode<CanGC>(cx, BASELINE_CODE);
|
||||
if (!code)
|
||||
return Method_Error;
|
||||
|
||||
|
@ -868,7 +868,7 @@ JitRuntime::generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrame
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BaselineDebugModeOSRHandler");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
if (!code)
|
||||
return nullptr;
|
||||
|
||||
|
@ -612,7 +612,7 @@ ICStubCompiler::getStubCode()
|
||||
return nullptr;
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("getStubCode");
|
||||
Rooted<JitCode *> newStubCode(cx, linker.newCode<CanGC>(cx, JSC::BASELINE_CODE));
|
||||
Rooted<JitCode *> newStubCode(cx, linker.newCode<CanGC>(cx, BASELINE_CODE));
|
||||
if (!newStubCode)
|
||||
return nullptr;
|
||||
|
||||
|
@ -5330,7 +5330,7 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("StringConcatStub");
|
||||
JitCode *code = linker.newCode<CanGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<CanGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "StringConcatStub");
|
||||
@ -5368,7 +5368,7 @@ JitRuntime::generateMallocStub(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("MallocStub");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "MallocStub");
|
||||
@ -5402,7 +5402,7 @@ JitRuntime::generateFreeStub(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("FreeStub");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "FreeStub");
|
||||
@ -6713,7 +6713,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
|
||||
AutoFlushICache afc("IonLink");
|
||||
JitCode *code = (executionMode == SequentialExecution)
|
||||
? linker.newCodeForIonScript(cx)
|
||||
: linker.newCode<CanGC>(cx, JSC::ION_CODE);
|
||||
: linker.newCode<CanGC>(cx, ION_CODE);
|
||||
if (!code) {
|
||||
// Use js_free instead of IonScript::Destroy: the cache list and
|
||||
// backedge list are still uninitialized.
|
||||
|
@ -29,9 +29,7 @@
|
||||
|
||||
#include "js/MemoryMetrics.h"
|
||||
|
||||
#if ENABLE_ASSEMBLER
|
||||
|
||||
namespace JSC {
|
||||
using namespace js::jit;
|
||||
|
||||
size_t ExecutableAllocator::pageSize = 0;
|
||||
size_t ExecutableAllocator::largeAllocSize = 0;
|
||||
@ -90,7 +88,3 @@ ExecutableAllocator::codeContains(char* address)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE(ASSEMBLER)
|
||||
|
@ -23,8 +23,8 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef assembler_jit_ExecutableAllocator_h
|
||||
#define assembler_jit_ExecutableAllocator_h
|
||||
#ifndef jit_ExecutableAllocator_h
|
||||
#define jit_ExecutableAllocator_h
|
||||
|
||||
#include <limits>
|
||||
#include <stddef.h> // for ptrdiff_t
|
||||
@ -37,8 +37,8 @@
|
||||
#include "js/HashTable.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
#if WTF_CPU_SPARC
|
||||
#ifdef linux // bugzilla 502369
|
||||
#ifdef JS_CPU_SPARC
|
||||
#ifdef __linux__ // bugzilla 502369
|
||||
static void sync_instruction_memory(caddr_t v, u_int len)
|
||||
{
|
||||
caddr_t end = v + len;
|
||||
@ -53,16 +53,7 @@ extern "C" void sync_instruction_memory(caddr_t v, u_int len);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if WTF_OS_IOS
|
||||
#include <libkern/OSCacheControl.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if WTF_OS_SYMBIAN
|
||||
#include <e32std.h>
|
||||
#endif
|
||||
|
||||
#if WTF_CPU_MIPS && WTF_OS_LINUX && !JS_MIPS_SIMULATOR
|
||||
#if defined(JS_CODEGEN_MIPS) && defined(__linux__) && !defined(JS_MIPS_SIMULATOR)
|
||||
#include <sys/cachectl.h>
|
||||
#endif
|
||||
|
||||
@ -74,19 +65,13 @@ extern "C" void sync_instruction_memory(caddr_t v, u_int len);
|
||||
#define INITIAL_PROTECTION_FLAGS (PROT_READ | PROT_WRITE | PROT_EXEC)
|
||||
#endif
|
||||
|
||||
namespace JSC {
|
||||
enum CodeKind { ION_CODE = 0, BASELINE_CODE, REGEXP_CODE, OTHER_CODE };
|
||||
}
|
||||
|
||||
#if ENABLE_ASSEMBLER
|
||||
|
||||
//#define DEBUG_STRESS_JSC_ALLOCATOR
|
||||
|
||||
namespace JS {
|
||||
struct CodeSizes;
|
||||
}
|
||||
|
||||
namespace JSC {
|
||||
namespace js {
|
||||
namespace jit {
|
||||
enum CodeKind { ION_CODE = 0, BASELINE_CODE, REGEXP_CODE, OTHER_CODE };
|
||||
|
||||
class ExecutableAllocator;
|
||||
|
||||
@ -98,9 +83,6 @@ private:
|
||||
struct Allocation {
|
||||
char* pages;
|
||||
size_t size;
|
||||
#if WTF_OS_SYMBIAN
|
||||
RChunk* chunk;
|
||||
#endif
|
||||
};
|
||||
|
||||
ExecutableAllocator* m_allocator;
|
||||
@ -284,7 +266,7 @@ public:
|
||||
private:
|
||||
static size_t pageSize;
|
||||
static size_t largeAllocSize;
|
||||
#if WTF_OS_WINDOWS
|
||||
#ifdef XP_WIN
|
||||
static uint64_t rngSeed;
|
||||
#endif
|
||||
|
||||
@ -322,11 +304,7 @@ private:
|
||||
if (!m_pools.initialized() && !m_pools.init())
|
||||
return NULL;
|
||||
|
||||
#ifdef DEBUG_STRESS_JSC_ALLOCATOR
|
||||
ExecutablePool::Allocation a = systemAlloc(size_t(4294967291));
|
||||
#else
|
||||
ExecutablePool::Allocation a = systemAlloc(allocSize);
|
||||
#endif
|
||||
if (!a.pages)
|
||||
return NULL;
|
||||
|
||||
@ -342,7 +320,6 @@ private:
|
||||
public:
|
||||
ExecutablePool* poolForSize(size_t n)
|
||||
{
|
||||
#ifndef DEBUG_STRESS_JSC_ALLOCATOR
|
||||
// Try to fit in an existing small allocator. Use the pool with the
|
||||
// least available space that is big enough (best-fit). This is the
|
||||
// best strategy because (a) it maximizes the chance of the next
|
||||
@ -358,7 +335,6 @@ public:
|
||||
minPool->addRef();
|
||||
return minPool;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the request is large, we just provide a unshared allocator
|
||||
if (n > largeAllocSize)
|
||||
@ -414,7 +390,7 @@ public:
|
||||
#endif
|
||||
|
||||
|
||||
#if WTF_CPU_X86 || WTF_CPU_X86_64
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
static void cacheFlush(void*, size_t)
|
||||
{
|
||||
}
|
||||
@ -423,7 +399,7 @@ public:
|
||||
{
|
||||
js::jit::Simulator::FlushICache(code, size);
|
||||
}
|
||||
#elif WTF_CPU_MIPS
|
||||
#elif defined(JS_CODEGEN_MIPS)
|
||||
static void cacheFlush(void* code, size_t size)
|
||||
{
|
||||
#if WTF_COMPILER_GCC && (GCC_VERSION >= 40300)
|
||||
@ -449,33 +425,6 @@ public:
|
||||
_flush_cache(reinterpret_cast<char*>(code), size, BCACHE);
|
||||
#endif
|
||||
}
|
||||
#elif WTF_CPU_ARM && WTF_OS_IOS
|
||||
static void cacheFlush(void* code, size_t size)
|
||||
{
|
||||
sys_dcache_flush(code, size);
|
||||
sys_icache_invalidate(code, size);
|
||||
}
|
||||
#elif WTF_CPU_ARM_THUMB2 && WTF_IOS
|
||||
static void cacheFlush(void* code, size_t size)
|
||||
{
|
||||
asm volatile (
|
||||
"push {r7}\n"
|
||||
"mov r0, %0\n"
|
||||
"mov r1, %1\n"
|
||||
"movw r7, #0x2\n"
|
||||
"movt r7, #0xf\n"
|
||||
"movs r2, #0x0\n"
|
||||
"svc 0x0\n"
|
||||
"pop {r7}\n"
|
||||
:
|
||||
: "r" (code), "r" (reinterpret_cast<char*>(code) + size)
|
||||
: "r0", "r1", "r2");
|
||||
}
|
||||
#elif WTF_OS_SYMBIAN
|
||||
static void cacheFlush(void* code, size_t size)
|
||||
{
|
||||
User::IMB_Range(code, static_cast<char*>(code) + size);
|
||||
}
|
||||
#elif WTF_CPU_ARM_TRADITIONAL && WTF_OS_LINUX && WTF_COMPILER_RVCT
|
||||
static __asm void cacheFlush(void* code, size_t size);
|
||||
#elif WTF_CPU_ARM_TRADITIONAL && (WTF_OS_LINUX || WTF_OS_ANDROID) && WTF_COMPILER_GCC
|
||||
@ -494,7 +443,7 @@ public:
|
||||
: "r" (code), "r" (reinterpret_cast<char*>(code) + size)
|
||||
: "r0", "r1", "r2");
|
||||
}
|
||||
#elif WTF_CPU_SPARC
|
||||
#elif JS_CPU_SPARC
|
||||
static void cacheFlush(void* code, size_t size)
|
||||
{
|
||||
sync_instruction_memory((caddr_t)code, size);
|
||||
@ -522,8 +471,7 @@ private:
|
||||
static size_t determinePageSize();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif // ENABLE(ASSEMBLER)
|
||||
|
||||
#endif /* assembler_jit_ExecutableAllocator_h */
|
||||
#endif /* jit_ExecutableAllocator_h */
|
||||
|
@ -23,19 +23,16 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "jit/ExecutableAllocator.h"
|
||||
|
||||
#if ENABLE_ASSEMBLER && WTF_OS_UNIX && !WTF_OS_SYMBIAN
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/TaggedAnonymousMemory.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "jit/ExecutableAllocator.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace JSC {
|
||||
using namespace js::jit;
|
||||
|
||||
size_t ExecutableAllocator::determinePageSize()
|
||||
{
|
||||
@ -108,7 +105,3 @@ ExecutablePool::toggleAllCodeAsAccessible(bool accessible)
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE(ASSEMBLER)
|
||||
|
@ -23,16 +23,15 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
||||
#include "jit/ExecutableAllocator.h"
|
||||
|
||||
#if ENABLE_ASSEMBLER && WTF_OS_WINDOWS
|
||||
|
||||
#include "jswin.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
||||
extern uint64_t random_next(uint64_t *, int);
|
||||
|
||||
namespace JSC {
|
||||
using namespace js::jit;
|
||||
|
||||
uint64_t ExecutableAllocator::rngSeed;
|
||||
|
||||
@ -57,10 +56,10 @@ void *ExecutableAllocator::computeRandomAllocationAddress()
|
||||
* x64: [2GiB, 4TiB), with 25 bits of randomness.
|
||||
*/
|
||||
static const unsigned chunkBits = 16;
|
||||
#if WTF_CPU_X86_64
|
||||
#ifdef JS_CPU_X64
|
||||
static const uintptr_t base = 0x0000000080000000;
|
||||
static const uintptr_t mask = 0x000003ffffff0000;
|
||||
#elif WTF_CPU_X86
|
||||
#elif defined(JS_CPU_X86)
|
||||
static const uintptr_t base = 0x04000000;
|
||||
static const uintptr_t mask = 0x3fff0000;
|
||||
#else
|
||||
@ -128,7 +127,3 @@ ExecutablePool::toggleAllCodeAsAccessible(bool accessible)
|
||||
#if ENABLE_ASSEMBLER_WX_EXCLUSIVE
|
||||
#error "ASSEMBLER_WX_EXCLUSIVE not yet suported on this platform."
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE(ASSEMBLER)
|
||||
|
@ -23,6 +23,8 @@ class InlineForwardListNode
|
||||
explicit InlineForwardListNode(InlineForwardListNode<T> *n) : next(n)
|
||||
{ }
|
||||
|
||||
InlineForwardListNode(const InlineForwardListNode<T> &) MOZ_DELETE;
|
||||
|
||||
protected:
|
||||
friend class InlineForwardList<T>;
|
||||
friend class InlineForwardListIterator<T>;
|
||||
@ -218,6 +220,8 @@ class InlineListNode : public InlineForwardListNode<T>
|
||||
prev(p)
|
||||
{ }
|
||||
|
||||
InlineListNode(const InlineListNode<T> &) MOZ_DELETE;
|
||||
|
||||
protected:
|
||||
friend class InlineList<T>;
|
||||
friend class InlineListIterator<T>;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "jit/LICM.h"
|
||||
#include "jit/LinearScan.h"
|
||||
#include "jit/LIR.h"
|
||||
#include "jit/LoopUnroller.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/ParallelSafetyAnalysis.h"
|
||||
#include "jit/PerfSpewer.h"
|
||||
@ -330,12 +331,12 @@ JitRuntime::freeOsrTempData()
|
||||
osrTempData_ = nullptr;
|
||||
}
|
||||
|
||||
JSC::ExecutableAllocator *
|
||||
ExecutableAllocator *
|
||||
JitRuntime::createIonAlloc(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock());
|
||||
|
||||
ionAlloc_ = js_new<JSC::ExecutableAllocator>();
|
||||
ionAlloc_ = js_new<ExecutableAllocator>();
|
||||
if (!ionAlloc_)
|
||||
js_ReportOutOfMemory(cx);
|
||||
return ionAlloc_;
|
||||
@ -690,7 +691,7 @@ JitRuntime::getVMWrapper(const VMFunction &f) const
|
||||
template <AllowGC allowGC>
|
||||
JitCode *
|
||||
JitCode::New(JSContext *cx, uint8_t *code, uint32_t bufferSize, uint32_t headerSize,
|
||||
JSC::ExecutablePool *pool, JSC::CodeKind kind)
|
||||
ExecutablePool *pool, CodeKind kind)
|
||||
{
|
||||
JitCode *codeObj = js::NewJitCode<allowGC>(cx);
|
||||
if (!codeObj) {
|
||||
@ -705,12 +706,12 @@ JitCode::New(JSContext *cx, uint8_t *code, uint32_t bufferSize, uint32_t headerS
|
||||
template
|
||||
JitCode *
|
||||
JitCode::New<CanGC>(JSContext *cx, uint8_t *code, uint32_t bufferSize, uint32_t headerSize,
|
||||
JSC::ExecutablePool *pool, JSC::CodeKind kind);
|
||||
ExecutablePool *pool, CodeKind kind);
|
||||
|
||||
template
|
||||
JitCode *
|
||||
JitCode::New<NoGC>(JSContext *cx, uint8_t *code, uint32_t bufferSize, uint32_t headerSize,
|
||||
JSC::ExecutablePool *pool, JSC::CodeKind kind);
|
||||
ExecutablePool *pool, CodeKind kind);
|
||||
|
||||
void
|
||||
JitCode::copyFrom(MacroAssembler &masm)
|
||||
@ -773,7 +774,7 @@ JitCode::finalize(FreeOp *fop)
|
||||
// Horrible hack: if we are using perf integration, we don't
|
||||
// want to reuse code addresses, so we just leak the memory instead.
|
||||
if (!PerfEnabled())
|
||||
pool_->release(headerSize_ + bufferSize_, JSC::CodeKind(kind_));
|
||||
pool_->release(headerSize_ + bufferSize_, CodeKind(kind_));
|
||||
pool_ = nullptr;
|
||||
}
|
||||
}
|
||||
@ -1512,6 +1513,16 @@ OptimizeMIR(MIRGenerator *mir)
|
||||
if (mir->shouldCancel("Truncate Doubles"))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mir->optimizationInfo().loopUnrollingEnabled()) {
|
||||
AutoTraceLog log(logger, TraceLogger::LoopUnrolling);
|
||||
|
||||
if (!UnrollLoops(graph, r.loopIterationBounds))
|
||||
return false;
|
||||
|
||||
IonSpewPass("Unroll Loops");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
}
|
||||
}
|
||||
|
||||
if (mir->optimizationInfo().eaaEnabled()) {
|
||||
@ -1537,10 +1548,8 @@ OptimizeMIR(MIRGenerator *mir)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make loops contiguious. We do this after GVN/UCE and range analysis,
|
||||
// Make loops contiguous. We do this after GVN/UCE and range analysis,
|
||||
// which can remove CFG edges, exposing more blocks that can be moved.
|
||||
// We also disable this when profiling, since reordering blocks appears
|
||||
// to make the profiler unhappy.
|
||||
{
|
||||
AutoTraceLog log(logger, TraceLogger::MakeLoopsContiguous);
|
||||
if (!MakeLoopsContiguous(graph))
|
||||
@ -2974,7 +2983,7 @@ AutoFlushICache::flush(uintptr_t start, size_t len)
|
||||
AutoFlushICache *afc = TlsPerThreadData.get()->PerThreadData::autoFlushICache();
|
||||
if (!afc) {
|
||||
IonSpewCont(IonSpew_CacheFlush, "#");
|
||||
JSC::ExecutableAllocator::cacheFlush((void*)start, len);
|
||||
ExecutableAllocator::cacheFlush((void*)start, len);
|
||||
JS_ASSERT(len <= 16);
|
||||
return;
|
||||
}
|
||||
@ -2987,7 +2996,7 @@ AutoFlushICache::flush(uintptr_t start, size_t len)
|
||||
}
|
||||
|
||||
IonSpewCont(IonSpew_CacheFlush, afc->inhibit_ ? "x" : "*");
|
||||
JSC::ExecutableAllocator::cacheFlush((void *)start, len);
|
||||
ExecutableAllocator::cacheFlush((void *)start, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -3050,7 +3059,7 @@ AutoFlushICache::~AutoFlushICache()
|
||||
JS_ASSERT(pt->PerThreadData::autoFlushICache() == this);
|
||||
|
||||
if (!inhibit_ && start_)
|
||||
JSC::ExecutableAllocator::cacheFlush((void *)start_, size_t(stop_ - start_));
|
||||
ExecutableAllocator::cacheFlush((void *)start_, size_t(stop_ - start_));
|
||||
|
||||
IonSpewCont(IonSpew_CacheFlush, "%s%s>", name_, start_ ? "" : " U");
|
||||
IonSpewFin(IonSpew_CacheFlush);
|
||||
|
@ -1543,6 +1543,13 @@ IntersectDominators(MBasicBlock *block1, MBasicBlock *block2)
|
||||
return finger1;
|
||||
}
|
||||
|
||||
void
|
||||
jit::ClearDominatorTree(MIRGraph &graph)
|
||||
{
|
||||
for (MBasicBlockIterator iter = graph.begin(); iter != graph.end(); iter++)
|
||||
iter->clearDominatorInfo();
|
||||
}
|
||||
|
||||
static void
|
||||
ComputeImmediateDominators(MIRGraph &graph)
|
||||
{
|
||||
@ -2347,13 +2354,19 @@ LinearSum::multiply(int32_t scale)
|
||||
}
|
||||
|
||||
bool
|
||||
LinearSum::add(const LinearSum &other)
|
||||
LinearSum::add(const LinearSum &other, int32_t scale /* = 1 */)
|
||||
{
|
||||
for (size_t i = 0; i < other.terms_.length(); i++) {
|
||||
if (!add(other.terms_[i].term, other.terms_[i].scale))
|
||||
int32_t newScale = scale;
|
||||
if (!SafeMul(scale, other.terms_[i].scale, &newScale))
|
||||
return false;
|
||||
if (!add(other.terms_[i].term, newScale))
|
||||
return false;
|
||||
}
|
||||
return add(other.constant_);
|
||||
int32_t newConstant = scale;
|
||||
if (!SafeMul(scale, other.constant_, &newConstant))
|
||||
return false;
|
||||
return add(newConstant);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2434,6 +2447,129 @@ LinearSum::dump() const
|
||||
dump(stderr);
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
jit::ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum)
|
||||
{
|
||||
MDefinition *def = nullptr;
|
||||
|
||||
for (size_t i = 0; i < sum.numTerms(); i++) {
|
||||
LinearTerm term = sum.term(i);
|
||||
JS_ASSERT(!term.term->isConstant());
|
||||
if (term.scale == 1) {
|
||||
if (def) {
|
||||
def = MAdd::New(alloc, def, term.term);
|
||||
def->toAdd()->setInt32();
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
def = term.term;
|
||||
}
|
||||
} else if (term.scale == -1) {
|
||||
if (!def) {
|
||||
def = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
}
|
||||
def = MSub::New(alloc, def, term.term);
|
||||
def->toSub()->setInt32();
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
JS_ASSERT(term.scale != 0);
|
||||
MConstant *factor = MConstant::New(alloc, Int32Value(term.scale));
|
||||
block->insertAtEnd(factor);
|
||||
MMul *mul = MMul::New(alloc, term.term, factor);
|
||||
mul->setInt32();
|
||||
block->insertAtEnd(mul);
|
||||
mul->computeRange(alloc);
|
||||
if (def) {
|
||||
def = MAdd::New(alloc, def, mul);
|
||||
def->toAdd()->setInt32();
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
def = mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: The constant component of the term is not converted.
|
||||
if (!def) {
|
||||
def = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertAtEnd(def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
MCompare *
|
||||
jit::ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum)
|
||||
{
|
||||
LinearSum lhs(sum);
|
||||
|
||||
// Look for a term with a -1 scale which we can use for the rhs.
|
||||
MDefinition *rhsDef = nullptr;
|
||||
for (size_t i = 0; i < lhs.numTerms(); i++) {
|
||||
if (lhs.term(i).scale == -1) {
|
||||
rhsDef = lhs.term(i).term;
|
||||
lhs.add(rhsDef, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MDefinition *lhsDef = nullptr;
|
||||
JSOp op = JSOP_GE;
|
||||
|
||||
do {
|
||||
if (!lhs.numTerms()) {
|
||||
lhsDef = MConstant::New(alloc, Int32Value(lhs.constant()));
|
||||
block->insertAtEnd(lhsDef->toInstruction());
|
||||
lhsDef->computeRange(alloc);
|
||||
break;
|
||||
}
|
||||
|
||||
lhsDef = ConvertLinearSum(alloc, block, lhs);
|
||||
if (lhs.constant() == 0)
|
||||
break;
|
||||
|
||||
if (lhs.constant() == -1) {
|
||||
op = JSOP_GT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rhsDef) {
|
||||
int32_t constant = lhs.constant();
|
||||
if (SafeMul(constant, -1, &constant)) {
|
||||
rhsDef = MConstant::New(alloc, Int32Value(constant));
|
||||
block->insertAtEnd(rhsDef->toInstruction());
|
||||
rhsDef->computeRange(alloc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MDefinition *constant = MConstant::New(alloc, Int32Value(lhs.constant()));
|
||||
block->insertAtEnd(constant->toInstruction());
|
||||
constant->computeRange(alloc);
|
||||
lhsDef = MAdd::New(alloc, lhsDef, constant);
|
||||
lhsDef->toAdd()->setInt32();
|
||||
block->insertAtEnd(lhsDef->toInstruction());
|
||||
lhsDef->computeRange(alloc);
|
||||
} while (false);
|
||||
|
||||
if (!rhsDef) {
|
||||
rhsDef = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertAtEnd(rhsDef->toInstruction());
|
||||
rhsDef->computeRange(alloc);
|
||||
}
|
||||
|
||||
MCompare *compare = MCompare::New(alloc, lhsDef, rhsDef, op);
|
||||
block->insertAtEnd(compare);
|
||||
compare->setCompareType(MCompare::Compare_Int32);
|
||||
|
||||
return compare;
|
||||
}
|
||||
|
||||
static bool
|
||||
AnalyzePoppedThis(JSContext *cx, types::TypeObject *type,
|
||||
MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted,
|
||||
|
@ -62,6 +62,9 @@ AccountForCFGChanges(MIRGenerator *mir, MIRGraph &graph, bool updateAliasAnalysi
|
||||
bool
|
||||
RemoveUnmarkedBlocks(MIRGenerator *mir, MIRGraph &graph, uint32_t numMarkedBlocks);
|
||||
|
||||
void
|
||||
ClearDominatorTree(MIRGraph &graph);
|
||||
|
||||
bool
|
||||
BuildDominatorTree(MIRGraph &graph);
|
||||
|
||||
@ -129,13 +132,14 @@ class LinearSum
|
||||
}
|
||||
|
||||
bool multiply(int32_t scale);
|
||||
bool add(const LinearSum &other);
|
||||
bool add(const LinearSum &other, int32_t scale = 1);
|
||||
bool add(MDefinition *term, int32_t scale);
|
||||
bool add(int32_t constant);
|
||||
|
||||
int32_t constant() const { return constant_; }
|
||||
size_t numTerms() const { return terms_.length(); }
|
||||
LinearTerm term(size_t i) const { return terms_[i]; }
|
||||
void replaceTerm(size_t i, MDefinition *def) { terms_[i].term = def; }
|
||||
|
||||
void print(Sprinter &sp) const;
|
||||
void dump(FILE *) const;
|
||||
@ -146,6 +150,16 @@ class LinearSum
|
||||
int32_t constant_;
|
||||
};
|
||||
|
||||
// Convert all components of a linear sum *except* its constant to a definition,
|
||||
// adding any necessary instructions to the end of block.
|
||||
MDefinition *
|
||||
ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum);
|
||||
|
||||
// Convert the test 'sum >= 0' to a comparison, adding any necessary
|
||||
// instructions to the end of block.
|
||||
MCompare *
|
||||
ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum);
|
||||
|
||||
bool
|
||||
AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
|
||||
types::TypeObject *type, HandleObject baseobj,
|
||||
|
@ -107,7 +107,7 @@ IonCache::LinkStatus
|
||||
IonCache::linkCode(JSContext *cx, MacroAssembler &masm, IonScript *ion, JitCode **code)
|
||||
{
|
||||
Linker linker(masm);
|
||||
*code = linker.newCode<CanGC>(cx, JSC::ION_CODE);
|
||||
*code = linker.newCode<CanGC>(cx, ION_CODE);
|
||||
if (!*code)
|
||||
return LINK_ERROR;
|
||||
|
||||
|
@ -19,10 +19,6 @@
|
||||
#include "jit/IonOptimizationLevels.h"
|
||||
#include "jit/IonTypes.h"
|
||||
|
||||
namespace JSC {
|
||||
class ExecutablePool;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
class AsmJSModule;
|
||||
@ -37,7 +33,7 @@ class JitCode : public gc::BarrieredCell<JitCode>
|
||||
{
|
||||
protected:
|
||||
uint8_t *code_;
|
||||
JSC::ExecutablePool *pool_;
|
||||
ExecutablePool *pool_;
|
||||
uint32_t bufferSize_; // Total buffer size. Does not include headerSize_.
|
||||
uint32_t insnSize_; // Instruction stream size.
|
||||
uint32_t dataSize_; // Size of the read-only data area.
|
||||
@ -45,7 +41,7 @@ class JitCode : public gc::BarrieredCell<JitCode>
|
||||
uint32_t dataRelocTableBytes_; // Size of the data relocation table.
|
||||
uint32_t preBarrierTableBytes_; // Size of the prebarrier table.
|
||||
uint8_t headerSize_ : 5; // Number of bytes allocated before codeStart.
|
||||
uint8_t kind_ : 3; // JSC::CodeKind, for the memory reporters.
|
||||
uint8_t kind_ : 3; // jit::CodeKind, for the memory reporters.
|
||||
bool invalidated_ : 1; // Whether the code object has been invalidated.
|
||||
// This is necessary to prevent GC tracing.
|
||||
|
||||
@ -58,8 +54,8 @@ class JitCode : public gc::BarrieredCell<JitCode>
|
||||
: code_(nullptr),
|
||||
pool_(nullptr)
|
||||
{ }
|
||||
JitCode(uint8_t *code, uint32_t bufferSize, uint32_t headerSize, JSC::ExecutablePool *pool,
|
||||
JSC::CodeKind kind)
|
||||
JitCode(uint8_t *code, uint32_t bufferSize, uint32_t headerSize, ExecutablePool *pool,
|
||||
CodeKind kind)
|
||||
: code_(code),
|
||||
pool_(pool),
|
||||
bufferSize_(bufferSize),
|
||||
@ -72,7 +68,7 @@ class JitCode : public gc::BarrieredCell<JitCode>
|
||||
kind_(kind),
|
||||
invalidated_(false)
|
||||
{
|
||||
MOZ_ASSERT(JSC::CodeKind(kind_) == kind);
|
||||
MOZ_ASSERT(CodeKind(kind_) == kind);
|
||||
MOZ_ASSERT(headerSize_ == headerSize);
|
||||
}
|
||||
|
||||
@ -136,7 +132,7 @@ class JitCode : public gc::BarrieredCell<JitCode>
|
||||
// automatically released, so the code may be freed.
|
||||
template <AllowGC allowGC>
|
||||
static JitCode *New(JSContext *cx, uint8_t *code, uint32_t bufferSize, uint32_t headerSize,
|
||||
JSC::ExecutablePool *pool, JSC::CodeKind kind);
|
||||
ExecutablePool *pool, CodeKind kind);
|
||||
|
||||
public:
|
||||
static inline ThingRootKind rootKind() { return THING_ROOT_JIT_CODE; }
|
||||
|
@ -29,14 +29,14 @@ class Linker
|
||||
}
|
||||
|
||||
template <AllowGC allowGC>
|
||||
JitCode *newCode(JSContext *cx, JSC::ExecutableAllocator *execAlloc, JSC::CodeKind kind) {
|
||||
JitCode *newCode(JSContext *cx, ExecutableAllocator *execAlloc, CodeKind kind) {
|
||||
JS_ASSERT(masm.numAsmJSAbsoluteLinks() == 0);
|
||||
|
||||
gc::AutoSuppressGC suppressGC(cx);
|
||||
if (masm.oom())
|
||||
return fail(cx);
|
||||
|
||||
JSC::ExecutablePool *pool;
|
||||
ExecutablePool *pool;
|
||||
size_t bytesNeeded = masm.bytesNeeded() + sizeof(JitCode *) + CodeAlignment;
|
||||
if (bytesNeeded >= MAX_BUFFER_SIZE)
|
||||
return fail(cx);
|
||||
@ -77,7 +77,7 @@ class Linker
|
||||
}
|
||||
|
||||
template <AllowGC allowGC>
|
||||
JitCode *newCode(JSContext *cx, JSC::CodeKind kind) {
|
||||
JitCode *newCode(JSContext *cx, CodeKind kind) {
|
||||
return newCode<allowGC>(cx, cx->runtime()->jitRuntime()->execAlloc(), kind);
|
||||
}
|
||||
|
||||
@ -86,11 +86,11 @@ class Linker
|
||||
// thread requesting an interrupt may use the executable allocator below.
|
||||
JS_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock());
|
||||
|
||||
JSC::ExecutableAllocator *alloc = cx->runtime()->jitRuntime()->getIonAlloc(cx);
|
||||
ExecutableAllocator *alloc = cx->runtime()->jitRuntime()->getIonAlloc(cx);
|
||||
if (!alloc)
|
||||
return nullptr;
|
||||
|
||||
return newCode<CanGC>(cx, alloc, JSC::ION_CODE);
|
||||
return newCode<CanGC>(cx, alloc, ION_CODE);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -32,6 +32,7 @@ OptimizationInfo::initNormalOptimizationInfo()
|
||||
licm_ = true;
|
||||
uce_ = true;
|
||||
rangeAnalysis_ = true;
|
||||
loopUnrolling_ = true;
|
||||
autoTruncate_ = true;
|
||||
registerAllocator_ = RegisterAllocator_LSRA;
|
||||
|
||||
|
@ -73,6 +73,9 @@ class OptimizationInfo
|
||||
// Toggles whether Range Analysis is used.
|
||||
bool rangeAnalysis_;
|
||||
|
||||
// Toggles whether loop unrolling is performed.
|
||||
bool loopUnrolling_;
|
||||
|
||||
// Toggles whether Truncation based on Range Analysis is used.
|
||||
bool autoTruncate_;
|
||||
|
||||
@ -144,6 +147,10 @@ class OptimizationInfo
|
||||
return rangeAnalysis_ && !js_JitOptions.disableRangeAnalysis;
|
||||
}
|
||||
|
||||
bool loopUnrollingEnabled() const {
|
||||
return loopUnrolling_ && !js_JitOptions.disableLoopUnrolling;
|
||||
}
|
||||
|
||||
bool autoTruncateEnabled() const {
|
||||
return autoTruncate_ && rangeAnalysisEnabled();
|
||||
}
|
||||
|
@ -248,6 +248,7 @@ jit::CheckLogging()
|
||||
" pools Literal Pools (ARM only for now)\n"
|
||||
" cacheflush Instruction Cache flushes (ARM only for now)\n"
|
||||
" range Range Analysis\n"
|
||||
" unroll Loop unrolling\n"
|
||||
" logs C1 and JSON visualization logging\n"
|
||||
" all Everything\n"
|
||||
"\n"
|
||||
@ -277,6 +278,8 @@ jit::CheckLogging()
|
||||
EnableChannel(IonSpew_GVN);
|
||||
if (ContainsFlag(env, "range"))
|
||||
EnableChannel(IonSpew_Range);
|
||||
if (ContainsFlag(env, "unroll"))
|
||||
EnableChannel(IonSpew_Unrolling);
|
||||
if (ContainsFlag(env, "licm"))
|
||||
EnableChannel(IonSpew_LICM);
|
||||
if (ContainsFlag(env, "regalloc"))
|
||||
|
@ -34,6 +34,8 @@ namespace jit {
|
||||
_(GVN) \
|
||||
/* Information during Range analysis */ \
|
||||
_(Range) \
|
||||
/* Information during loop unrolling */ \
|
||||
_(Unrolling) \
|
||||
/* Information during LICM */ \
|
||||
_(LICM) \
|
||||
/* Information during regalloc */ \
|
||||
|
@ -142,14 +142,14 @@ class JitRuntime
|
||||
|
||||
// Executable allocator for all code except the main code in an IonScript.
|
||||
// Shared with the runtime.
|
||||
JSC::ExecutableAllocator *execAlloc_;
|
||||
ExecutableAllocator *execAlloc_;
|
||||
|
||||
// Executable allocator used for allocating the main code in an IonScript.
|
||||
// All accesses on this allocator must be protected by the runtime's
|
||||
// interrupt lock, as the executable memory may be protected() when
|
||||
// requesting an interrupt to force a fault in the Ion code and avoid the
|
||||
// need for explicit interrupt checks.
|
||||
JSC::ExecutableAllocator *ionAlloc_;
|
||||
ExecutableAllocator *ionAlloc_;
|
||||
|
||||
// Shared post-exception-handler tail
|
||||
JitCode *exceptionTail_;
|
||||
@ -248,7 +248,7 @@ class JitRuntime
|
||||
JitCode *generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut);
|
||||
JitCode *generateVMWrapper(JSContext *cx, const VMFunction &f);
|
||||
|
||||
JSC::ExecutableAllocator *createIonAlloc(JSContext *cx);
|
||||
ExecutableAllocator *createIonAlloc(JSContext *cx);
|
||||
|
||||
public:
|
||||
JitRuntime();
|
||||
@ -260,16 +260,16 @@ class JitRuntime
|
||||
|
||||
static void Mark(JSTracer *trc);
|
||||
|
||||
JSC::ExecutableAllocator *execAlloc() const {
|
||||
ExecutableAllocator *execAlloc() const {
|
||||
return execAlloc_;
|
||||
}
|
||||
|
||||
JSC::ExecutableAllocator *getIonAlloc(JSContext *cx) {
|
||||
ExecutableAllocator *getIonAlloc(JSContext *cx) {
|
||||
JS_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock());
|
||||
return ionAlloc_ ? ionAlloc_ : createIonAlloc(cx);
|
||||
}
|
||||
|
||||
JSC::ExecutableAllocator *ionAlloc(JSRuntime *rt) {
|
||||
ExecutableAllocator *ionAlloc(JSRuntime *rt) {
|
||||
JS_ASSERT(rt->currentThreadOwnsInterruptLock());
|
||||
return ionAlloc_;
|
||||
}
|
||||
@ -469,7 +469,7 @@ class JitCompartment
|
||||
|
||||
void toggleBaselineStubBarriers(bool enabled);
|
||||
|
||||
JSC::ExecutableAllocator *createIonAlloc();
|
||||
ExecutableAllocator *createIonAlloc();
|
||||
|
||||
public:
|
||||
JitCompartment();
|
||||
|
@ -54,6 +54,9 @@ JitOptions::JitOptions()
|
||||
// Toggles whether Range Analysis is globally disabled.
|
||||
disableRangeAnalysis = false;
|
||||
|
||||
// Toggles whether Loop Unrolling is globally disabled.
|
||||
disableLoopUnrolling = true;
|
||||
|
||||
// Toggles whether Unreachable Code Elimination is globally disabled.
|
||||
disableUce = false;
|
||||
|
||||
|
@ -40,6 +40,7 @@ struct JitOptions
|
||||
bool disableInlining;
|
||||
bool disableEdgeCaseAnalysis;
|
||||
bool disableRangeAnalysis;
|
||||
bool disableLoopUnrolling;
|
||||
bool disableUce;
|
||||
bool disableEaa;
|
||||
bool eagerCompilation;
|
||||
|
373
js/src/jit/LoopUnroller.cpp
Normal file
373
js/src/jit/LoopUnroller.cpp
Normal file
@ -0,0 +1,373 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 "jit/LoopUnroller.h"
|
||||
|
||||
#include "jit/MIRGraph.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
using mozilla::ArrayLength;
|
||||
|
||||
namespace {
|
||||
|
||||
struct LoopUnroller
|
||||
{
|
||||
typedef HashMap<MDefinition *, MDefinition *,
|
||||
PointerHasher<MDefinition *, 2>, SystemAllocPolicy> DefinitionMap;
|
||||
|
||||
explicit LoopUnroller(MIRGraph &graph)
|
||||
: graph(graph), alloc(graph.alloc())
|
||||
{}
|
||||
|
||||
MIRGraph &graph;
|
||||
TempAllocator &alloc;
|
||||
|
||||
// Header and body of the original loop.
|
||||
MBasicBlock *header, *backedge;
|
||||
|
||||
// Header and body of the unrolled loop.
|
||||
MBasicBlock *unrolledHeader, *unrolledBackedge;
|
||||
|
||||
// Old and new preheaders. The old preheader starts out associated with the
|
||||
// original loop, but becomes the preheader of the new loop. The new
|
||||
// preheader will be given to the original loop.
|
||||
MBasicBlock *oldPreheader, *newPreheader;
|
||||
|
||||
// Map terms in the original loop to terms in the current unrolled iteration.
|
||||
DefinitionMap unrolledDefinitions;
|
||||
|
||||
MDefinition *getReplacementDefinition(MDefinition *def);
|
||||
MResumePoint *makeReplacementResumePoint(MBasicBlock *block, MResumePoint *rp);
|
||||
void makeReplacementInstruction(MInstruction *ins);
|
||||
|
||||
void go(LoopIterationBound *bound);
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
MDefinition *
|
||||
LoopUnroller::getReplacementDefinition(MDefinition *def)
|
||||
{
|
||||
if (def->block()->id() < header->id()) {
|
||||
// The definition is loop invariant.
|
||||
return def;
|
||||
}
|
||||
|
||||
DefinitionMap::Ptr p = unrolledDefinitions.lookup(def);
|
||||
JS_ASSERT(p);
|
||||
|
||||
return p->value();
|
||||
}
|
||||
|
||||
void
|
||||
LoopUnroller::makeReplacementInstruction(MInstruction *ins)
|
||||
{
|
||||
MDefinitionVector inputs(alloc);
|
||||
for (size_t i = 0; i < ins->numOperands(); i++) {
|
||||
MDefinition *old = ins->getOperand(i);
|
||||
MDefinition *replacement = getReplacementDefinition(old);
|
||||
if (!inputs.append(replacement))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::makeReplacementDefinition");
|
||||
}
|
||||
|
||||
MInstruction *clone = ins->clone(alloc, inputs);
|
||||
|
||||
unrolledBackedge->add(clone);
|
||||
|
||||
if (!unrolledDefinitions.putNew(ins, clone))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::makeReplacementDefinition");
|
||||
|
||||
if (MResumePoint *old = ins->resumePoint()) {
|
||||
MResumePoint *rp = makeReplacementResumePoint(unrolledBackedge, old);
|
||||
clone->setResumePoint(rp);
|
||||
}
|
||||
}
|
||||
|
||||
MResumePoint *
|
||||
LoopUnroller::makeReplacementResumePoint(MBasicBlock *block, MResumePoint *rp)
|
||||
{
|
||||
MDefinitionVector inputs(alloc);
|
||||
for (size_t i = 0; i < rp->stackDepth(); i++) {
|
||||
MDefinition *old = rp->getOperand(i);
|
||||
MDefinition *replacement = old->isUnused() ? old : getReplacementDefinition(old);
|
||||
if (!inputs.append(replacement))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::makeReplacementResumePoint");
|
||||
}
|
||||
|
||||
MResumePoint *clone = MResumePoint::New(alloc, block, rp->pc(), rp->caller(), rp->mode(), inputs);
|
||||
if (!clone)
|
||||
CrashAtUnhandlableOOM("LoopUnroller::makeReplacementResumePoint");
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
void
|
||||
LoopUnroller::go(LoopIterationBound *bound)
|
||||
{
|
||||
// For now we always unroll loops the same number of times.
|
||||
static const size_t UnrollCount = 10;
|
||||
|
||||
IonSpew(IonSpew_Unrolling, "Attempting to unroll loop");
|
||||
|
||||
header = bound->header;
|
||||
JS_ASSERT(header->isLoopHeader());
|
||||
|
||||
backedge = header->backedge();
|
||||
oldPreheader = header->loopPredecessor();
|
||||
|
||||
JS_ASSERT(oldPreheader->numSuccessors() == 1);
|
||||
|
||||
// Only unroll loops with two blocks: an initial one ending with the
|
||||
// bound's test, and the body ending with the backedge.
|
||||
MTest *test = bound->test;
|
||||
if (header->lastIns() != test)
|
||||
return;
|
||||
if (test->ifTrue() == backedge) {
|
||||
if (test->ifFalse()->id() <= backedge->id())
|
||||
return;
|
||||
} else if (test->ifFalse() == backedge) {
|
||||
if (test->ifTrue()->id() <= backedge->id())
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (backedge->numPredecessors() != 1 || backedge->numSuccessors() != 1)
|
||||
return;
|
||||
JS_ASSERT(backedge->phisEmpty());
|
||||
|
||||
MBasicBlock *bodyBlocks[] = { header, backedge };
|
||||
|
||||
// All instructions in the header and body must be clonable.
|
||||
for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
|
||||
MBasicBlock *block = bodyBlocks[i];
|
||||
for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
|
||||
MInstruction *ins = *iter;
|
||||
if (ins->canClone())
|
||||
continue;
|
||||
if (ins->isTest() || ins->isGoto() || ins->isInterruptCheck())
|
||||
continue;
|
||||
IonSpew(IonSpew_Unrolling, "Aborting: can't clone instruction %s", ins->opName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the linear inequality we will use for exiting the unrolled loop:
|
||||
//
|
||||
// iterationBound - iterationCount - UnrollCount >= 0
|
||||
//
|
||||
LinearSum remainingIterationsInequality(bound->boundSum);
|
||||
if (!remainingIterationsInequality.add(bound->currentSum, -1))
|
||||
return;
|
||||
if (!remainingIterationsInequality.add(-int32_t(UnrollCount)))
|
||||
return;
|
||||
|
||||
// Terms in the inequality need to be either loop invariant or phis from
|
||||
// the original header.
|
||||
for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) {
|
||||
MDefinition *def = remainingIterationsInequality.term(i).term;
|
||||
if (def->block()->id() < header->id())
|
||||
continue;
|
||||
if (def->block() == header && def->isPhi())
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// OK, we've checked everything, now unroll the loop.
|
||||
|
||||
IonSpew(IonSpew_Unrolling, "Unrolling loop");
|
||||
|
||||
// The old preheader will go before the unrolled loop, and the old loop
|
||||
// will need a new empty preheader.
|
||||
CompileInfo &info = oldPreheader->info();
|
||||
if (header->trackedSite().pc()) {
|
||||
unrolledHeader =
|
||||
MBasicBlock::New(graph, nullptr, info,
|
||||
oldPreheader, header->trackedSite(), MBasicBlock::LOOP_HEADER);
|
||||
unrolledBackedge =
|
||||
MBasicBlock::New(graph, nullptr, info,
|
||||
unrolledHeader, backedge->trackedSite(), MBasicBlock::NORMAL);
|
||||
newPreheader =
|
||||
MBasicBlock::New(graph, nullptr, info,
|
||||
unrolledHeader, oldPreheader->trackedSite(), MBasicBlock::NORMAL);
|
||||
} else {
|
||||
unrolledHeader = MBasicBlock::NewAsmJS(graph, info, oldPreheader, MBasicBlock::LOOP_HEADER);
|
||||
unrolledBackedge = MBasicBlock::NewAsmJS(graph, info, unrolledHeader, MBasicBlock::NORMAL);
|
||||
newPreheader = MBasicBlock::NewAsmJS(graph, info, unrolledHeader, MBasicBlock::NORMAL);
|
||||
}
|
||||
|
||||
unrolledHeader->discardAllResumePoints();
|
||||
unrolledBackedge->discardAllResumePoints();
|
||||
newPreheader->discardAllResumePoints();
|
||||
|
||||
// Insert new blocks at their RPO position, and update block ids.
|
||||
graph.insertBlockAfter(oldPreheader, unrolledHeader);
|
||||
graph.insertBlockAfter(unrolledHeader, unrolledBackedge);
|
||||
graph.insertBlockAfter(unrolledBackedge, newPreheader);
|
||||
graph.renumberBlocksAfter(oldPreheader);
|
||||
|
||||
if (!unrolledDefinitions.init())
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
|
||||
// Add phis to the unrolled loop header which correspond to the phis in the
|
||||
// original loop header.
|
||||
JS_ASSERT(header->getPredecessor(0) == oldPreheader);
|
||||
for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
|
||||
MPhi *old = *iter;
|
||||
JS_ASSERT(old->numOperands() == 2);
|
||||
MPhi *phi = MPhi::New(alloc);
|
||||
phi->setResultType(old->type());
|
||||
phi->setResultTypeSet(old->resultTypeSet());
|
||||
phi->setRange(old->range());
|
||||
|
||||
unrolledHeader->addPhi(phi);
|
||||
|
||||
if (!phi->reserveLength(2))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
|
||||
// Set the first input for the phi for now. We'll set the second after
|
||||
// finishing the unroll.
|
||||
phi->addInput(old->getOperand(0));
|
||||
|
||||
// The old phi will now take the value produced by the unrolled loop.
|
||||
old->replaceOperand(0, phi);
|
||||
|
||||
if (!unrolledDefinitions.putNew(old, phi))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
}
|
||||
|
||||
// The loop condition can bail out on e.g. integer overflow, so make a
|
||||
// resume point based on the initial resume point of the original header.
|
||||
MResumePoint *headerResumePoint = header->entryResumePoint();
|
||||
if (headerResumePoint) {
|
||||
MResumePoint *rp = makeReplacementResumePoint(unrolledHeader, headerResumePoint);
|
||||
unrolledHeader->setEntryResumePoint(rp);
|
||||
|
||||
// Perform an interrupt check at the start of the unrolled loop.
|
||||
unrolledHeader->add(MInterruptCheck::New(alloc));
|
||||
}
|
||||
|
||||
// Generate code for the test in the unrolled loop.
|
||||
for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) {
|
||||
MDefinition *def = remainingIterationsInequality.term(i).term;
|
||||
MDefinition *replacement = getReplacementDefinition(def);
|
||||
remainingIterationsInequality.replaceTerm(i, replacement);
|
||||
}
|
||||
MCompare *compare = ConvertLinearInequality(alloc, unrolledHeader, remainingIterationsInequality);
|
||||
MTest *unrolledTest = MTest::New(alloc, compare, unrolledBackedge, newPreheader);
|
||||
unrolledHeader->end(unrolledTest);
|
||||
|
||||
// Make an entry resume point for the unrolled body. The unrolled header
|
||||
// does not have side effects on stack values, even if the original loop
|
||||
// header does, so use the same resume point as for the unrolled header.
|
||||
if (headerResumePoint) {
|
||||
MResumePoint *rp = makeReplacementResumePoint(unrolledBackedge, headerResumePoint);
|
||||
unrolledBackedge->setEntryResumePoint(rp);
|
||||
}
|
||||
|
||||
// Make an entry resume point for the new preheader. There are no
|
||||
// instructions which use this but some other stuff wants one to be here.
|
||||
if (headerResumePoint) {
|
||||
MResumePoint *rp = makeReplacementResumePoint(newPreheader, headerResumePoint);
|
||||
newPreheader->setEntryResumePoint(rp);
|
||||
}
|
||||
|
||||
// Generate the unrolled code.
|
||||
JS_ASSERT(UnrollCount > 1);
|
||||
size_t unrollIndex = 0;
|
||||
while (true) {
|
||||
// Clone the contents of the original loop into the unrolled loop body.
|
||||
for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
|
||||
MBasicBlock *block = bodyBlocks[i];
|
||||
for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
|
||||
MInstruction *ins = *iter;
|
||||
if (ins->canClone()) {
|
||||
makeReplacementInstruction(*iter);
|
||||
} else {
|
||||
// Control instructions are handled separately.
|
||||
JS_ASSERT(ins->isTest() || ins->isGoto() || ins->isInterruptCheck());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the value of each loop header phi after the execution of
|
||||
// this unrolled iteration.
|
||||
MDefinitionVector phiValues(alloc);
|
||||
JS_ASSERT(header->getPredecessor(1) == backedge);
|
||||
for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
|
||||
MPhi *old = *iter;
|
||||
MDefinition *oldInput = old->getOperand(1);
|
||||
if (!phiValues.append(getReplacementDefinition(oldInput)))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
}
|
||||
|
||||
unrolledDefinitions.clear();
|
||||
|
||||
if (unrollIndex == UnrollCount - 1) {
|
||||
// We're at the end of the last unrolled iteration, set the
|
||||
// backedge input for the unrolled loop phis.
|
||||
size_t phiIndex = 0;
|
||||
for (MPhiIterator iter(unrolledHeader->phisBegin()); iter != unrolledHeader->phisEnd(); iter++) {
|
||||
MPhi *phi = *iter;
|
||||
phi->addInput(phiValues[phiIndex++]);
|
||||
}
|
||||
JS_ASSERT(phiIndex == phiValues.length());
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the map for the phis in the next iteration.
|
||||
size_t phiIndex = 0;
|
||||
for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
|
||||
MPhi *old = *iter;
|
||||
if (!unrolledDefinitions.putNew(old, phiValues[phiIndex++]))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
}
|
||||
JS_ASSERT(phiIndex == phiValues.length());
|
||||
|
||||
unrollIndex++;
|
||||
}
|
||||
|
||||
MGoto *backedgeJump = MGoto::New(alloc, unrolledHeader);
|
||||
unrolledBackedge->end(backedgeJump);
|
||||
|
||||
// Place the old preheader before the unrolled loop.
|
||||
JS_ASSERT(oldPreheader->lastIns()->isGoto());
|
||||
oldPreheader->discardLastIns();
|
||||
oldPreheader->end(MGoto::New(alloc, unrolledHeader));
|
||||
|
||||
// Place the new preheader before the original loop.
|
||||
newPreheader->end(MGoto::New(alloc, header));
|
||||
|
||||
// Cleanup the MIR graph.
|
||||
if (!unrolledHeader->addPredecessorWithoutPhis(unrolledBackedge))
|
||||
CrashAtUnhandlableOOM("LoopUnroller::go");
|
||||
header->replacePredecessor(oldPreheader, newPreheader);
|
||||
oldPreheader->setSuccessorWithPhis(unrolledHeader, 0);
|
||||
newPreheader->setSuccessorWithPhis(header, 0);
|
||||
unrolledBackedge->setSuccessorWithPhis(unrolledHeader, 1);
|
||||
}
|
||||
|
||||
bool
|
||||
jit::UnrollLoops(MIRGraph &graph, const LoopIterationBoundVector &bounds)
|
||||
{
|
||||
if (bounds.empty())
|
||||
return true;
|
||||
|
||||
for (size_t i = 0; i < bounds.length(); i++) {
|
||||
LoopUnroller unroller(graph);
|
||||
unroller.go(bounds[i]);
|
||||
}
|
||||
|
||||
// The MIR graph is valid, but now has several new blocks. Update or
|
||||
// recompute previous analysis information for the remaining optimization
|
||||
// passes.
|
||||
ClearDominatorTree(graph);
|
||||
if (!BuildDominatorTree(graph))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
21
js/src/jit/LoopUnroller.h
Normal file
21
js/src/jit/LoopUnroller.h
Normal file
@ -0,0 +1,21 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 jit_LoopUnroller_h
|
||||
#define jit_LoopUnroller_h
|
||||
|
||||
#include "jit/RangeAnalysis.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
bool
|
||||
UnrollLoops(MIRGraph &graph, const LoopIterationBoundVector &bounds);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif // jit_LoopUnroller_h
|
@ -2351,6 +2351,20 @@ MResumePoint::New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, MRes
|
||||
return resume;
|
||||
}
|
||||
|
||||
MResumePoint *
|
||||
MResumePoint::New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, MResumePoint *parent,
|
||||
Mode mode, const MDefinitionVector &operands)
|
||||
{
|
||||
MResumePoint *resume = new(alloc) MResumePoint(block, pc, parent, mode);
|
||||
|
||||
if (!resume->operands_.init(alloc, operands.length()))
|
||||
return nullptr;
|
||||
for (size_t i = 0; i < operands.length(); i++)
|
||||
resume->initOperand(i, operands[i]);
|
||||
|
||||
return resume;
|
||||
}
|
||||
|
||||
MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *caller,
|
||||
Mode mode)
|
||||
: MNode(block),
|
||||
|
192
js/src/jit/MIR.h
192
js/src/jit/MIR.h
@ -138,6 +138,13 @@ class MUse : public TempObject, public InlineListNode<MUse>
|
||||
: producer_(nullptr), consumer_(nullptr)
|
||||
{ }
|
||||
|
||||
// MUses can only be copied when they are not in a use list.
|
||||
explicit MUse(const MUse &other)
|
||||
: producer_(other.producer_), consumer_(other.consumer_)
|
||||
{
|
||||
JS_ASSERT(!other.next && !other.prev);
|
||||
}
|
||||
|
||||
// Set this use, which was previously clear.
|
||||
inline void init(MDefinition *producer, MNode *consumer);
|
||||
// Like init, but works even when the use contains uninitialized data.
|
||||
@ -386,6 +393,17 @@ class MDefinition : public MNode
|
||||
trackedSite_()
|
||||
{ }
|
||||
|
||||
// Copying a definition leaves the list of uses and the block empty.
|
||||
explicit MDefinition(const MDefinition &other)
|
||||
: id_(0),
|
||||
flags_(other.flags_),
|
||||
range_(other.range_),
|
||||
resultType_(other.resultType_),
|
||||
resultTypeSet_(other.resultTypeSet_),
|
||||
dependency_(other.dependency_),
|
||||
trackedSite_(other.trackedSite_)
|
||||
{ }
|
||||
|
||||
virtual Opcode op() const = 0;
|
||||
virtual const char *opName() const = 0;
|
||||
virtual bool accept(MDefinitionVisitor *visitor) = 0;
|
||||
@ -766,6 +784,8 @@ class MUseDefIterator
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector<MDefinition *, 8, IonAllocPolicy> MDefinitionVector;
|
||||
|
||||
// An instruction is an SSA name that is inserted into a basic block's IR
|
||||
// stream.
|
||||
class MInstruction
|
||||
@ -779,6 +799,12 @@ class MInstruction
|
||||
: resumePoint_(nullptr)
|
||||
{ }
|
||||
|
||||
// Copying an instruction leaves the block and resume point as empty.
|
||||
explicit MInstruction(const MInstruction &other)
|
||||
: MDefinition(other),
|
||||
resumePoint_(nullptr)
|
||||
{ }
|
||||
|
||||
void setResumePoint(MResumePoint *resumePoint) {
|
||||
JS_ASSERT(!resumePoint_);
|
||||
resumePoint_ = resumePoint;
|
||||
@ -791,6 +817,17 @@ class MInstruction
|
||||
MResumePoint *resumePoint() const {
|
||||
return resumePoint_;
|
||||
}
|
||||
|
||||
// For instructions which can be cloned with new inputs, with all other
|
||||
// information being the same. clone() implementations do not need to worry
|
||||
// about cloning generic MInstruction/MDefinition state like flags and
|
||||
// resume points.
|
||||
virtual bool canClone() const {
|
||||
return false;
|
||||
}
|
||||
virtual MInstruction *clone(TempAllocator &alloc, const MDefinitionVector &inputs) const {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
};
|
||||
|
||||
#define INSTRUCTION_HEADER(opcode) \
|
||||
@ -804,6 +841,18 @@ class MInstruction
|
||||
return visitor->visit##opcode(this); \
|
||||
}
|
||||
|
||||
#define ALLOW_CLONE(typename) \
|
||||
bool canClone() const { \
|
||||
return true; \
|
||||
} \
|
||||
MInstruction *clone(TempAllocator &alloc, \
|
||||
const MDefinitionVector &inputs) const { \
|
||||
MInstruction *res = new(alloc) typename(*this); \
|
||||
for (size_t i = 0; i < numOperands(); i++) \
|
||||
res->replaceOperand(i, inputs[i]); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
template <size_t Arity>
|
||||
class MAryInstruction : public MInstruction
|
||||
{
|
||||
@ -835,6 +884,15 @@ class MAryInstruction : public MInstruction
|
||||
void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
|
||||
operands_[index].replaceProducer(operand);
|
||||
}
|
||||
|
||||
MAryInstruction() { }
|
||||
|
||||
explicit MAryInstruction(const MAryInstruction<Arity> &other)
|
||||
: MInstruction(other)
|
||||
{
|
||||
for (int i = 0; i < (int) Arity; i++) // N.B. use |int| to avoid warnings when Arity == 0
|
||||
operands_[i].init(other.operands_[i].producer(), this);
|
||||
}
|
||||
};
|
||||
|
||||
class MNullaryInstruction : public MAryInstruction<0>
|
||||
@ -1070,6 +1128,8 @@ class MNop : public MNullaryInstruction
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MNop)
|
||||
};
|
||||
|
||||
// Truncation barrier. This is intended for protecting its input against
|
||||
@ -1169,6 +1229,8 @@ class MConstant : public MNullaryInstruction
|
||||
bool truncate(TruncateKind kind);
|
||||
|
||||
bool canProduceFloat32() const;
|
||||
|
||||
ALLOW_CLONE(MConstant)
|
||||
};
|
||||
|
||||
// Deep clone a constant JSObject.
|
||||
@ -2785,6 +2847,8 @@ class MCompare
|
||||
}
|
||||
# endif
|
||||
|
||||
ALLOW_CLONE(MCompare)
|
||||
|
||||
protected:
|
||||
bool congruentTo(const MDefinition *ins) const {
|
||||
if (!binaryCongruentTo(ins))
|
||||
@ -2828,6 +2892,8 @@ class MBox : public MUnaryInstruction
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBox)
|
||||
};
|
||||
|
||||
// Note: the op may have been inverted during lowering (to put constants in a
|
||||
@ -2945,6 +3011,8 @@ class MUnbox : public MUnaryInstruction, public BoxInputsPolicy
|
||||
JS_ASSERT(mode() != Fallible);
|
||||
mode_ = Infallible;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MUnbox)
|
||||
};
|
||||
|
||||
class MGuardObject : public MUnaryInstruction, public SingleObjectPolicy
|
||||
@ -3388,6 +3456,8 @@ class MToDouble
|
||||
void setTruncateKind(TruncateKind kind) {
|
||||
implicitTruncate_ = Max(implicitTruncate_, kind);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MToDouble)
|
||||
};
|
||||
|
||||
// Converts a primitive (either typed or untyped) to a float32. If the input is
|
||||
@ -3451,6 +3521,8 @@ class MToFloat32
|
||||
|
||||
bool canConsumeFloat32(MUse *use) const { return true; }
|
||||
bool canProduceFloat32() const { return true; }
|
||||
|
||||
ALLOW_CLONE(MToFloat32)
|
||||
};
|
||||
|
||||
// Converts a uint32 to a double (coming from asm.js).
|
||||
@ -3572,6 +3644,8 @@ class MToInt32
|
||||
#ifdef DEBUG
|
||||
bool isConsistentFloat32Use(MUse *use) const { return true; }
|
||||
#endif
|
||||
|
||||
ALLOW_CLONE(MToInt32)
|
||||
};
|
||||
|
||||
// Converts a value or typed input to a truncated int32, for use with bitwise
|
||||
@ -3614,6 +3688,8 @@ class MTruncateToInt32 : public MUnaryInstruction
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
ALLOW_CLONE(MTruncateToInt32)
|
||||
};
|
||||
|
||||
// Converts any type to a string
|
||||
@ -3652,6 +3728,8 @@ class MToString :
|
||||
bool fallible() const {
|
||||
return input()->mightBeType(MIRType_Object);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MToString)
|
||||
};
|
||||
|
||||
class MBitNot
|
||||
@ -3692,6 +3770,8 @@ class MBitNot
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ != MIRType_None;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBitNot)
|
||||
};
|
||||
|
||||
class MTypeOf
|
||||
@ -3837,6 +3917,8 @@ class MBitAnd : public MBinaryBitwiseInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ != MIRType_None;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBitAnd)
|
||||
};
|
||||
|
||||
class MBitOr : public MBinaryBitwiseInstruction
|
||||
@ -3864,6 +3946,8 @@ class MBitOr : public MBinaryBitwiseInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ != MIRType_None;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBitOr)
|
||||
};
|
||||
|
||||
class MBitXor : public MBinaryBitwiseInstruction
|
||||
@ -3892,6 +3976,8 @@ class MBitXor : public MBinaryBitwiseInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MBitXor)
|
||||
};
|
||||
|
||||
class MShiftInstruction
|
||||
@ -3934,6 +4020,8 @@ class MLsh : public MShiftInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ != MIRType_None;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MLsh)
|
||||
};
|
||||
|
||||
class MRsh : public MShiftInstruction
|
||||
@ -3958,6 +4046,8 @@ class MRsh : public MShiftInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MRsh)
|
||||
};
|
||||
|
||||
class MUrsh : public MShiftInstruction
|
||||
@ -3997,6 +4087,8 @@ class MUrsh : public MShiftInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MUrsh)
|
||||
};
|
||||
|
||||
class MBinaryArithInstruction
|
||||
@ -4114,6 +4206,8 @@ class MMinMax
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MMinMax)
|
||||
};
|
||||
|
||||
class MAbs
|
||||
@ -4162,6 +4256,8 @@ class MAbs
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MAbs)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.sqrt().
|
||||
@ -4205,6 +4301,8 @@ class MSqrt
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSqrt)
|
||||
};
|
||||
|
||||
// Inline implementation of atan2 (arctangent of y/x).
|
||||
@ -4253,6 +4351,8 @@ class MAtan2
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MAtan2)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.hypot().
|
||||
@ -4296,6 +4396,8 @@ class MHypot
|
||||
bool possiblyCalls() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MHypot)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.pow().
|
||||
@ -4342,6 +4444,8 @@ class MPow
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MPow)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.pow(x, 0.5), which subtly differs from Math.sqrt(x).
|
||||
@ -4391,6 +4495,8 @@ class MPowHalf
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MPowHalf)
|
||||
};
|
||||
|
||||
// Inline implementation of Math.random().
|
||||
@ -4416,6 +4522,8 @@ class MRandom : public MNullaryInstruction
|
||||
}
|
||||
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MRandom)
|
||||
};
|
||||
|
||||
class MMathFunction
|
||||
@ -4509,6 +4617,8 @@ class MMathFunction
|
||||
bool canRecoverOnBailout() const {
|
||||
return function_ == Round;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MMathFunction)
|
||||
};
|
||||
|
||||
class MAdd : public MBinaryArithInstruction
|
||||
@ -4554,6 +4664,8 @@ class MAdd : public MBinaryArithInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MAdd)
|
||||
};
|
||||
|
||||
class MSub : public MBinaryArithInstruction
|
||||
@ -4595,6 +4707,8 @@ class MSub : public MBinaryArithInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSub)
|
||||
};
|
||||
|
||||
class MMul : public MBinaryArithInstruction
|
||||
@ -4696,6 +4810,8 @@ class MMul : public MBinaryArithInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MMul)
|
||||
};
|
||||
|
||||
class MDiv : public MBinaryArithInstruction
|
||||
@ -4797,6 +4913,8 @@ class MDiv : public MBinaryArithInstruction
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MDiv)
|
||||
};
|
||||
|
||||
class MMod : public MBinaryArithInstruction
|
||||
@ -4871,6 +4989,8 @@ class MMod : public MBinaryArithInstruction
|
||||
bool truncate(TruncateKind kind);
|
||||
void collectRangeInfoPreTrunc();
|
||||
TruncateKind operandTruncateKind(size_t index) const;
|
||||
|
||||
ALLOW_CLONE(MMod)
|
||||
};
|
||||
|
||||
class MConcat
|
||||
@ -4908,6 +5028,7 @@ class MConcat
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MConcat)
|
||||
};
|
||||
|
||||
class MConcatPar
|
||||
@ -4986,6 +5107,8 @@ class MCharCodeAt
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MCharCodeAt)
|
||||
};
|
||||
|
||||
class MFromCharCode
|
||||
@ -5020,6 +5143,8 @@ class MFromCharCode
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MFromCharCode)
|
||||
};
|
||||
|
||||
class MStringSplit
|
||||
@ -5960,6 +6085,8 @@ class MSlots
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSlots)
|
||||
};
|
||||
|
||||
// Returns obj->elements.
|
||||
@ -5993,6 +6120,8 @@ class MElements
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MElements)
|
||||
};
|
||||
|
||||
// A constant value for some object's array elements or typed array elements.
|
||||
@ -6031,6 +6160,8 @@ class MConstantElements : public MNullaryInstruction
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MConstantElements)
|
||||
};
|
||||
|
||||
// Passes through an object's elements, after ensuring it is entirely doubles.
|
||||
@ -6140,6 +6271,8 @@ class MInitializedLength
|
||||
}
|
||||
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MInitializedLength)
|
||||
};
|
||||
|
||||
// Store to the initialized length in an elements header. Note the input is an
|
||||
@ -6168,6 +6301,8 @@ class MSetInitializedLength
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSetInitializedLength)
|
||||
};
|
||||
|
||||
// Load the array length from an elements header.
|
||||
@ -6199,6 +6334,8 @@ class MArrayLength
|
||||
}
|
||||
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MArrayLength)
|
||||
};
|
||||
|
||||
// Store to the length in an elements header. Note the input is an *index*, one
|
||||
@ -6295,6 +6432,8 @@ class MTypedArrayElements
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MTypedArrayElements)
|
||||
};
|
||||
|
||||
// Checks whether a typed object is neutered.
|
||||
@ -6531,6 +6670,8 @@ class MBoundsCheck
|
||||
return AliasSet::None();
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MBoundsCheck)
|
||||
};
|
||||
|
||||
// Bailout if index < minimum.
|
||||
@ -6639,6 +6780,8 @@ class MLoadElement
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::Element);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MLoadElement)
|
||||
};
|
||||
|
||||
// Load a value from a dense array's element vector. If the index is
|
||||
@ -6703,6 +6846,8 @@ class MLoadElementHole
|
||||
return AliasSet::Load(AliasSet::Element);
|
||||
}
|
||||
void collectRangeInfoPreTrunc();
|
||||
|
||||
ALLOW_CLONE(MLoadElementHole)
|
||||
};
|
||||
|
||||
class MStoreElementCommon
|
||||
@ -6785,6 +6930,8 @@ class MStoreElement
|
||||
bool fallible() const {
|
||||
return needsHoleCheck();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreElement)
|
||||
};
|
||||
|
||||
// Like MStoreElement, but supports indexes >= initialized length. The downside
|
||||
@ -6834,6 +6981,8 @@ class MStoreElementHole
|
||||
// or reallocate obj->elements.
|
||||
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreElementHole)
|
||||
};
|
||||
|
||||
// Array.prototype.pop or Array.prototype.shift on a dense array.
|
||||
@ -6884,6 +7033,8 @@ class MArrayPopShift
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MArrayPopShift)
|
||||
};
|
||||
|
||||
// Array.prototype.push on a dense array. Returns the new array length.
|
||||
@ -6917,6 +7068,8 @@ class MArrayPush
|
||||
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MArrayPush)
|
||||
};
|
||||
|
||||
// Array.prototype.concat on two dense arrays.
|
||||
@ -7022,6 +7175,8 @@ class MLoadTypedArrayElement
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
bool canProduceFloat32() const { return arrayType_ == Scalar::Float32; }
|
||||
|
||||
ALLOW_CLONE(MLoadTypedArrayElement)
|
||||
};
|
||||
|
||||
// Load a value from a typed array. Out-of-bounds accesses are handled in-line.
|
||||
@ -7082,6 +7237,8 @@ class MLoadTypedArrayElementHole
|
||||
return AliasSet::Load(AliasSet::TypedArrayElement);
|
||||
}
|
||||
bool canProduceFloat32() const { return arrayType_ == Scalar::Float32; }
|
||||
|
||||
ALLOW_CLONE(MLoadTypedArrayElementHole)
|
||||
};
|
||||
|
||||
// Load a value fallibly or infallibly from a statically known typed array.
|
||||
@ -7207,6 +7364,8 @@ class MStoreTypedArrayElement
|
||||
bool canConsumeFloat32(MUse *use) const {
|
||||
return use == getUseFor(2) && arrayType_ == Scalar::Float32;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreTypedArrayElement)
|
||||
};
|
||||
|
||||
class MStoreTypedArrayElementHole
|
||||
@ -7275,6 +7434,8 @@ class MStoreTypedArrayElementHole
|
||||
bool canConsumeFloat32(MUse *use) const {
|
||||
return use == getUseFor(3) && arrayType_ == Scalar::Float32;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreTypedArrayElementHole)
|
||||
};
|
||||
|
||||
// Store a value infallibly to a statically known typed array.
|
||||
@ -7360,6 +7521,8 @@ class MEffectiveAddress : public MBinaryInstruction
|
||||
int32_t displacement() const {
|
||||
return displacement_;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MEffectiveAddress)
|
||||
};
|
||||
|
||||
// Clamp input to range [0, 255] for Uint8ClampedArray.
|
||||
@ -7393,6 +7556,8 @@ class MClampToUint8
|
||||
return AliasSet::None();
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MClampToUint8)
|
||||
};
|
||||
|
||||
class MLoadFixedSlot
|
||||
@ -7441,6 +7606,8 @@ class MLoadFixedSlot
|
||||
}
|
||||
|
||||
bool mightAlias(const MDefinition *store) const;
|
||||
|
||||
ALLOW_CLONE(MLoadFixedSlot)
|
||||
};
|
||||
|
||||
class MStoreFixedSlot
|
||||
@ -7493,6 +7660,8 @@ class MStoreFixedSlot
|
||||
void setNeedsBarrier(bool needsBarrier = true) {
|
||||
needsBarrier_ = needsBarrier;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreFixedSlot)
|
||||
};
|
||||
|
||||
typedef Vector<JSObject *, 4, IonAllocPolicy> ObjectVector;
|
||||
@ -8284,6 +8453,8 @@ class MGuardClass
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MGuardClass)
|
||||
};
|
||||
|
||||
// Load from vp[slot] (slots that are not inline in an object).
|
||||
@ -8335,6 +8506,8 @@ class MLoadSlot
|
||||
return AliasSet::Load(AliasSet::DynamicSlot);
|
||||
}
|
||||
bool mightAlias(const MDefinition *store) const;
|
||||
|
||||
ALLOW_CLONE(MLoadSlot)
|
||||
};
|
||||
|
||||
// Inline call to access a function's environment (scope chain).
|
||||
@ -8486,6 +8659,8 @@ class MStoreSlot
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::DynamicSlot);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreSlot)
|
||||
};
|
||||
|
||||
class MGetNameCache
|
||||
@ -9159,6 +9334,8 @@ class MStringLength
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStringLength)
|
||||
};
|
||||
|
||||
// Inlined version of Math.floor().
|
||||
@ -9204,6 +9381,8 @@ class MFloor
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MFloor)
|
||||
};
|
||||
|
||||
// Inlined version of Math.ceil().
|
||||
@ -9245,6 +9424,8 @@ class MCeil
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MCeil)
|
||||
};
|
||||
|
||||
// Inlined version of Math.round().
|
||||
@ -9291,6 +9472,8 @@ class MRound
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MRound)
|
||||
};
|
||||
|
||||
class MIteratorStart
|
||||
@ -9893,6 +10076,8 @@ class MTypeBarrier
|
||||
return false;
|
||||
return input()->type() != type;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MTypeBarrier)
|
||||
};
|
||||
|
||||
// Like MTypeBarrier, guard that the value is in the given type set. This is
|
||||
@ -9978,6 +10163,8 @@ class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0>
|
||||
return use == getUseFor(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
ALLOW_CLONE(MPostWriteBarrier)
|
||||
};
|
||||
|
||||
class MNewDeclEnvObject : public MNullaryInstruction
|
||||
@ -10269,6 +10456,9 @@ class MResumePoint MOZ_FINAL : public MNode, public InlineForwardListNode<MResum
|
||||
public:
|
||||
static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc,
|
||||
MResumePoint *parent, Mode mode);
|
||||
static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc,
|
||||
MResumePoint *parent, Mode mode,
|
||||
const MDefinitionVector &operands);
|
||||
|
||||
MNode::Kind kind() const {
|
||||
return MNode::ResumePoint;
|
||||
@ -10913,8 +11103,6 @@ MControlInstruction *MDefinition::toControlInstruction() {
|
||||
return (MControlInstruction *)this;
|
||||
}
|
||||
|
||||
typedef Vector<MDefinition *, 8, IonAllocPolicy> MDefinitionVector;
|
||||
|
||||
// Helper functions used to decide how to build MIR.
|
||||
|
||||
bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id);
|
||||
|
@ -103,6 +103,17 @@ MIRGraph::insertBlockAfter(MBasicBlock *at, MBasicBlock *block)
|
||||
numBlocks_++;
|
||||
}
|
||||
|
||||
void
|
||||
MIRGraph::renumberBlocksAfter(MBasicBlock *at)
|
||||
{
|
||||
MBasicBlockIterator iter = begin(at);
|
||||
iter++;
|
||||
|
||||
uint32_t id = at->id();
|
||||
for (; iter != end(); iter++)
|
||||
iter->setId(++id);
|
||||
}
|
||||
|
||||
void
|
||||
MIRGraph::removeBlocksAfter(MBasicBlock *start)
|
||||
{
|
||||
@ -897,6 +908,15 @@ MBasicBlock::insertAfter(MInstruction *at, MInstruction *ins)
|
||||
ins->setTrackedSite(at->trackedSite());
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::insertAtEnd(MInstruction *ins)
|
||||
{
|
||||
if (hasLastIns())
|
||||
insertBefore(lastIns(), ins);
|
||||
else
|
||||
add(ins);
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::add(MInstruction *ins)
|
||||
{
|
||||
|
@ -167,8 +167,7 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||
MDefinition *pop();
|
||||
void popn(uint32_t n);
|
||||
|
||||
// Adds an instruction to this block's instruction list. |ins| may be
|
||||
// nullptr to simplify OOM checking.
|
||||
// Adds an instruction to this block's instruction list.
|
||||
void add(MInstruction *ins);
|
||||
|
||||
// Marks the last instruction of the block; no further instructions
|
||||
@ -237,6 +236,8 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||
void insertBefore(MInstruction *at, MInstruction *ins);
|
||||
void insertAfter(MInstruction *at, MInstruction *ins);
|
||||
|
||||
void insertAtEnd(MInstruction *ins);
|
||||
|
||||
// Add an instruction to this block, from elsewhere in the graph.
|
||||
void addFromElsewhere(MInstruction *ins);
|
||||
|
||||
@ -311,12 +312,11 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||
}
|
||||
MOZ_CRASH();
|
||||
}
|
||||
#ifdef DEBUG
|
||||
bool hasLastIns() const {
|
||||
return !instructions_.empty() && instructions_.rbegin()->isControlInstruction();
|
||||
}
|
||||
#endif
|
||||
MControlInstruction *lastIns() const {
|
||||
JS_ASSERT(hasLastIns());
|
||||
return instructions_.rbegin()->toControlInstruction();
|
||||
}
|
||||
MPhiIterator phisBegin() const {
|
||||
@ -467,6 +467,9 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||
MResumePoint *entryResumePoint() const {
|
||||
return entryResumePoint_;
|
||||
}
|
||||
void setEntryResumePoint(MResumePoint *rp) {
|
||||
entryResumePoint_ = rp;
|
||||
}
|
||||
void clearEntryResumePoint() {
|
||||
entryResumePoint_ = nullptr;
|
||||
}
|
||||
@ -616,6 +619,8 @@ class MIRGraph
|
||||
void addBlock(MBasicBlock *block);
|
||||
void insertBlockAfter(MBasicBlock *at, MBasicBlock *block);
|
||||
|
||||
void renumberBlocksAfter(MBasicBlock *at);
|
||||
|
||||
void unmarkBlocks();
|
||||
|
||||
void setReturnAccumulator(MIRGraphReturns *accum) {
|
||||
|
@ -1626,11 +1626,14 @@ RangeAnalysis::analyzeLoop(MBasicBlock *header)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!loopIterationBounds.append(iterationBound))
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (IonSpewEnabled(IonSpew_Range)) {
|
||||
Sprinter sp(GetIonContext()->cx);
|
||||
sp.init();
|
||||
iterationBound->sum.print(sp);
|
||||
iterationBound->boundSum.print(sp);
|
||||
IonSpew(IonSpew_Range, "computed symbolic bound on backedges: %s",
|
||||
sp.string());
|
||||
}
|
||||
@ -1748,7 +1751,8 @@ RangeAnalysis::analyzeLoopIterationCount(MBasicBlock *header,
|
||||
if (lhsModified.term != lhs.term)
|
||||
return nullptr;
|
||||
|
||||
LinearSum bound(alloc());
|
||||
LinearSum iterationBound(alloc());
|
||||
LinearSum currentIteration(alloc());
|
||||
|
||||
if (lhsModified.constant == 1 && !lessEqual) {
|
||||
// The value of lhs is 'initial(lhs) + iterCount' and this will end
|
||||
@ -1759,16 +1763,21 @@ RangeAnalysis::analyzeLoopIterationCount(MBasicBlock *header,
|
||||
// iterCount == rhsN - initial(lhs) - lhsN
|
||||
|
||||
if (rhs) {
|
||||
if (!bound.add(rhs, 1))
|
||||
if (!iterationBound.add(rhs, 1))
|
||||
return nullptr;
|
||||
}
|
||||
if (!bound.add(lhsInitial, -1))
|
||||
if (!iterationBound.add(lhsInitial, -1))
|
||||
return nullptr;
|
||||
|
||||
int32_t lhsConstant;
|
||||
if (!SafeSub(0, lhs.constant, &lhsConstant))
|
||||
return nullptr;
|
||||
if (!bound.add(lhsConstant))
|
||||
if (!iterationBound.add(lhsConstant))
|
||||
return nullptr;
|
||||
|
||||
if (!currentIteration.add(lhs.term, 1))
|
||||
return nullptr;
|
||||
if (!currentIteration.add(lhsInitial, -1))
|
||||
return nullptr;
|
||||
} else if (lhsModified.constant == -1 && lessEqual) {
|
||||
// The value of lhs is 'initial(lhs) - iterCount'. Similar to the above
|
||||
@ -1777,19 +1786,24 @@ RangeAnalysis::analyzeLoopIterationCount(MBasicBlock *header,
|
||||
// initial(lhs) - iterCount + lhsN == rhs
|
||||
// iterCount == initial(lhs) - rhs + lhsN
|
||||
|
||||
if (!bound.add(lhsInitial, 1))
|
||||
if (!iterationBound.add(lhsInitial, 1))
|
||||
return nullptr;
|
||||
if (rhs) {
|
||||
if (!bound.add(rhs, -1))
|
||||
if (!iterationBound.add(rhs, -1))
|
||||
return nullptr;
|
||||
}
|
||||
if (!bound.add(lhs.constant))
|
||||
if (!iterationBound.add(lhs.constant))
|
||||
return nullptr;
|
||||
|
||||
if (!currentIteration.add(lhsInitial, 1))
|
||||
return nullptr;
|
||||
if (!currentIteration.add(lhs.term, -1))
|
||||
return nullptr;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new(alloc()) LoopIterationBound(header, test, bound);
|
||||
return new(alloc()) LoopIterationBound(header, test, iterationBound, currentIteration);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1836,7 +1850,7 @@ RangeAnalysis::analyzeLoopPhi(MBasicBlock *header, LoopIterationBound *loopBound
|
||||
// phi is initial(phi) + (loopBound - 1) * N, without requiring us to
|
||||
// ensure that loopBound >= 0.
|
||||
|
||||
LinearSum limitSum(loopBound->sum);
|
||||
LinearSum limitSum(loopBound->boundSum);
|
||||
if (!limitSum.multiply(modified.constant) || !limitSum.add(initialSum))
|
||||
return;
|
||||
|
||||
@ -1876,63 +1890,6 @@ SymbolicBoundIsValid(MBasicBlock *header, MBoundsCheck *ins, const SymbolicBound
|
||||
return bb == bound->loop->test->block();
|
||||
}
|
||||
|
||||
// Convert all components of a linear sum *except* its constant to a definition,
|
||||
// adding any necessary instructions to the end of block.
|
||||
static inline MDefinition *
|
||||
ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum)
|
||||
{
|
||||
MDefinition *def = nullptr;
|
||||
|
||||
for (size_t i = 0; i < sum.numTerms(); i++) {
|
||||
LinearTerm term = sum.term(i);
|
||||
JS_ASSERT(!term.term->isConstant());
|
||||
if (term.scale == 1) {
|
||||
if (def) {
|
||||
def = MAdd::New(alloc, def, term.term);
|
||||
def->toAdd()->setInt32();
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
def = term.term;
|
||||
}
|
||||
} else if (term.scale == -1) {
|
||||
if (!def) {
|
||||
def = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
}
|
||||
def = MSub::New(alloc, def, term.term);
|
||||
def->toSub()->setInt32();
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
JS_ASSERT(term.scale != 0);
|
||||
MConstant *factor = MConstant::New(alloc, Int32Value(term.scale));
|
||||
block->insertBefore(block->lastIns(), factor);
|
||||
MMul *mul = MMul::New(alloc, term.term, factor);
|
||||
mul->setInt32();
|
||||
block->insertBefore(block->lastIns(), mul);
|
||||
mul->computeRange(alloc);
|
||||
if (def) {
|
||||
def = MAdd::New(alloc, def, mul);
|
||||
def->toAdd()->setInt32();
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
} else {
|
||||
def = mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!def) {
|
||||
def = MConstant::New(alloc, Int32Value(0));
|
||||
block->insertBefore(block->lastIns(), def->toInstruction());
|
||||
def->computeRange(alloc);
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
bool
|
||||
RangeAnalysis::tryHoistBoundsCheck(MBasicBlock *header, MBoundsCheck *ins)
|
||||
{
|
||||
|
@ -32,21 +32,30 @@ struct LoopIterationBound : public TempObject
|
||||
// Loop for which this bound applies.
|
||||
MBasicBlock *header;
|
||||
|
||||
// Test from which this bound was derived. Code in the loop body which this
|
||||
// Test from which this bound was derived; after executing exactly 'bound'
|
||||
// times this test will exit the loop. Code in the loop body which this
|
||||
// test dominates (will include the backedge) will execute at most 'bound'
|
||||
// times. Other code in the loop will execute at most '1 + Max(bound, 0)'
|
||||
// times.
|
||||
MTest *test;
|
||||
|
||||
// Symbolic bound computed for the number of backedge executions.
|
||||
LinearSum sum;
|
||||
// Symbolic bound computed for the number of backedge executions. The terms
|
||||
// in this bound are all loop invariant.
|
||||
LinearSum boundSum;
|
||||
|
||||
LoopIterationBound(MBasicBlock *header, MTest *test, LinearSum sum)
|
||||
: header(header), test(test), sum(sum)
|
||||
// Linear sum for the number of iterations already executed, at the start
|
||||
// of the loop header. This will use loop invariant terms and header phis.
|
||||
LinearSum currentSum;
|
||||
|
||||
LoopIterationBound(MBasicBlock *header, MTest *test, LinearSum boundSum, LinearSum currentSum)
|
||||
: header(header), test(test),
|
||||
boundSum(boundSum), currentSum(currentSum)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector<LoopIterationBound *, 0, SystemAllocPolicy> LoopIterationBoundVector;
|
||||
|
||||
// A symbolic upper or lower bound computed for a term.
|
||||
struct SymbolicBound : public TempObject
|
||||
{
|
||||
@ -90,7 +99,7 @@ class RangeAnalysis
|
||||
TempAllocator &alloc() const;
|
||||
|
||||
public:
|
||||
MOZ_CONSTEXPR RangeAnalysis(MIRGenerator *mir, MIRGraph &graph) :
|
||||
RangeAnalysis(MIRGenerator *mir, MIRGraph &graph) :
|
||||
mir(mir), graph_(graph) {}
|
||||
bool addBetaNodes();
|
||||
bool analyze();
|
||||
@ -99,6 +108,9 @@ class RangeAnalysis
|
||||
bool prepareForUCE(bool *shouldRemoveDeadCode);
|
||||
bool truncate();
|
||||
|
||||
// Any iteration bounds discovered for loops in the graph.
|
||||
LoopIterationBoundVector loopIterationBounds;
|
||||
|
||||
private:
|
||||
bool analyzeLoop(MBasicBlock *header);
|
||||
LoopIterationBound *analyzeLoopIterationCount(MBasicBlock *header,
|
||||
|
@ -336,7 +336,7 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("EnterJIT");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "EnterJIT");
|
||||
@ -406,7 +406,7 @@ JitRuntime::generateInvalidator(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("Invalidator");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw());
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
@ -509,7 +509,7 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
masm.ret();
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("ArgumentsRectifier");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
CodeOffsetLabel returnLabel(returnOffset);
|
||||
returnLabel.fixup(&masm);
|
||||
@ -684,7 +684,7 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutTable");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutTable");
|
||||
@ -711,7 +711,7 @@ JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutHandler");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutHandler");
|
||||
@ -884,7 +884,7 @@ JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("VMWrapper");
|
||||
JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *wrapper = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
if (!wrapper)
|
||||
return nullptr;
|
||||
|
||||
@ -933,7 +933,7 @@ JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("PreBarrier");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "PreBarrier");
|
||||
@ -989,7 +989,7 @@ JitRuntime::generateDebugTrapHandler(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("DebugTrapHandler");
|
||||
JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *codeDbg = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
|
||||
@ -1007,7 +1007,7 @@ JitRuntime::generateExceptionTailStub(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("ExceptionTailStub");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
|
||||
@ -1025,7 +1025,7 @@ JitRuntime::generateBailoutTailStub(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutTailStub");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
|
||||
|
@ -306,7 +306,7 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("GenerateEnterJIT");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "EnterJIT");
|
||||
@ -379,7 +379,7 @@ JitRuntime::generateInvalidator(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("Invalidator");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw());
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
@ -511,7 +511,7 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
masm.ret();
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("ArgumentsRectifier");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
CodeOffsetLabel returnLabel(returnOffset);
|
||||
returnLabel.fixup(&masm);
|
||||
@ -668,7 +668,7 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutTable");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutTable");
|
||||
@ -695,7 +695,7 @@ JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutHandler");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutHandler");
|
||||
@ -894,7 +894,7 @@ JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("VMWrapper");
|
||||
JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *wrapper = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
if (!wrapper)
|
||||
return nullptr;
|
||||
|
||||
@ -944,7 +944,7 @@ JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("PreBarrier");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "PreBarrier");
|
||||
@ -1004,7 +1004,7 @@ JitRuntime::generateDebugTrapHandler(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("DebugTrapHandler");
|
||||
JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *codeDbg = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
|
||||
@ -1023,7 +1023,7 @@ JitRuntime::generateExceptionTailStub(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("ExceptionTailStub");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
|
||||
@ -1041,7 +1041,7 @@ JitRuntime::generateBailoutTailStub(JSContext *cx)
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutTailStub");
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
|
||||
|
@ -2216,7 +2216,7 @@ JitRuntime::generateForkJoinGetSliceStub(JSContext *cx)
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "ForkJoinGetSliceStub");
|
||||
|
@ -300,7 +300,7 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type)
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "EnterJIT");
|
||||
@ -349,7 +349,7 @@ JitRuntime::generateInvalidator(JSContext *cx)
|
||||
masm.jmp(bailoutTail);
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "Invalidator");
|
||||
@ -433,7 +433,7 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
|
||||
@ -539,7 +539,7 @@ JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
}
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutHandler");
|
||||
@ -716,7 +716,7 @@ JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
f.extraValuesToPop * sizeof(Value)));
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *wrapper = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
if (!wrapper)
|
||||
return nullptr;
|
||||
|
||||
@ -758,7 +758,7 @@ JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "PreBarrier");
|
||||
@ -817,7 +817,7 @@ JitRuntime::generateDebugTrapHandler(JSContext *cx)
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *codeDbg = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
|
||||
@ -834,7 +834,7 @@ JitRuntime::generateExceptionTailStub(JSContext *cx)
|
||||
masm.handleFailureWithHandlerTail();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
|
||||
@ -851,7 +851,7 @@ JitRuntime::generateBailoutTailStub(JSContext *cx)
|
||||
masm.generateBailoutTail(rdx, r9);
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
|
||||
|
@ -288,7 +288,7 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type)
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "EnterJIT");
|
||||
@ -345,7 +345,7 @@ JitRuntime::generateInvalidator(JSContext *cx)
|
||||
masm.jmp(bailoutTail);
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw());
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
@ -442,7 +442,7 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
|
||||
@ -553,7 +553,7 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
GenerateBailoutThunk(cx, masm, frameClass);
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutHandler");
|
||||
@ -579,7 +579,7 @@ JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
}
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutHandler");
|
||||
@ -751,7 +751,7 @@ JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
f.extraValuesToPop * sizeof(Value)));
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *wrapper = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
if (!wrapper)
|
||||
return nullptr;
|
||||
|
||||
@ -800,7 +800,7 @@ JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "PreBarrier");
|
||||
@ -859,7 +859,7 @@ JitRuntime::generateDebugTrapHandler(JSContext *cx)
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *codeDbg = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
|
||||
@ -876,7 +876,7 @@ JitRuntime::generateExceptionTailStub(JSContext *cx)
|
||||
masm.handleFailureWithHandlerTail();
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
|
||||
@ -893,7 +893,7 @@ JitRuntime::generateBailoutTailStub(JSContext *cx)
|
||||
masm.generateBailoutTail(edx, ecx);
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
|
||||
|
@ -195,7 +195,7 @@ MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 141, 0, JSEXN_SYNTAXERR, "unterminated reg
|
||||
MSG_DEF(JSMSG_BAD_CLONE_FUNOBJ_SCOPE, 142, 0, JSEXN_TYPEERR, "bad cloned function scope chain")
|
||||
MSG_DEF(JSMSG_MISSING_OCTAL_DIGITS, 143, 0, JSEXN_SYNTAXERR, "missing octal digits after '0o'")
|
||||
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 144, 0, JSEXN_SYNTAXERR, "illegal character")
|
||||
MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "numbers starting with 0 followed by a digit are octals and can't contain {0}")
|
||||
MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
|
||||
MSG_DEF(JSMSG_RESULTING_STRING_TOO_LARGE, 146, 0, JSEXN_RANGEERR, "repeat count must be less than infinity and not overflow maximum string size")
|
||||
MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_INTERNALERR, "uncaught exception: {0}")
|
||||
MSG_DEF(JSMSG_INVALID_BACKREF, 148, 0, JSEXN_SYNTAXERR, "non-octal digit in an escape sequence that doesn't match a back-reference")
|
||||
|
@ -4430,7 +4430,7 @@ GCRuntime::endSweepPhase(JSGCInvocationKind gckind, bool lastGC)
|
||||
SweepScriptData(rt);
|
||||
|
||||
/* Clear out any small pools that we're hanging on to. */
|
||||
if (JSC::ExecutableAllocator *execAlloc = rt->maybeExecAlloc())
|
||||
if (jit::ExecutableAllocator *execAlloc = rt->maybeExecAlloc())
|
||||
execAlloc->purge();
|
||||
|
||||
if (rt->jitRuntime() && rt->jitRuntime()->hasIonAlloc()) {
|
||||
|
@ -224,8 +224,6 @@ js::DumpIonScriptCounts(Sprinter *sp, jit::IonScriptCounts *ionCounts)
|
||||
Sprint(sp, "IonScript [%lu blocks]:\n", ionCounts->numBlocks());
|
||||
for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
|
||||
const jit::IonBlockCounts &block = ionCounts->block(i);
|
||||
if (block.hitCount() < 10)
|
||||
continue;
|
||||
Sprint(sp, "BB #%lu [%05u]", block.id(), block.offset());
|
||||
for (size_t j = 0; j < block.numSuccessors(); j++)
|
||||
Sprint(sp, " -> #%lu", block.successor(j));
|
||||
|
@ -173,6 +173,7 @@ UNIFIED_SOURCES += [
|
||||
'jit/LinearScan.cpp',
|
||||
'jit/LIR.cpp',
|
||||
'jit/LiveRangeAllocator.cpp',
|
||||
'jit/LoopUnroller.cpp',
|
||||
'jit/Lowering.cpp',
|
||||
'jit/MCallOptimize.cpp',
|
||||
'jit/MIR.cpp',
|
||||
|
@ -5787,14 +5787,23 @@ SetRuntimeOptions(JSRuntime *rt, const OptionParser &op)
|
||||
return OptionFailure("ion-edgecase-analysis", str);
|
||||
}
|
||||
|
||||
if (const char *str = op.getStringOption("ion-range-analysis")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::js_JitOptions.disableRangeAnalysis = false;
|
||||
else if (strcmp(str, "off") == 0)
|
||||
jit::js_JitOptions.disableRangeAnalysis = true;
|
||||
else
|
||||
return OptionFailure("ion-range-analysis", str);
|
||||
}
|
||||
if (const char *str = op.getStringOption("ion-range-analysis")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::js_JitOptions.disableRangeAnalysis = false;
|
||||
else if (strcmp(str, "off") == 0)
|
||||
jit::js_JitOptions.disableRangeAnalysis = true;
|
||||
else
|
||||
return OptionFailure("ion-range-analysis", str);
|
||||
}
|
||||
|
||||
if (const char *str = op.getStringOption("ion-loop-unrolling")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::js_JitOptions.disableLoopUnrolling = false;
|
||||
else if (strcmp(str, "off") == 0)
|
||||
jit::js_JitOptions.disableLoopUnrolling = true;
|
||||
else
|
||||
return OptionFailure("ion-loop-unrolling", str);
|
||||
}
|
||||
|
||||
if (op.getBoolOption("ion-check-range-analysis"))
|
||||
jit::js_JitOptions.checkRangeAnalysis = true;
|
||||
@ -6059,6 +6068,8 @@ main(int argc, char **argv, char **envp)
|
||||
"Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
|
||||
|| !op.addStringOption('\0', "ion-range-analysis", "on/off",
|
||||
"Range analysis (default: on, off to disable)")
|
||||
|| !op.addStringOption('\0', "ion-loop-unrolling", "on/off",
|
||||
"Loop unrolling (default: off, on to enable)")
|
||||
|| !op.addBoolOption('\0', "ion-check-range-analysis",
|
||||
"Range analysis checking")
|
||||
|| !op.addStringOption('\0', "ion-inlining", "on/off",
|
||||
|
59
js/src/tests/ecma/LexicalConventions/7.7.3-2.js
Normal file
59
js/src/tests/ecma/LexicalConventions/7.7.3-2.js
Normal file
@ -0,0 +1,59 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: 7.7.3-2.js
|
||||
ECMA Section: 7.7.3 Numeric Literals
|
||||
|
||||
Description:
|
||||
|
||||
This is a regression test for
|
||||
http://scopus.mcom.com/bugsplat/show_bug.cgi?id=122884
|
||||
|
||||
Waldemar's comments:
|
||||
|
||||
A numeric literal that starts with either '08' or '09' is interpreted as a
|
||||
decimal literal; it should be an error instead. (Strictly speaking, according
|
||||
to ECMA v1 such literals should be interpreted as two integers -- a zero
|
||||
followed by a decimal number whose first digit is 8 or 9, but this is a bug in
|
||||
ECMA that will be fixed in v2. In any case, there is no place in the grammar
|
||||
where two consecutive numbers would be legal.)
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 15 june 1998
|
||||
|
||||
*/
|
||||
var SECTION = "7.7.3-2";
|
||||
var VERSION = "ECMA_1";
|
||||
var TITLE = "Numeric Literals";
|
||||
var BUGNUMBER="122884";
|
||||
|
||||
startTest();
|
||||
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
new TestCase( SECTION,
|
||||
"9",
|
||||
9,
|
||||
9 );
|
||||
|
||||
new TestCase( SECTION,
|
||||
"09",
|
||||
9,
|
||||
09 );
|
||||
|
||||
new TestCase( SECTION,
|
||||
"099",
|
||||
99,
|
||||
099 );
|
||||
|
||||
|
||||
new TestCase( SECTION,
|
||||
"077",
|
||||
63,
|
||||
077 );
|
||||
|
||||
test();
|
@ -3,4 +3,4 @@
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
load("ecma_5/Object/defineProperty-setup.js");
|
||||
runDictionaryPropertyPresentTestsFraction(3, 8);
|
||||
runDictionaryPropertyPresentTestsFraction(1, 32);
|
@ -3,4 +3,4 @@
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
load("ecma_5/Object/defineProperty-setup.js");
|
||||
runDictionaryPropertyPresentTestsFraction(4, 8);
|
||||
runDictionaryPropertyPresentTestsFraction(2, 32);
|
@ -3,4 +3,4 @@
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
load("ecma_5/Object/defineProperty-setup.js");
|
||||
runDictionaryPropertyPresentTestsFraction(1, 8);
|
||||
runDictionaryPropertyPresentTestsFraction(3, 32);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user