Merge m-c to b-i, a=merge

This commit is contained in:
Nigel Babu 2014-08-11 12:48:04 +05:30
commit 642c6b1a30
171 changed files with 6247 additions and 4100 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -51,6 +51,9 @@ public:
void DefaultCheckOverflowing() { CheckOverflowing(mResizeImageByDefault); }
void AddDecodedClass();
void RemoveDecodedClass();
// WebIDL API
virtual JSObject* WrapNode(JSContext* aCx)
MOZ_OVERRIDE;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

@ -22,6 +22,7 @@ UNIFIED_SOURCES += [
'SourceBuffer.cpp',
'SourceBufferList.cpp',
'SourceBufferResource.cpp',
'SubBufferDecoder.cpp',
]
FAIL_ON_WARNINGS = True

View File

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

View File

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

View 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

View 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

View File

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

View File

@ -136,11 +136,6 @@ DOMInterfaces = {
'resultNotAddRefed': [ 'inputBuffer', 'outputBuffer' ],
},
'AnimationPlayer' : {
'nativeType': 'mozilla::ElementAnimation',
'headerFile': 'AnimationCommon.h',
},
'BarProp': {
'headerFile': 'mozilla/dom/BarProps.h',
},

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

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

View File

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

View File

@ -21,6 +21,7 @@ WEBIDL_FILES = [
'AlarmsManager.webidl',
'AnalyserNode.webidl',
'Animatable.webidl',
'Animation.webidl',
'AnimationEvent.webidl',
'AnimationPlayer.webidl',
'AnimationTimeline.webidl',

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,6 +32,7 @@ OptimizationInfo::initNormalOptimizationInfo()
licm_ = true;
uce_ = true;
rangeAnalysis_ = true;
loopUnrolling_ = true;
autoTruncate_ = true;
registerAllocator_ = RegisterAllocator_LSRA;

View File

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

View File

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

View File

@ -34,6 +34,8 @@ namespace jit {
_(GVN) \
/* Information during Range analysis */ \
_(Range) \
/* Information during loop unrolling */ \
_(Unrolling) \
/* Information during LICM */ \
_(LICM) \
/* Information during regalloc */ \

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -3,4 +3,4 @@
// http://creativecommons.org/licenses/publicdomain/
load("ecma_5/Object/defineProperty-setup.js");
runDictionaryPropertyPresentTestsFraction(3, 8);
runDictionaryPropertyPresentTestsFraction(1, 32);

View File

@ -3,4 +3,4 @@
// http://creativecommons.org/licenses/publicdomain/
load("ecma_5/Object/defineProperty-setup.js");
runDictionaryPropertyPresentTestsFraction(4, 8);
runDictionaryPropertyPresentTestsFraction(2, 32);

View File

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