зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c. a=merge
This commit is contained in:
Коммит
35369d17c7
|
@ -428,7 +428,7 @@ class CGDOMJSClass(CGThing):
|
|||
""",
|
||||
objectMoved=objectMovedHook)
|
||||
if self.descriptor.isGlobal():
|
||||
classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
|
||||
classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
|
||||
traceHook = "JS_GlobalObjectTraceHook"
|
||||
reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
|
||||
if self.descriptor.interface.identifier.name == "Window":
|
||||
|
|
|
@ -1204,6 +1204,9 @@ bool CanvasRenderingContext2D::SwitchRenderingMode(RenderingMode aRenderingMode)
|
|||
transform = mTarget->GetTransform();
|
||||
} else {
|
||||
MOZ_ASSERT(mBufferProvider);
|
||||
// When mBufferProvider is true but we have no mTarget, our current state's
|
||||
// transform is always valid. See ReturnTarget().
|
||||
transform = CurrentState().transform;
|
||||
snapshot = mBufferProvider->GetSnapshot();
|
||||
}
|
||||
mTarget = nullptr;
|
||||
|
|
|
@ -471,7 +471,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcMediaSource)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcStream)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaybackStream)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcAttrStream)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourcePointer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadBlockedDoc)
|
||||
|
@ -623,8 +622,8 @@ NS_IMETHODIMP HTMLMediaElement::GetError(nsIDOMMediaError * *aError)
|
|||
bool
|
||||
HTMLMediaElement::Ended()
|
||||
{
|
||||
if (mSrcStream) {
|
||||
return GetSrcMediaStream()->IsFinished();
|
||||
if (MediaStream* stream = GetSrcMediaStream()) {
|
||||
return stream->IsFinished();
|
||||
}
|
||||
|
||||
if (mDecoder) {
|
||||
|
@ -1391,11 +1390,11 @@ NS_IMETHODIMP HTMLMediaElement::GetSeeking(bool* aSeeking)
|
|||
double
|
||||
HTMLMediaElement::CurrentTime() const
|
||||
{
|
||||
if (mSrcStream) {
|
||||
MediaStream* stream = GetSrcMediaStream();
|
||||
if (stream) {
|
||||
return stream->StreamTimeToSeconds(stream->GetCurrentTime());
|
||||
if (MediaStream* stream = GetSrcMediaStream()) {
|
||||
if (mSrcStreamPausedCurrentTime >= 0) {
|
||||
return mSrcStreamPausedCurrentTime;
|
||||
}
|
||||
return stream->StreamTimeToSeconds(stream->GetCurrentTime());
|
||||
}
|
||||
|
||||
if (mDecoder) {
|
||||
|
@ -1703,14 +1702,9 @@ HTMLMediaElement::Pause(ErrorResult& aRv)
|
|||
mAutoplaying = false;
|
||||
// We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
|
||||
AddRemoveSelfReference();
|
||||
UpdateSrcMediaStreamPlaying();
|
||||
|
||||
if (!oldPaused) {
|
||||
if (mSrcStream) {
|
||||
MediaStream* stream = GetSrcMediaStream();
|
||||
if (stream) {
|
||||
stream->ChangeExplicitBlockerCount(1);
|
||||
}
|
||||
}
|
||||
FireTimeUpdate(false);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
|
||||
}
|
||||
|
@ -1825,8 +1819,10 @@ void HTMLMediaElement::SetVolumeInternal()
|
|||
|
||||
if (mDecoder) {
|
||||
mDecoder->SetVolume(effectiveVolume);
|
||||
} else if (mSrcStream) {
|
||||
GetSrcMediaStream()->SetAudioOutputVolume(this, effectiveVolume);
|
||||
} else if (MediaStream* stream = GetSrcMediaStream()) {
|
||||
if (mSrcStreamIsPlaying) {
|
||||
stream->SetAudioOutputVolume(this, effectiveVolume);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAudioChannelPlayingState();
|
||||
|
@ -2038,6 +2034,7 @@ HTMLMediaElement::LookupMediaElementURITable(nsIURI* aURI)
|
|||
HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo),
|
||||
mWatchManager(this, AbstractThread::MainThread()),
|
||||
mSrcStreamPausedCurrentTime(-1),
|
||||
mCurrentLoadID(0),
|
||||
mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
|
||||
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING, "HTMLMediaElement::mReadyState"),
|
||||
|
@ -2080,6 +2077,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||
mHasSelfReference(false),
|
||||
mShuttingDown(false),
|
||||
mSuspendedForPreloadNone(false),
|
||||
mSrcStreamIsPlaying(false),
|
||||
mMediaSecurityVerified(false),
|
||||
mCORSMode(CORS_NONE),
|
||||
mIsEncrypted(false),
|
||||
|
@ -2255,9 +2253,6 @@ HTMLMediaElement::Play(ErrorResult& aRv)
|
|||
// TODO: If the playback has ended, then the user agent must set
|
||||
// seek to the effective start.
|
||||
if (mPaused) {
|
||||
if (mSrcStream) {
|
||||
GetSrcMediaStream()->ChangeExplicitBlockerCount(-1);
|
||||
}
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("play"));
|
||||
switch (mReadyState) {
|
||||
case nsIDOMHTMLMediaElement::HAVE_NOTHING:
|
||||
|
@ -2281,6 +2276,7 @@ HTMLMediaElement::Play(ErrorResult& aRv)
|
|||
// and our preload status.
|
||||
AddRemoveSelfReference();
|
||||
UpdatePreloadAction();
|
||||
UpdateSrcMediaStreamPlaying();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP HTMLMediaElement::Play()
|
||||
|
@ -2889,6 +2885,7 @@ public:
|
|||
mElement(aElement),
|
||||
mHaveCurrentData(false),
|
||||
mBlocked(false),
|
||||
mFinished(false),
|
||||
mMutex(aName),
|
||||
mPendingNotifyOutput(false)
|
||||
{}
|
||||
|
@ -2897,23 +2894,29 @@ public:
|
|||
// Main thread
|
||||
void DoNotifyFinished()
|
||||
{
|
||||
mFinished = true;
|
||||
if (mElement) {
|
||||
nsRefPtr<HTMLMediaElement> deathGrip = mElement;
|
||||
mElement->PlaybackEnded();
|
||||
|
||||
// Update NextFrameStatus() to move to NEXT_FRAME_UNAVAILABLE and
|
||||
// HAVE_CURRENT_DATA.
|
||||
mElement = nullptr;
|
||||
// NotifyWatchers before calling PlaybackEnded since PlaybackEnded
|
||||
// can remove watchers.
|
||||
NotifyWatchers();
|
||||
|
||||
deathGrip->PlaybackEnded();
|
||||
}
|
||||
}
|
||||
|
||||
MediaDecoderOwner::NextFrameStatus NextFrameStatus()
|
||||
{
|
||||
if (!mElement || !mHaveCurrentData) {
|
||||
if (!mElement || !mHaveCurrentData || mFinished) {
|
||||
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
|
||||
}
|
||||
return mBlocked ? MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING
|
||||
: MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
|
||||
return mBlocked
|
||||
? MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING
|
||||
: MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
|
||||
}
|
||||
|
||||
void DoNotifyBlocked()
|
||||
|
@ -2993,6 +2996,7 @@ private:
|
|||
HTMLMediaElement* mElement;
|
||||
bool mHaveCurrentData;
|
||||
bool mBlocked;
|
||||
bool mFinished;
|
||||
|
||||
// mMutex protects the fields below; they can be accessed on any thread
|
||||
Mutex mMutex;
|
||||
|
@ -3072,6 +3076,77 @@ private:
|
|||
HTMLMediaElement* mElement;
|
||||
};
|
||||
|
||||
void HTMLMediaElement::UpdateSrcMediaStreamPlaying(uint32_t aFlags)
|
||||
{
|
||||
if (!mSrcStream) {
|
||||
return;
|
||||
}
|
||||
// We might be in cycle collection with mSrcStream->GetStream() already
|
||||
// returning null due to unlinking.
|
||||
|
||||
MediaStream* stream = mSrcStream->GetStream();
|
||||
bool shouldPlay = !(aFlags & REMOVING_SRC_STREAM) && !mPaused &&
|
||||
!mPausedForInactiveDocumentOrChannel && stream;
|
||||
if (shouldPlay == mSrcStreamIsPlaying) {
|
||||
return;
|
||||
}
|
||||
mSrcStreamIsPlaying = shouldPlay;
|
||||
|
||||
if (shouldPlay) {
|
||||
mSrcStreamPausedCurrentTime = -1;
|
||||
|
||||
mMediaStreamListener = new StreamListener(this,
|
||||
"HTMLMediaElement::mMediaStreamListener");
|
||||
mMediaStreamSizeListener = new StreamSizeListener(this);
|
||||
stream->AddListener(mMediaStreamListener);
|
||||
stream->AddListener(mMediaStreamSizeListener);
|
||||
|
||||
mWatchManager.Watch(*mMediaStreamListener,
|
||||
&HTMLMediaElement::UpdateReadyStateInternal);
|
||||
|
||||
stream->AddAudioOutput(this);
|
||||
SetVolumeInternal();
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
bool bUseOverlayImage = mSrcStream->AsDOMHwMediaStream() != nullptr;
|
||||
#else
|
||||
bool bUseOverlayImage = false;
|
||||
#endif
|
||||
VideoFrameContainer* container;
|
||||
if (bUseOverlayImage) {
|
||||
container = GetOverlayImageVideoFrameContainer();
|
||||
} else {
|
||||
container = GetVideoFrameContainer();
|
||||
}
|
||||
if (container) {
|
||||
stream->AddVideoOutput(container);
|
||||
}
|
||||
} else {
|
||||
if (stream) {
|
||||
mSrcStreamPausedCurrentTime = CurrentTime();
|
||||
|
||||
stream->RemoveListener(mMediaStreamListener);
|
||||
stream->RemoveListener(mMediaStreamSizeListener);
|
||||
|
||||
stream->RemoveAudioOutput(this);
|
||||
VideoFrameContainer* container = GetVideoFrameContainer();
|
||||
if (container) {
|
||||
stream->RemoveVideoOutput(container);
|
||||
}
|
||||
}
|
||||
// If stream is null, then DOMMediaStream::Destroy must have been
|
||||
// called and that will remove all listeners/outputs.
|
||||
|
||||
mWatchManager.Unwatch(*mMediaStreamListener,
|
||||
&HTMLMediaElement::UpdateReadyStateInternal);
|
||||
|
||||
mMediaStreamListener->Forget();
|
||||
mMediaStreamListener = nullptr;
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
|
||||
{
|
||||
NS_ASSERTION(!mSrcStream && !mMediaStreamListener && !mMediaStreamSizeListener,
|
||||
|
@ -3084,121 +3159,38 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
|
|||
return;
|
||||
}
|
||||
|
||||
// XXX Remove this if with CameraPreviewMediaStream per bug 1124630.
|
||||
if (!mSrcStream->GetStream()->AsCameraPreviewStream()) {
|
||||
// Now that we have access to |mSrcStream| we can pipe it to our shadow
|
||||
// version |mPlaybackStream|. If two media elements are playing the
|
||||
// same realtime DOMMediaStream, this allows them to pause playback
|
||||
// independently of each other.
|
||||
MediaStreamGraph* graph = mSrcStream->GetStream()->Graph();
|
||||
mPlaybackStream = DOMMediaStream::CreateTrackUnionStream(window, graph);
|
||||
mPlaybackStreamInputPort = mPlaybackStream->GetStream()->AsProcessedStream()->
|
||||
AllocateInputPort(mSrcStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
|
||||
|
||||
nsRefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
|
||||
mPlaybackStream->CombineWithPrincipal(principal);
|
||||
|
||||
// Let |mSrcStream| decide when the stream has finished.
|
||||
GetSrcMediaStream()->AsProcessedStream()->SetAutofinish(true);
|
||||
}
|
||||
|
||||
nsRefPtr<MediaStream> stream = mSrcStream->GetStream();
|
||||
if (stream) {
|
||||
stream->SetAudioChannelType(mAudioChannel);
|
||||
}
|
||||
|
||||
// XXX if we ever support capturing the output of a media element which is
|
||||
// playing a stream, we'll need to add a CombineWithPrincipal call here.
|
||||
mMediaStreamListener = new StreamListener(this, "HTMLMediaElement::mMediaStreamListener");
|
||||
mMediaStreamSizeListener = new StreamSizeListener(this);
|
||||
mWatchManager.Watch(*mMediaStreamListener, &HTMLMediaElement::UpdateReadyStateInternal);
|
||||
|
||||
GetSrcMediaStream()->AddListener(mMediaStreamListener);
|
||||
// Listen for an initial image size on mSrcStream so we can get results even
|
||||
// if we block the mPlaybackStream.
|
||||
stream->AddListener(mMediaStreamSizeListener);
|
||||
if (mPaused) {
|
||||
GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
|
||||
}
|
||||
if (mPausedForInactiveDocumentOrChannel) {
|
||||
GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
|
||||
}
|
||||
|
||||
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
|
||||
|
||||
ChangeDelayLoadStatus(false);
|
||||
GetSrcMediaStream()->AddAudioOutput(this);
|
||||
SetVolumeInternal();
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
bool bUseOverlayImage = mSrcStream->AsDOMHwMediaStream() != nullptr;
|
||||
#else
|
||||
bool bUseOverlayImage = false;
|
||||
#endif
|
||||
VideoFrameContainer* container;
|
||||
|
||||
if (bUseOverlayImage) {
|
||||
container = GetOverlayImageVideoFrameContainer();
|
||||
}
|
||||
else {
|
||||
container = GetVideoFrameContainer();
|
||||
}
|
||||
|
||||
if (container) {
|
||||
GetSrcMediaStream()->AddVideoOutput(container);
|
||||
}
|
||||
|
||||
CheckAutoplayDataReady();
|
||||
UpdateSrcMediaStreamPlaying();
|
||||
|
||||
// Note: we must call DisconnectTrackListListeners(...) before dropping
|
||||
// mSrcStream
|
||||
// mSrcStream.
|
||||
// If we pause this media element, track changes in the underlying stream
|
||||
// will continue to fire events at this element and alter its track list.
|
||||
// That's simpler than delaying the events, but probably confusing...
|
||||
mSrcStream->ConstructMediaTracks(AudioTracks(), VideoTracks());
|
||||
|
||||
mSrcStream->OnTracksAvailable(new MediaStreamTracksAvailableCallback(this));
|
||||
|
||||
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
|
||||
ChangeDelayLoadStatus(false);
|
||||
CheckAutoplayDataReady();
|
||||
|
||||
// FirstFrameLoaded() will be called when the stream has current data.
|
||||
}
|
||||
|
||||
void HTMLMediaElement::EndSrcMediaStreamPlayback()
|
||||
{
|
||||
MediaStream* stream = GetSrcMediaStream();
|
||||
if (stream) {
|
||||
stream->RemoveListener(mMediaStreamListener);
|
||||
}
|
||||
if (mSrcStream->GetStream()) {
|
||||
mSrcStream->GetStream()->RemoveListener(mMediaStreamSizeListener);
|
||||
}
|
||||
MOZ_ASSERT(mSrcStream);
|
||||
|
||||
UpdateSrcMediaStreamPlaying(REMOVING_SRC_STREAM);
|
||||
|
||||
mSrcStream->DisconnectTrackListListeners(AudioTracks(), VideoTracks());
|
||||
|
||||
if (mPlaybackStreamInputPort) {
|
||||
mPlaybackStreamInputPort->Destroy();
|
||||
}
|
||||
|
||||
// Kill its reference to this element
|
||||
mWatchManager.Unwatch(*mMediaStreamListener, &HTMLMediaElement::UpdateReadyStateInternal);
|
||||
mMediaStreamListener->Forget();
|
||||
mMediaStreamListener = nullptr;
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
if (stream) {
|
||||
stream->RemoveAudioOutput(this);
|
||||
}
|
||||
VideoFrameContainer* container = GetVideoFrameContainer();
|
||||
if (container) {
|
||||
if (stream) {
|
||||
stream->RemoveVideoOutput(container);
|
||||
}
|
||||
container->ClearCurrentFrame();
|
||||
}
|
||||
if (mPaused && stream) {
|
||||
stream->ChangeExplicitBlockerCount(-1);
|
||||
}
|
||||
if (mPausedForInactiveDocumentOrChannel && stream) {
|
||||
stream->ChangeExplicitBlockerCount(-1);
|
||||
}
|
||||
mSrcStream = nullptr;
|
||||
mPlaybackStreamInputPort = nullptr;
|
||||
mPlaybackStream = nullptr;
|
||||
}
|
||||
|
||||
void HTMLMediaElement::ProcessMediaFragmentURI()
|
||||
|
@ -3791,6 +3783,7 @@ void HTMLMediaElement::CheckAutoplayDataReady()
|
|||
mPaused = false;
|
||||
// We changed mPaused which can affect AddRemoveSelfReference
|
||||
AddRemoveSelfReference();
|
||||
UpdateSrcMediaStreamPlaying();
|
||||
|
||||
if (mDecoder) {
|
||||
SetPlayedOrSeeked(true);
|
||||
|
@ -3800,7 +3793,6 @@ void HTMLMediaElement::CheckAutoplayDataReady()
|
|||
mDecoder->Play();
|
||||
} else if (mSrcStream) {
|
||||
SetPlayedOrSeeked(true);
|
||||
GetSrcMediaStream()->ChangeExplicitBlockerCount(-1);
|
||||
}
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("play"));
|
||||
|
||||
|
@ -3990,6 +3982,7 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendE
|
|||
|
||||
if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
|
||||
mPausedForInactiveDocumentOrChannel = aPauseElement;
|
||||
UpdateSrcMediaStreamPlaying();
|
||||
if (aPauseElement) {
|
||||
if (mMediaSource) {
|
||||
ReportMSETelemetry();
|
||||
|
@ -4015,8 +4008,6 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendE
|
|||
if (mDecoder) {
|
||||
mDecoder->Pause();
|
||||
mDecoder->Suspend();
|
||||
} else if (mSrcStream) {
|
||||
GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
|
||||
}
|
||||
mEventDeliveryPaused = aSuspendEvents;
|
||||
} else {
|
||||
|
@ -4028,8 +4019,6 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendE
|
|||
if (!mPaused && !mDecoder->IsEndedOrShutdown()) {
|
||||
mDecoder->Play();
|
||||
}
|
||||
} else if (mSrcStream) {
|
||||
GetSrcMediaStream()->ChangeExplicitBlockerCount(-1);
|
||||
}
|
||||
if (mEventDeliveryPaused) {
|
||||
mEventDeliveryPaused = false;
|
||||
|
@ -4770,11 +4759,11 @@ NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged()
|
|||
MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
|
||||
AudioChannel::Normal);
|
||||
|
||||
if (!mPlaybackStream) {
|
||||
if (mSrcStream) {
|
||||
mCaptureStreamPort = msg->ConnectToCaptureStream(id, mSrcStream->GetStream());
|
||||
} else {
|
||||
nsRefPtr<DOMMediaStream> stream = CaptureStreamInternal(false, msg);
|
||||
mCaptureStreamPort = msg->ConnectToCaptureStream(id, stream->GetStream());
|
||||
} else {
|
||||
mCaptureStreamPort = msg->ConnectToCaptureStream(id, mPlaybackStream->GetStream());
|
||||
}
|
||||
} else {
|
||||
mAudioCapturedByWindow = false;
|
||||
|
|
|
@ -347,14 +347,14 @@ public:
|
|||
*/
|
||||
virtual void FireTimeUpdate(bool aPeriodic) final override;
|
||||
|
||||
/**
|
||||
* This will return null if mSrcStream is null, or if mSrcStream is not
|
||||
* null but its GetStream() returns null --- which can happen during
|
||||
* cycle collection unlinking!
|
||||
*/
|
||||
MediaStream* GetSrcMediaStream() const
|
||||
{
|
||||
NS_ASSERTION(mSrcStream, "Don't call this when not playing a stream");
|
||||
if (!mPlaybackStream) {
|
||||
// XXX Remove this check with CameraPreviewMediaStream per bug 1124630.
|
||||
return mSrcStream->GetStream();
|
||||
}
|
||||
return mPlaybackStream->GetStream();
|
||||
return mSrcStream ? mSrcStream->GetStream() : nullptr;
|
||||
}
|
||||
|
||||
// WebIDL
|
||||
|
@ -747,6 +747,11 @@ protected:
|
|||
* Stop playback on mSrcStream.
|
||||
*/
|
||||
void EndSrcMediaStreamPlayback();
|
||||
/**
|
||||
* Ensure we're playing mSrcStream if and only if we're not paused.
|
||||
*/
|
||||
enum { REMOVING_SRC_STREAM = 0x1 };
|
||||
void UpdateSrcMediaStreamPlaying(uint32_t aFlags = 0);
|
||||
|
||||
/**
|
||||
* Returns an nsDOMMediaStream containing the played contents of this
|
||||
|
@ -1081,8 +1086,9 @@ protected:
|
|||
// At most one of mDecoder and mSrcStream can be non-null.
|
||||
nsRefPtr<DOMMediaStream> mSrcStream;
|
||||
|
||||
// Holds a reference to a MediaInputPort connecting mSrcStream to mPlaybackStream.
|
||||
nsRefPtr<MediaInputPort> mPlaybackStreamInputPort;
|
||||
// If non-negative, the time we should return for currentTime while playing
|
||||
// mSrcStream.
|
||||
double mSrcStreamPausedCurrentTime;
|
||||
|
||||
// Holds a reference to the stream connecting this stream to the capture sink.
|
||||
nsRefPtr<MediaInputPort> mCaptureStreamPort;
|
||||
|
@ -1370,6 +1376,9 @@ protected:
|
|||
// stored in mPreloadURI.
|
||||
bool mSuspendedForPreloadNone;
|
||||
|
||||
// True if we've connected mSrcStream to the media element output.
|
||||
bool mSrcStreamIsPlaying;
|
||||
|
||||
// True if a same-origin check has been done for the media element and resource.
|
||||
bool mMediaSecurityVerified;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "MediaDecoderStateMachine.h"
|
||||
#include "MediaTimer.h"
|
||||
#include "mediasink/DecodedAudioDataSink.h"
|
||||
#include "mediasink/AudioSinkWrapper.h"
|
||||
#include "nsTArray.h"
|
||||
#include "MediaDecoder.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
|
@ -287,6 +288,15 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||
mTaskQueue, this, &MediaDecoderStateMachine::OnAudioPopped);
|
||||
mVideoQueueListener = VideoQueue().PopEvent().Connect(
|
||||
mTaskQueue, this, &MediaDecoderStateMachine::OnVideoPopped);
|
||||
|
||||
nsRefPtr<MediaDecoderStateMachine> self = this;
|
||||
auto audioSinkCreator = [self] () {
|
||||
MOZ_ASSERT(self->OnTaskQueue());
|
||||
return new DecodedAudioDataSink(
|
||||
self->mAudioQueue, self->GetMediaTime(),
|
||||
self->mInfo.mAudio, self->mDecoder->GetAudioChannel());
|
||||
};
|
||||
mAudioSink = new AudioSinkWrapper(mTaskQueue, audioSinkCreator);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine::~MediaDecoderStateMachine()
|
||||
|
@ -331,6 +341,9 @@ MediaDecoderStateMachine::InitializationTask()
|
|||
mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
|
||||
mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
|
||||
mWatchManager.Watch(mSameOriginMedia, &MediaDecoderStateMachine::SameOriginMediaChanged);
|
||||
|
||||
// Propagate mSameOriginMedia to mDecodedStream.
|
||||
SameOriginMediaChanged();
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::HasFutureAudio()
|
||||
|
@ -362,7 +375,7 @@ int64_t MediaDecoderStateMachine::GetDecodedAudioDuration()
|
|||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
int64_t audioDecoded = AudioQueue().Duration();
|
||||
if (AudioEndTime() != -1) {
|
||||
if (mAudioSink->IsStarted()) {
|
||||
audioDecoded += AudioEndTime() - GetMediaTime();
|
||||
}
|
||||
return audioDecoded;
|
||||
|
@ -372,7 +385,7 @@ void MediaDecoderStateMachine::DiscardStreamData()
|
|||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
|
||||
MOZ_ASSERT(!mAudioSink->IsStarted(), "Should've been stopped in RunStateMachine()");
|
||||
|
||||
const auto clockTime = GetClock();
|
||||
while (true) {
|
||||
|
@ -1137,9 +1150,7 @@ void MediaDecoderStateMachine::VolumeChanged()
|
|||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetVolume(mVolume);
|
||||
}
|
||||
mAudioSink->SetVolume(mVolume);
|
||||
mDecodedStream->SetVolume(mVolume);
|
||||
}
|
||||
|
||||
|
@ -1260,6 +1271,8 @@ void MediaDecoderStateMachine::Shutdown()
|
|||
|
||||
Reset();
|
||||
|
||||
mAudioSink->Shutdown();
|
||||
|
||||
// Shut down our start time rendezvous.
|
||||
if (mStartTimeRendezvous) {
|
||||
mStartTimeRendezvous->Destroy();
|
||||
|
@ -1456,12 +1469,11 @@ void MediaDecoderStateMachine::StopAudioSink()
|
|||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
|
||||
if (mAudioSink) {
|
||||
DECODER_LOG("Shutdown audio thread");
|
||||
mAudioSink->Shutdown();
|
||||
mAudioSink = nullptr;
|
||||
if (mAudioSink->IsStarted()) {
|
||||
DECODER_LOG("Stop AudioSink");
|
||||
mAudioSink->Stop();
|
||||
mAudioSinkPromise.DisconnectIfExists();
|
||||
}
|
||||
mAudioSinkPromise.DisconnectIfExists();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1743,25 +1755,19 @@ MediaDecoderStateMachine::StartAudioSink()
|
|||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
if (mAudioCaptured) {
|
||||
MOZ_ASSERT(!mAudioSink);
|
||||
MOZ_ASSERT(!mAudioSink->IsStarted());
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasAudio() && !mAudioSink) {
|
||||
if (HasAudio() && !mAudioSink->IsStarted()) {
|
||||
mAudioCompleted = false;
|
||||
mAudioSink = new DecodedAudioDataSink(mAudioQueue,
|
||||
GetMediaTime(), mInfo.mAudio,
|
||||
mDecoder->GetAudioChannel());
|
||||
mAudioSink->Start(GetMediaTime(), mInfo);
|
||||
|
||||
mAudioSinkPromise.Begin(
|
||||
mAudioSink->Init()->Then(
|
||||
mAudioSink->OnEnded(TrackInfo::kAudioTrack)->Then(
|
||||
OwnerThread(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioSinkComplete,
|
||||
&MediaDecoderStateMachine::OnAudioSinkError));
|
||||
|
||||
mAudioSink->SetVolume(mVolume);
|
||||
mAudioSink->SetPlaybackRate(mPlaybackRate);
|
||||
mAudioSink->SetPreservesPitch(mPreservesPitch);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1799,7 +1805,7 @@ int64_t MediaDecoderStateMachine::AudioDecodedUsecs()
|
|||
// The amount of audio we have decoded is the amount of audio data we've
|
||||
// already decoded and pushed to the hardware, plus the amount of audio
|
||||
// data waiting to be pushed to the hardware.
|
||||
int64_t pushed = (AudioEndTime() != -1) ? (AudioEndTime() - GetMediaTime()) : 0;
|
||||
int64_t pushed = mAudioSink->IsStarted() ? (AudioEndTime() - GetMediaTime()) : 0;
|
||||
|
||||
// Currently for real time streams, AudioQueue().Duration() produce
|
||||
// wrong values (Bug 1114434), so we use frame counts to calculate duration.
|
||||
|
@ -1828,7 +1834,7 @@ bool MediaDecoderStateMachine::OutOfDecodedAudio()
|
|||
MOZ_ASSERT(OnTaskQueue());
|
||||
return IsAudioDecoding() && !AudioQueue().IsFinished() &&
|
||||
AudioQueue().GetSize() == 0 &&
|
||||
(!mAudioSink || !mAudioSink->HasUnplayedFrames());
|
||||
!mAudioSink->HasUnplayedFrames(TrackInfo::kAudioTrack);
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::HasLowUndecodedData()
|
||||
|
@ -2391,8 +2397,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
|||
|
||||
mSentPlaybackEndedEvent = true;
|
||||
|
||||
// Stop audio sink after call to AudioEndTime() above, otherwise it will
|
||||
// return an incorrect value due to a null mAudioSink.
|
||||
// MediaSink::GetEndTime() must be called before stopping playback.
|
||||
StopAudioSink();
|
||||
StopDecodedStream();
|
||||
}
|
||||
|
@ -2568,8 +2573,8 @@ MediaDecoderStateMachine::GetAudioClock() const
|
|||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(HasAudio() && !mAudioCompleted && IsPlaying());
|
||||
// Since this function is called while we are playing and AudioSink is
|
||||
// created once playback starts, mAudioSink is guaranteed to be non-null.
|
||||
MOZ_ASSERT(mAudioSink);
|
||||
// started once playback starts, IsStarted() is guaranteed to be true.
|
||||
MOZ_ASSERT(mAudioSink->IsStarted());
|
||||
return mAudioSink->GetPosition();
|
||||
}
|
||||
|
||||
|
@ -2909,9 +2914,7 @@ void MediaDecoderStateMachine::SetPlayStartTime(const TimeStamp& aTimeStamp)
|
|||
AssertCurrentThreadInMonitor();
|
||||
mPlayStartTime = aTimeStamp;
|
||||
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetPlaying(!mPlayStartTime.IsNull());
|
||||
}
|
||||
mAudioSink->SetPlaying(!mPlayStartTime.IsNull());
|
||||
// Have DecodedStream remember the playing state so it doesn't need to
|
||||
// ask MDSM about IsPlaying(). Note we have to do this even before capture
|
||||
// happens since capture could happen in the middle of playback.
|
||||
|
@ -3004,9 +3007,7 @@ MediaDecoderStateMachine::LogicalPlaybackRateChanged()
|
|||
}
|
||||
|
||||
mPlaybackRate = mLogicalPlaybackRate;
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetPlaybackRate(mPlaybackRate);
|
||||
}
|
||||
mAudioSink->SetPlaybackRate(mPlaybackRate);
|
||||
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
@ -3015,10 +3016,7 @@ void MediaDecoderStateMachine::PreservesPitchChanged()
|
|||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetPreservesPitch(mPreservesPitch);
|
||||
}
|
||||
mAudioSink->SetPreservesPitch(mPreservesPitch);
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::IsShutdown()
|
||||
|
@ -3045,15 +3043,12 @@ MediaDecoderStateMachine::AudioEndTime() const
|
|||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
if (mAudioSink) {
|
||||
return mAudioSink->GetEndTime();
|
||||
if (mAudioSink->IsStarted()) {
|
||||
return mAudioSink->GetEndTime(TrackInfo::kAudioTrack);
|
||||
} else if (mAudioCaptured) {
|
||||
return mDecodedStream->AudioEndTime();
|
||||
}
|
||||
// Don't call this after mAudioSink becomes null since we can't distinguish
|
||||
// "before StartAudioSink" and "after StopAudioSink" where mAudioSink
|
||||
// is null in both cases.
|
||||
MOZ_ASSERT(!mAudioCompleted);
|
||||
MOZ_ASSERT(!HasAudio());
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ hardware (via AudioStream).
|
|||
namespace mozilla {
|
||||
|
||||
namespace media {
|
||||
class AudioSink;
|
||||
class MediaSink;
|
||||
}
|
||||
|
||||
class AudioSegment;
|
||||
|
@ -999,8 +999,8 @@ private:
|
|||
// Media Fragment end time in microseconds. Access controlled by decoder monitor.
|
||||
int64_t mFragmentEndTime;
|
||||
|
||||
// The audio sink resource. Used on state machine and audio threads.
|
||||
RefPtr<media::AudioSink> mAudioSink;
|
||||
// The audio sink resource. Used on the state machine thread.
|
||||
nsRefPtr<media::MediaSink> mAudioSink;
|
||||
|
||||
// The reader, don't call its methods with the decoder monitor held.
|
||||
// This is created in the state machine's constructor.
|
||||
|
|
|
@ -461,6 +461,9 @@ public:
|
|||
}
|
||||
void RemoveVideoOutputImpl(VideoFrameContainer* aContainer)
|
||||
{
|
||||
// Ensure that any frames currently queued for playback by the compositor
|
||||
// are removed.
|
||||
aContainer->ClearFutureFrames();
|
||||
mVideoOutputs.RemoveElement(aContainer);
|
||||
}
|
||||
void ChangeExplicitBlockerCountImpl(GraphTime aTime, int32_t aDelta)
|
||||
|
|
|
@ -93,7 +93,24 @@ void VideoFrameContainer::ClearCurrentFrame()
|
|||
mImageContainer->GetCurrentImages(&kungFuDeathGrip);
|
||||
|
||||
mImageContainer->ClearAllImages();
|
||||
mImageSizeChanged = false;
|
||||
}
|
||||
|
||||
void VideoFrameContainer::ClearFutureFrames()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
// See comment in SetCurrentFrame for the reasoning behind
|
||||
// using a kungFuDeathGrip here.
|
||||
nsTArray<ImageContainer::OwningImage> kungFuDeathGrip;
|
||||
mImageContainer->GetCurrentImages(&kungFuDeathGrip);
|
||||
|
||||
if (!kungFuDeathGrip.IsEmpty()) {
|
||||
nsTArray<ImageContainer::NonOwningImage> currentFrame;
|
||||
const ImageContainer::OwningImage& img = kungFuDeathGrip[0];
|
||||
currentFrame.AppendElement(ImageContainer::NonOwningImage(img.mImage,
|
||||
img.mTimeStamp, img.mFrameID, img.mProducerID));
|
||||
mImageContainer->SetCurrentImages(currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
ImageContainer* VideoFrameContainer::GetImageContainer() {
|
||||
|
|
|
@ -52,6 +52,9 @@ public:
|
|||
}
|
||||
|
||||
void ClearCurrentFrame();
|
||||
// Make the current frame the only frame in the container, i.e. discard
|
||||
// all future frames.
|
||||
void ClearFutureFrames();
|
||||
// Time in seconds by which the last painted video frame was late by.
|
||||
// E.g. if the last painted frame should have been painted at time t,
|
||||
// but was actually painted at t+n, this returns n in seconds. Threadsafe.
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AudioSink.h"
|
||||
#include "AudioSinkWrapper.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace media {
|
||||
|
||||
AudioSinkWrapper::~AudioSinkWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AudioSinkWrapper::Shutdown()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(!mIsStarted, "Must be called after playback stopped.");
|
||||
mCreator = nullptr;
|
||||
}
|
||||
|
||||
const MediaSink::PlaybackParams&
|
||||
AudioSinkWrapper::GetPlaybackParams() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
return mParams;
|
||||
}
|
||||
|
||||
void
|
||||
AudioSinkWrapper::SetPlaybackParams(const PlaybackParams& aParams)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetVolume(aParams.volume);
|
||||
mAudioSink->SetPlaybackRate(aParams.playbackRate);
|
||||
mAudioSink->SetPreservesPitch(aParams.preservesPitch);
|
||||
}
|
||||
mParams = aParams;
|
||||
}
|
||||
|
||||
nsRefPtr<GenericPromise>
|
||||
AudioSinkWrapper::OnEnded(TrackType aType)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mIsStarted, "Must be called after playback starts.");
|
||||
if (aType == TrackInfo::kAudioTrack) {
|
||||
return mEndPromise;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int64_t
|
||||
AudioSinkWrapper::GetEndTime(TrackType aType) const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mIsStarted, "Must be called after playback starts.");
|
||||
if (aType == TrackInfo::kAudioTrack && mAudioSink) {
|
||||
return mAudioSink->GetEndTime();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t
|
||||
AudioSinkWrapper::GetPosition() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mIsStarted, "Must be called after playback starts.");
|
||||
return mAudioSink->GetPosition();
|
||||
}
|
||||
|
||||
bool
|
||||
AudioSinkWrapper::HasUnplayedFrames(TrackType aType) const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
return mAudioSink ? mAudioSink->HasUnplayedFrames() : false;
|
||||
}
|
||||
|
||||
void
|
||||
AudioSinkWrapper::SetVolume(double aVolume)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
mParams.volume = aVolume;
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetVolume(aVolume);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioSinkWrapper::SetPlaybackRate(double aPlaybackRate)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
mParams.playbackRate = aPlaybackRate;
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetPlaybackRate(aPlaybackRate);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioSinkWrapper::SetPreservesPitch(bool aPreservesPitch)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
mParams.preservesPitch = aPreservesPitch;
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetPreservesPitch(aPreservesPitch);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioSinkWrapper::SetPlaying(bool aPlaying)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
// Resume/pause matters only when playback started.
|
||||
if (!mIsStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetPlaying(aPlaying);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioSinkWrapper::Start(int64_t aStartTime, const MediaInfo& aInfo)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(!mIsStarted, "playback already started.");
|
||||
|
||||
mIsStarted = true;
|
||||
|
||||
mAudioSink = mCreator->Create();
|
||||
mEndPromise = mAudioSink->Init();
|
||||
SetPlaybackParams(mParams);
|
||||
}
|
||||
|
||||
void
|
||||
AudioSinkWrapper::Stop()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mIsStarted, "playback not started.");
|
||||
|
||||
mIsStarted = false;
|
||||
mAudioSink->Shutdown();
|
||||
mAudioSink = nullptr;
|
||||
mEndPromise = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
AudioSinkWrapper::IsStarted() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
return mIsStarted;
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
} // namespace mozilla
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef AudioSinkWrapper_h_
|
||||
#define AudioSinkWrapper_h_
|
||||
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/dom/AudioChannelBinding.h"
|
||||
#include "mozilla/nsRefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "MediaSink.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaData;
|
||||
template <class T> class MediaQueue;
|
||||
|
||||
namespace media {
|
||||
|
||||
class AudioSink;
|
||||
|
||||
/**
|
||||
* A wrapper around AudioSink to provide the interface of MediaSink.
|
||||
*/
|
||||
class AudioSinkWrapper : public MediaSink {
|
||||
// An AudioSink factory.
|
||||
class Creator {
|
||||
public:
|
||||
virtual ~Creator() {}
|
||||
virtual AudioSink* Create() = 0;
|
||||
};
|
||||
|
||||
// Wrap around a function object which creates AudioSinks.
|
||||
template <typename Function>
|
||||
class CreatorImpl : public Creator {
|
||||
public:
|
||||
explicit CreatorImpl(const Function& aFunc) : mFunction(aFunc) {}
|
||||
AudioSink* Create() override { return mFunction(); }
|
||||
private:
|
||||
Function mFunction;
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename Function>
|
||||
AudioSinkWrapper(AbstractThread* aOwnerThread, const Function& aFunc)
|
||||
: mOwnerThread(aOwnerThread)
|
||||
, mCreator(new CreatorImpl<Function>(aFunc))
|
||||
, mIsStarted(false)
|
||||
{}
|
||||
|
||||
const PlaybackParams& GetPlaybackParams() const override;
|
||||
void SetPlaybackParams(const PlaybackParams& aParams) override;
|
||||
|
||||
nsRefPtr<GenericPromise> OnEnded(TrackType aType) override;
|
||||
int64_t GetEndTime(TrackType aType) const override;
|
||||
int64_t GetPosition() const override;
|
||||
bool HasUnplayedFrames(TrackType aType) const override;
|
||||
|
||||
void SetVolume(double aVolume) override;
|
||||
void SetPlaybackRate(double aPlaybackRate) override;
|
||||
void SetPreservesPitch(bool aPreservesPitch) override;
|
||||
void SetPlaying(bool aPlaying) override;
|
||||
|
||||
void Start(int64_t aStartTime, const MediaInfo& aInfo) override;
|
||||
void Stop() override;
|
||||
bool IsStarted() const override;
|
||||
|
||||
void Shutdown() override;
|
||||
|
||||
private:
|
||||
virtual ~AudioSinkWrapper();
|
||||
|
||||
void AssertOwnerThread() const {
|
||||
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
|
||||
}
|
||||
|
||||
const nsRefPtr<AbstractThread> mOwnerThread;
|
||||
UniquePtr<Creator> mCreator;
|
||||
nsRefPtr<AudioSink> mAudioSink;
|
||||
nsRefPtr<GenericPromise> mEndPromise;
|
||||
|
||||
bool mIsStarted;
|
||||
PlaybackParams mParams;
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
} // namespace mozilla
|
||||
|
||||
#endif //AudioSinkWrapper_h_
|
|
@ -0,0 +1,115 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MediaSink_h_
|
||||
#define MediaSink_h_
|
||||
|
||||
#include "mozilla/nsRefPtr.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "MediaInfo.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace media {
|
||||
|
||||
/**
|
||||
* A consumer of audio/video data which plays audio and video tracks and
|
||||
* manages A/V sync between them.
|
||||
*
|
||||
* A typical sink sends audio/video outputs to the speaker and screen.
|
||||
* However, there are also sinks which capture the output of an media element
|
||||
* and send the output to a MediaStream.
|
||||
*
|
||||
* This class is used to move A/V sync management and audio/video rendering
|
||||
* out of MDSM so it is possible for subclasses to do external rendering using
|
||||
* specific hardware which is required by TV projects and CDM.
|
||||
*
|
||||
* Note this class is not thread-safe and should be called from the state
|
||||
* machine thread only.
|
||||
*/
|
||||
class MediaSink {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaSink);
|
||||
typedef mozilla::TrackInfo::TrackType TrackType;
|
||||
|
||||
struct PlaybackParams {
|
||||
PlaybackParams()
|
||||
: volume(1.0) , playbackRate(1.0) , preservesPitch(true) {}
|
||||
double volume;
|
||||
double playbackRate;
|
||||
bool preservesPitch;
|
||||
};
|
||||
|
||||
// Return the playback parameters of this sink.
|
||||
// Can be called in any state.
|
||||
virtual const PlaybackParams& GetPlaybackParams() const = 0;
|
||||
|
||||
// Set the playback parameters of this sink.
|
||||
// Can be called in any state.
|
||||
virtual void SetPlaybackParams(const PlaybackParams& aParams) = 0;
|
||||
|
||||
// Return a promise which is resolved when the track finishes
|
||||
// or null if no such track.
|
||||
// Must be called after playback starts.
|
||||
virtual nsRefPtr<GenericPromise> OnEnded(TrackType aType) = 0;
|
||||
|
||||
// Return the end time of the audio/video data that has been consumed
|
||||
// or -1 if no such track.
|
||||
// Must be called after playback starts.
|
||||
virtual int64_t GetEndTime(TrackType aType) const = 0;
|
||||
|
||||
// Return playback position of the media.
|
||||
// Since A/V sync is always maintained by this sink, there is no need to
|
||||
// specify whether we want to get audio or video position.
|
||||
// Must be called after playback starts.
|
||||
virtual int64_t GetPosition() const = 0;
|
||||
|
||||
// Return true if there are data consumed but not played yet.
|
||||
// Can be called in any state.
|
||||
virtual bool HasUnplayedFrames(TrackType aType) const = 0;
|
||||
|
||||
// Set volume of the audio track.
|
||||
// Do nothing if this sink has no audio track.
|
||||
// Can be called in any state.
|
||||
virtual void SetVolume(double aVolume) {}
|
||||
|
||||
// Set the playback rate.
|
||||
// Can be called in any state.
|
||||
virtual void SetPlaybackRate(double aPlaybackRate) {}
|
||||
|
||||
// Whether to preserve pitch of the audio track.
|
||||
// Do nothing if this sink has no audio track.
|
||||
// Can be called in any state.
|
||||
virtual void SetPreservesPitch(bool aPreservesPitch) {}
|
||||
|
||||
// Pause/resume the playback. Only work after playback starts.
|
||||
virtual void SetPlaying(bool aPlaying) = 0;
|
||||
|
||||
// Begin a playback session with the provided start time and media info.
|
||||
// Must be called when playback is stopped.
|
||||
virtual void Start(int64_t aStartTime, const MediaInfo& aInfo) = 0;
|
||||
|
||||
// Finish a playback session.
|
||||
// Must be called after playback starts.
|
||||
virtual void Stop() = 0;
|
||||
|
||||
// Return true if playback has started.
|
||||
// Can be called in any state.
|
||||
virtual bool IsStarted() const = 0;
|
||||
|
||||
// Called on the state machine thread to shut down the sink. All resources
|
||||
// allocated by this sink should be released.
|
||||
// Must be called after playback stopped.
|
||||
virtual void Shutdown() {}
|
||||
|
||||
protected:
|
||||
virtual ~MediaSink() {}
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
} // namespace mozilla
|
||||
|
||||
#endif //MediaSink_h_
|
|
@ -5,6 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AudioSinkWrapper.cpp',
|
||||
'DecodedAudioDataSink.cpp',
|
||||
]
|
||||
|
||||
|
|
|
@ -782,6 +782,8 @@ skip-if = (toolkit == 'android' && processor == 'x86') #timeout x86 only bug 914
|
|||
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
||||
[test_streams_autoplay.html]
|
||||
tags=msg capturestream
|
||||
[test_streams_capture_origin.html]
|
||||
tags=msg capturestream
|
||||
[test_streams_element_capture.html]
|
||||
#x86 only bug 914439, b2g desktop bug 752796
|
||||
skip-if = (toolkit == 'android' && processor == 'x86') || (buildapp == 'b2g' && toolkit != 'gonk')
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Bug 1189506</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1189506">Mozilla Bug 1189506</a>
|
||||
<p id="display"></p>
|
||||
<video id="vin"></video>
|
||||
<video id="vout" style="position:absolute; top:0; left:0"></video>
|
||||
<canvas id="c"></canvas>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1189506 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var resource = getPlayableVideo(gSmallTests).name;
|
||||
|
||||
vin.src = "http://example.org:8000/tests/dom/media/test/" + resource;
|
||||
var stream = vin.mozCaptureStreamUntilEnded();
|
||||
vout.srcObject = stream;
|
||||
vin.play();
|
||||
vout.play();
|
||||
|
||||
vout.onended = function() {
|
||||
var ctx = SpecialPowers.wrap(c.getContext("2d"));
|
||||
ctx.drawWindow(window, 0, 0, 10, 10, "rgb(255, 255, 0)", 0);
|
||||
var data = ctx.getImageData(2, 2, 1, 1);
|
||||
// Captured cross-origin video streams should render entirely black.
|
||||
is(data.data.join(','), "0,0,0,255", "expected black");
|
||||
vout.style.position = "";
|
||||
SimpleTest.finish();
|
||||
};
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -9,6 +9,7 @@
|
|||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// longer timeout for slow platforms
|
||||
if (isSlowPlatform()) {
|
||||
|
@ -16,8 +17,6 @@ if (isSlowPlatform()) {
|
|||
SimpleTest.requestCompleteLog();
|
||||
}
|
||||
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function checkDrawImage(vout) {
|
||||
var canvas = document.createElement("canvas");
|
||||
var ctx = canvas.getContext("2d");
|
||||
|
@ -26,14 +25,14 @@ function checkDrawImage(vout) {
|
|||
is(imgData.data[3], 255, "Check video frame pixel has been drawn");
|
||||
}
|
||||
|
||||
function startTest(test, token) {
|
||||
manager.started(token);
|
||||
function isGreaterThanOrEqualEps(a, b, msg) {
|
||||
ok(a >= b - 0.01,
|
||||
"Got " + a + ", expected at least " + b + "; " + msg);
|
||||
}
|
||||
|
||||
function startTest(test) {
|
||||
var v = document.createElement('video');
|
||||
var vout = document.createElement('video');
|
||||
vout.token = token;
|
||||
v.name = token + "(v)";
|
||||
vout.name = token + "(vout)";
|
||||
|
||||
v.src = test.name;
|
||||
var stream = v.mozCaptureStreamUntilEnded();
|
||||
|
@ -44,8 +43,8 @@ function startTest(test, token) {
|
|||
var checkEnded = function(test, vout, stream) { return function() {
|
||||
is(stream.currentTime, vout.currentTime, test.name + " stream final currentTime");
|
||||
if (test.duration) {
|
||||
ok(Math.abs(vout.currentTime - test.duration) < 0.1,
|
||||
test.name + " current time at end: " + vout.currentTime + " should be: " + test.duration);
|
||||
isGreaterThanOrEqualEps(vout.currentTime, test.duration,
|
||||
test.name + " current time at end");
|
||||
}
|
||||
is(vout.readyState, vout.HAVE_CURRENT_DATA, test.name + " checking readyState");
|
||||
ok(vout.ended, test.name + " checking playback has ended");
|
||||
|
@ -53,8 +52,8 @@ function startTest(test, token) {
|
|||
checkDrawImage(vout);
|
||||
}
|
||||
vout.parentNode.removeChild(vout);
|
||||
manager.finished(vout.token);
|
||||
removeNodeAndSource(v);
|
||||
SimpleTest.finish();
|
||||
}}(test, vout, stream);
|
||||
vout.addEventListener("ended", checkEnded, false);
|
||||
|
||||
|
@ -76,7 +75,16 @@ function startTest(test, token) {
|
|||
|
||||
}
|
||||
|
||||
manager.runTests(gSmallTests, startTest);
|
||||
// We only test one playable video because for some of the audio files
|
||||
// --- small-shot.mp3.mp4 and small-shot.m4a --- GStreamer doesn't decode
|
||||
// as much data as indicated by the duration, causing this test to fail on
|
||||
// Linux. See bug 1084185.
|
||||
var testVideo = getPlayableVideo(gSmallTests);
|
||||
if (testVideo) {
|
||||
startTest(testVideo);
|
||||
} else {
|
||||
todo(false, "No playable video");
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -21,6 +21,11 @@ function checkDrawImage(vout) {
|
|||
is(imgData.data[3], 255, "Check video frame pixel has been drawn");
|
||||
}
|
||||
|
||||
function isGreaterThanOrEqualEps(a, b, msg) {
|
||||
ok(a >= b - 0.01,
|
||||
"Got " + a + ", expected at least " + b + "; " + msg);
|
||||
}
|
||||
|
||||
function startTest(test, token) {
|
||||
manager.started(token);
|
||||
|
||||
|
@ -36,8 +41,8 @@ function startTest(test, token) {
|
|||
var checkEnded = function(test, vout, stream) { return function() {
|
||||
is(stream.currentTime, vout.currentTime, test.name + " stream final currentTime");
|
||||
if (test.duration) {
|
||||
ok(Math.abs(vout.currentTime - test.duration) < 0.1,
|
||||
test.name + " current time at end: " + vout.currentTime + " should be: " + test.duration);
|
||||
isGreaterThanOrEqualEps(vout.currentTime, test.duration,
|
||||
test.name + " current time at end");
|
||||
}
|
||||
is(vout.readyState, vout.HAVE_CURRENT_DATA, test.name + " checking readyState");
|
||||
ok(vout.ended, test.name + " checking playback has ended");
|
||||
|
|
|
@ -39,19 +39,24 @@ function isWithinEps(a, b, msg) {
|
|||
"Got " + a + ", expected " + b + "; " + msg);
|
||||
}
|
||||
|
||||
function isGreaterThanOrEqualEps(a, b, msg) {
|
||||
ok(a >= b - 0.01,
|
||||
"Got " + a + ", expected at least " + b + "; " + msg);
|
||||
}
|
||||
|
||||
function startTest(test) {
|
||||
var seekTime = test.duration/2;
|
||||
|
||||
function endedAfterReplay() {
|
||||
isWithinEps(v.currentTime, test.duration, "checking v.currentTime at third 'ended' event");
|
||||
isWithinEps(vout.currentTime, (test.duration - seekTime) + test.duration*2,
|
||||
isGreaterThanOrEqualEps(v.currentTime, test.duration, "checking v.currentTime at third 'ended' event");
|
||||
isGreaterThanOrEqualEps(vout.currentTime, (test.duration - seekTime) + test.duration*2,
|
||||
"checking vout.currentTime after seeking, playing through and reloading");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
function endedAfterSeek() {
|
||||
isWithinEps(v.currentTime, test.duration, "checking v.currentTime at second 'ended' event");
|
||||
isWithinEps(vout.currentTime, (test.duration - seekTime) + test.duration,
|
||||
isGreaterThanOrEqualEps(v.currentTime, test.duration, "checking v.currentTime at second 'ended' event");
|
||||
isGreaterThanOrEqualEps(vout.currentTime, (test.duration - seekTime) + test.duration,
|
||||
"checking vout.currentTime after seeking and playing through again");
|
||||
v.removeEventListener("ended", endedAfterSeek, false);
|
||||
v.addEventListener("ended", endedAfterReplay, false);
|
||||
|
@ -60,8 +65,8 @@ function startTest(test) {
|
|||
};
|
||||
|
||||
function seeked() {
|
||||
isWithinEps(v.currentTime, seekTime, "Finished seeking");
|
||||
isWithinEps(vout.currentTime, test.duration,
|
||||
isGreaterThanOrEqualEps(v.currentTime, seekTime, "Finished seeking");
|
||||
isGreaterThanOrEqualEps(vout.currentTime, test.duration,
|
||||
"checking vout.currentTime has not changed after seeking");
|
||||
v.removeEventListener("seeked", seeked, false);
|
||||
function dontPlayAgain() {
|
||||
|
@ -80,8 +85,8 @@ function startTest(test) {
|
|||
return;
|
||||
}
|
||||
|
||||
isWithinEps(vout.currentTime, test.duration, "checking vout.currentTime at first 'ended' event");
|
||||
isWithinEps(v.currentTime, test.duration, "checking v.currentTime at first 'ended' event");
|
||||
isGreaterThanOrEqualEps(vout.currentTime, test.duration, "checking vout.currentTime at first 'ended' event");
|
||||
isGreaterThanOrEqualEps(v.currentTime, test.duration, "checking v.currentTime at first 'ended' event");
|
||||
is(vout.ended, false, "checking vout has not ended");
|
||||
is(vout_untilended.ended, true, "checking vout_untilended has actually ended");
|
||||
|
||||
|
|
|
@ -206,7 +206,7 @@ CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
|
|||
const static js::Class sNPObjectJSWrapperClass =
|
||||
{
|
||||
NPRUNTIME_JSCLASS_NAME,
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
|
||||
JSCLASS_HAS_PRIVATE,
|
||||
NPObjWrapper_AddProperty,
|
||||
NPObjWrapper_DelProperty,
|
||||
NPObjWrapper_GetProperty,
|
||||
|
@ -264,7 +264,7 @@ NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
|
|||
|
||||
static const JSClass sNPObjectMemberClass =
|
||||
{
|
||||
"NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
|
||||
"NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, NPObjectMember_Convert,
|
||||
NPObjectMember_Finalize, NPObjectMember_Call,
|
||||
|
|
|
@ -596,24 +596,18 @@ nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary)
|
|||
// Ignore pending dictionary fetchers by increasing this number.
|
||||
mDictionaryFetcherGroup++;
|
||||
|
||||
nsDefaultStringComparator comparator;
|
||||
nsAutoString langCode;
|
||||
int32_t dashIdx = aDictionary.FindChar('-');
|
||||
if (dashIdx != -1) {
|
||||
langCode.Assign(Substring(aDictionary, 0, dashIdx));
|
||||
} else {
|
||||
langCode.Assign(aDictionary);
|
||||
}
|
||||
uint32_t flags = 0;
|
||||
mEditor->GetFlags(&flags);
|
||||
if (!(flags & nsIPlaintextEditor::eEditorMailMask)) {
|
||||
if (mPreferredLang.IsEmpty() ||
|
||||
!nsStyleUtil::DashMatchCompare(mPreferredLang, langCode, comparator)) {
|
||||
if (mPreferredLang.IsEmpty() || !mPreferredLang.Equals(aDictionary)) {
|
||||
// When user sets dictionary manually, we store this value associated
|
||||
// with editor url.
|
||||
// with editor url, if it doesn't match the document language exactly.
|
||||
// For example on "en" sites, we need to store "en-GB", otherwise
|
||||
// the language might jump back to en-US although the user explicitly
|
||||
// chose otherwise.
|
||||
StoreCurrentDictionary(mEditor, aDictionary);
|
||||
} else {
|
||||
// If user sets a dictionary matching (even partially), lang defined by
|
||||
// If user sets a dictionary matching the language defined by
|
||||
// document, we consider content pref has been canceled, and we clear it.
|
||||
ClearCurrentDictionary(mEditor);
|
||||
}
|
||||
|
|
|
@ -619,8 +619,7 @@ struct JSClass {
|
|||
// SetNewObjectMetadata itself
|
||||
#define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) // private is (nsISupports*)
|
||||
#define JSCLASS_IS_DOMJSCLASS (1<<4) // objects are DOM
|
||||
#define JSCLASS_IMPLEMENTS_BARRIERS (1<<5) // Correctly implements GC read
|
||||
// and write barriers
|
||||
// Bit 5 is unused.
|
||||
#define JSCLASS_EMULATES_UNDEFINED (1<<6) // objects of this class act
|
||||
// like the value undefined,
|
||||
// in some contexts
|
||||
|
|
|
@ -213,9 +213,6 @@ GCForReason(JSRuntime* rt, JSGCInvocationKind gckind, gcreason::Reason reason);
|
|||
* JS_GC().
|
||||
* - The GC mode must have been set to JSGC_MODE_INCREMENTAL with
|
||||
* JS_SetGCParameter().
|
||||
* - All native objects that have their own trace hook must indicate that they
|
||||
* implement read and write barriers with the JSCLASS_IMPLEMENTS_BARRIERS
|
||||
* flag.
|
||||
*
|
||||
* Note: Even if incremental GC is enabled and working correctly,
|
||||
* non-incremental collections can still happen when low on memory.
|
||||
|
|
|
@ -132,8 +132,8 @@ namespace JS {
|
|||
_(ICGetElemStub_Dense) \
|
||||
_(ICGetElemStub_DenseHole) \
|
||||
_(ICGetElemStub_TypedArray) \
|
||||
_(ICGetElemStub_ArgsElement) \
|
||||
_(ICGetElemStub_ArgsElementStrict) \
|
||||
_(ICGetElemStub_ArgsElementMapped) \
|
||||
_(ICGetElemStub_ArgsElementUnmapped) \
|
||||
\
|
||||
_(ICSetElemStub_Dense) \
|
||||
_(ICSetElemStub_TypedArray) \
|
||||
|
@ -150,7 +150,6 @@ namespace JS {
|
|||
_(CantInlineClassConstructor) \
|
||||
_(CantInlineDisabledIon) \
|
||||
_(CantInlineTooManyArgs) \
|
||||
_(CantInlineHeavyweight) \
|
||||
_(CantInlineNeedsArgsObj) \
|
||||
_(CantInlineDebuggee) \
|
||||
_(CantInlineUnknownProps) \
|
||||
|
|
|
@ -1025,7 +1025,7 @@ AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj)
|
|||
|
||||
const Class AsmJSModuleObject::class_ = {
|
||||
"AsmJSModuleObject",
|
||||
JSCLASS_IS_ANONYMOUS | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_DELAY_METADATA_CALLBACK |
|
||||
JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
|
|
|
@ -99,7 +99,7 @@ enum AsmJSSimdOperation
|
|||
|
||||
// These labels describe positions in the prologue/epilogue of functions while
|
||||
// compiling an AsmJSModule.
|
||||
struct AsmJSFunctionLabels
|
||||
struct MOZ_STACK_CLASS AsmJSFunctionLabels
|
||||
{
|
||||
AsmJSFunctionLabels(jit::Label& entry, jit::Label& overflowExit)
|
||||
: entry(entry), overflowExit(overflowExit) {}
|
||||
|
@ -542,7 +542,10 @@ class AsmJSModule
|
|||
|
||||
class CodeRange
|
||||
{
|
||||
protected:
|
||||
uint32_t nameIndex_;
|
||||
|
||||
private:
|
||||
uint32_t lineNumber_;
|
||||
uint32_t begin_;
|
||||
uint32_t profilingReturn_;
|
||||
|
@ -627,6 +630,24 @@ class AsmJSModule
|
|||
}
|
||||
};
|
||||
|
||||
class FunctionCodeRange : public CodeRange
|
||||
{
|
||||
private:
|
||||
PropertyName* name_;
|
||||
|
||||
public:
|
||||
FunctionCodeRange(PropertyName* name, uint32_t lineNumber, const AsmJSFunctionLabels& l)
|
||||
: CodeRange(UINT32_MAX, lineNumber, l), name_(name)
|
||||
{}
|
||||
|
||||
PropertyName* name() const { return name_; }
|
||||
|
||||
void initNameIndex(uint32_t nameIndex) {
|
||||
MOZ_ASSERT(nameIndex_ == UINT32_MAX);
|
||||
nameIndex_ = nameIndex;
|
||||
}
|
||||
};
|
||||
|
||||
class FuncPtrTable
|
||||
{
|
||||
uint32_t globalDataOffset_;
|
||||
|
@ -1137,15 +1158,14 @@ class AsmJSModule
|
|||
bool addCodeRange(CodeRange::Kind kind, uint32_t begin, uint32_t pret, uint32_t end) {
|
||||
return codeRanges_.append(CodeRange(kind, begin, pret, end));
|
||||
}
|
||||
bool addFunctionCodeRange(PropertyName* name, uint32_t lineNumber,
|
||||
const AsmJSFunctionLabels& labels)
|
||||
bool addFunctionCodeRange(PropertyName* name, FunctionCodeRange&& codeRange)
|
||||
{
|
||||
MOZ_ASSERT(!isFinished());
|
||||
MOZ_ASSERT(name->isTenured());
|
||||
if (names_.length() >= UINT32_MAX)
|
||||
return false;
|
||||
uint32_t nameIndex = names_.length();
|
||||
return names_.append(name) && codeRanges_.append(CodeRange(nameIndex, lineNumber, labels));
|
||||
codeRange.initNameIndex(names_.length());
|
||||
return names_.append(name) && codeRanges_.append(Move(codeRange));
|
||||
}
|
||||
bool addBuiltinThunkCodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin,
|
||||
uint32_t profilingReturn, uint32_t end)
|
||||
|
@ -1191,12 +1211,10 @@ class AsmJSModule
|
|||
return functionCounts_.append(counts);
|
||||
}
|
||||
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
bool addProfiledFunction(PropertyName* name, unsigned codeStart, unsigned codeEnd,
|
||||
unsigned line, unsigned column)
|
||||
bool addProfiledFunction(ProfiledFunction&& func)
|
||||
{
|
||||
MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
|
||||
ProfiledFunction func(name, codeStart, codeEnd, line, column);
|
||||
return profiledFunctions_.append(func);
|
||||
return profiledFunctions_.append(mozilla::Move(func));
|
||||
}
|
||||
unsigned numProfiledFunctions() const {
|
||||
MOZ_ASSERT(isFinishedWithModulePrologue());
|
||||
|
@ -1208,11 +1226,9 @@ class AsmJSModule
|
|||
}
|
||||
#endif
|
||||
#ifdef JS_ION_PERF
|
||||
bool addProfiledBlocks(PropertyName* name, unsigned codeBegin, unsigned inlineEnd,
|
||||
unsigned codeEnd, jit::BasicBlocksVector& basicBlocks)
|
||||
bool addProfiledBlocks(ProfiledBlocksFunction&& func)
|
||||
{
|
||||
MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
|
||||
ProfiledBlocksFunction func(name, codeBegin, inlineEnd, codeEnd, basicBlocks);
|
||||
return perfProfiledBlocksFunctions_.append(mozilla::Move(func));
|
||||
}
|
||||
unsigned numPerfBlocksFunctions() const {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -78,6 +78,13 @@ ReportBadArrayType(JSContext* cx)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportOutOfRange(JSContext* cx)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_BAD_INDEX);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetSharedTypedArray(JSContext* cx, HandleValue v,
|
||||
MutableHandle<SharedTypedArrayObject*> viewp)
|
||||
|
@ -90,36 +97,17 @@ GetSharedTypedArray(JSContext* cx, HandleValue v,
|
|||
return true;
|
||||
}
|
||||
|
||||
// Returns true so long as the conversion succeeds, and then *inRange
|
||||
// is set to false if the index is not in range.
|
||||
static bool
|
||||
GetSharedTypedArrayIndex(JSContext* cx, HandleValue v, Handle<SharedTypedArrayObject*> view,
|
||||
uint32_t* offset, bool* inRange)
|
||||
uint32_t* offset)
|
||||
{
|
||||
RootedId id(cx);
|
||||
if (!ValueToId<CanGC>(cx, v, &id))
|
||||
return false;
|
||||
uint64_t index;
|
||||
if (!IsTypedArrayIndex(id, &index) || index >= view->length()) {
|
||||
*inRange = false;
|
||||
} else {
|
||||
*offset = (uint32_t)index;
|
||||
*inRange = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
js::atomics_fullMemoryBarrier()
|
||||
{
|
||||
jit::AtomicOperations::fenceSeqCst();
|
||||
}
|
||||
|
||||
static bool
|
||||
AtomicsFence(JSContext* cx, MutableHandleValue r)
|
||||
{
|
||||
atomics_fullMemoryBarrier();
|
||||
r.setUndefined();
|
||||
if (!IsTypedArrayIndex(id, &index) || index >= view->length())
|
||||
return ReportOutOfRange(cx);
|
||||
*offset = (uint32_t)index;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -127,7 +115,9 @@ bool
|
|||
js::atomics_fence(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return AtomicsFence(cx, args.rval());
|
||||
jit::AtomicOperations::fenceSeqCst();
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
|
@ -198,8 +188,7 @@ js::atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!GetSharedTypedArray(cx, objv, &view))
|
||||
return false;
|
||||
uint32_t offset;
|
||||
bool inRange;
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
int32_t oldCandidate;
|
||||
if (!ToInt32(cx, oldv, &oldCandidate))
|
||||
|
@ -208,9 +197,6 @@ js::atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!ToInt32(cx, newv, &newCandidate))
|
||||
return false;
|
||||
|
||||
if (!inRange)
|
||||
return AtomicsFence(cx, r);
|
||||
|
||||
bool badType = false;
|
||||
int32_t result = CompareExchange(view->type(), oldCandidate, newCandidate, view->viewData(), offset, &badType);
|
||||
|
||||
|
@ -236,13 +222,9 @@ js::atomics_load(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!GetSharedTypedArray(cx, objv, &view))
|
||||
return false;
|
||||
uint32_t offset;
|
||||
bool inRange;
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
|
||||
if (!inRange)
|
||||
return AtomicsFence(cx, r);
|
||||
|
||||
switch (view->type()) {
|
||||
case Scalar::Uint8:
|
||||
case Scalar::Uint8Clamped: {
|
||||
|
@ -356,19 +338,12 @@ ExchangeOrStore(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!GetSharedTypedArray(cx, objv, &view))
|
||||
return false;
|
||||
uint32_t offset;
|
||||
bool inRange;
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
int32_t numberValue;
|
||||
if (!ToInt32(cx, valv, &numberValue))
|
||||
return false;
|
||||
|
||||
if (!inRange) {
|
||||
atomics_fullMemoryBarrier();
|
||||
r.set(valv);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool badType = false;
|
||||
int32_t result = ExchangeOrStore<op>(view->type(), numberValue, view->viewData(), offset, &badType);
|
||||
|
||||
|
@ -403,16 +378,12 @@ AtomicsBinop(JSContext* cx, HandleValue objv, HandleValue idxv, HandleValue valv
|
|||
if (!GetSharedTypedArray(cx, objv, &view))
|
||||
return false;
|
||||
uint32_t offset;
|
||||
bool inRange;
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
int32_t numberValue;
|
||||
if (!ToInt32(cx, valv, &numberValue))
|
||||
return false;
|
||||
|
||||
if (!inRange)
|
||||
return AtomicsFence(cx, r);
|
||||
|
||||
switch (view->type()) {
|
||||
case Scalar::Int8: {
|
||||
int8_t v = (int8_t)numberValue;
|
||||
|
@ -804,8 +775,7 @@ js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (view->type() != Scalar::Int32)
|
||||
return ReportBadArrayType(cx);
|
||||
uint32_t offset;
|
||||
bool inRange;
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
int32_t value;
|
||||
if (!ToInt32(cx, valv, &value))
|
||||
|
@ -822,12 +792,6 @@ js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp)
|
|||
timeout_ms = 0;
|
||||
}
|
||||
|
||||
if (!inRange) {
|
||||
atomics_fullMemoryBarrier();
|
||||
r.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
// This lock also protects the "waiters" field on SharedArrayRawBuffer,
|
||||
// and it provides the necessary memory fence.
|
||||
AutoLockFutexAPI lock;
|
||||
|
@ -883,14 +847,8 @@ js::atomics_futexWake(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (view->type() != Scalar::Int32)
|
||||
return ReportBadArrayType(cx);
|
||||
uint32_t offset;
|
||||
bool inRange;
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
|
||||
if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
if (!inRange) {
|
||||
atomics_fullMemoryBarrier();
|
||||
r.setUndefined();
|
||||
return true;
|
||||
}
|
||||
double count;
|
||||
if (!ToInteger(cx, countv, &count))
|
||||
return false;
|
||||
|
@ -938,8 +896,7 @@ js::atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (view->type() != Scalar::Int32)
|
||||
return ReportBadArrayType(cx);
|
||||
uint32_t offset1;
|
||||
bool inRange1;
|
||||
if (!GetSharedTypedArrayIndex(cx, idx1v, view, &offset1, &inRange1))
|
||||
if (!GetSharedTypedArrayIndex(cx, idx1v, view, &offset1))
|
||||
return false;
|
||||
double count;
|
||||
if (!ToInteger(cx, countv, &count))
|
||||
|
@ -950,14 +907,8 @@ js::atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!ToInt32(cx, valv, &value))
|
||||
return false;
|
||||
uint32_t offset2;
|
||||
bool inRange2;
|
||||
if (!GetSharedTypedArrayIndex(cx, idx2v, view, &offset2, &inRange2))
|
||||
if (!GetSharedTypedArrayIndex(cx, idx2v, view, &offset2))
|
||||
return false;
|
||||
if (!(inRange1 && inRange2)) {
|
||||
atomics_fullMemoryBarrier();
|
||||
r.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoLockFutexAPI lock;
|
||||
|
||||
|
|
|
@ -29,8 +29,6 @@ class AtomicsObject : public JSObject
|
|||
};
|
||||
};
|
||||
|
||||
void atomics_fullMemoryBarrier();
|
||||
|
||||
bool atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp);
|
||||
bool atomics_exchange(JSContext* cx, unsigned argc, Value* vp);
|
||||
bool atomics_load(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
|
|
@ -106,7 +106,6 @@ namespace {
|
|||
|
||||
const Class MapIteratorObject::class_ = {
|
||||
"Map Iterator",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
|
@ -223,7 +222,7 @@ MapIteratorObject::next(JSContext* cx, Handle<MapIteratorObject*> mapIterator,
|
|||
|
||||
const Class MapObject::class_ = {
|
||||
"Map",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
|
||||
nullptr, // addProperty
|
||||
nullptr, // delProperty
|
||||
|
@ -831,7 +830,6 @@ class SetIteratorObject : public NativeObject
|
|||
|
||||
const Class SetIteratorObject::class_ = {
|
||||
"Set Iterator",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount),
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
|
@ -972,7 +970,7 @@ SetIteratorObject::next(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
const Class SetObject::class_ = {
|
||||
"Set",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
|
||||
nullptr, // addProperty
|
||||
nullptr, // delProperty
|
||||
|
|
|
@ -69,8 +69,7 @@ ImportEntryObject::class_ = {
|
|||
"ImportEntry",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ImportEntryObject::SlotCount) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_ImportEntry) |
|
||||
JSCLASS_IS_ANONYMOUS |
|
||||
JSCLASS_IMPLEMENTS_BARRIERS
|
||||
JSCLASS_IS_ANONYMOUS
|
||||
};
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, moduleRequest, ModuleRequestSlot)
|
||||
|
@ -138,8 +137,7 @@ ExportEntryObject::class_ = {
|
|||
"ExportEntry",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ExportEntryObject::SlotCount) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_ExportEntry) |
|
||||
JSCLASS_IS_ANONYMOUS |
|
||||
JSCLASS_IMPLEMENTS_BARRIERS
|
||||
JSCLASS_IS_ANONYMOUS
|
||||
};
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, exportName, ExportNameSlot)
|
||||
|
@ -218,8 +216,7 @@ ModuleObject::class_ = {
|
|||
"Module",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Module) |
|
||||
JSCLASS_IS_ANONYMOUS |
|
||||
JSCLASS_IMPLEMENTS_BARRIERS,
|
||||
JSCLASS_IS_ANONYMOUS,
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
|
|
|
@ -2247,7 +2247,7 @@ OutlineTransparentTypedObject::getOrCreateBuffer(JSContext* cx)
|
|||
#define DEFINE_TYPEDOBJ_CLASS(Name, Trace) \
|
||||
const Class Name::class_ = { \
|
||||
# Name, \
|
||||
Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS, \
|
||||
Class::NON_NATIVE, \
|
||||
nullptr, /* addProperty */ \
|
||||
nullptr, /* delProperty */ \
|
||||
nullptr, /* getProperty */ \
|
||||
|
|
|
@ -25,7 +25,7 @@ using mozilla::UniquePtr;
|
|||
|
||||
const Class WeakSetObject::class_ = {
|
||||
"WeakSet",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_CACHED_PROTO(JSProto_WeakSet) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_WeakSet) |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(WeakSetObject::RESERVED_SLOTS)
|
||||
};
|
||||
|
||||
|
|
|
@ -559,7 +559,7 @@ static const JSClass sCDataProtoClass = {
|
|||
|
||||
static const JSClass sCTypeClass = {
|
||||
"CType",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
|
||||
JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, CType::Finalize,
|
||||
CType::ConstructData, CType::HasInstance, CType::ConstructData,
|
||||
|
@ -576,7 +576,7 @@ static const JSClass sCDataClass = {
|
|||
|
||||
static const JSClass sCClosureClass = {
|
||||
"CClosure",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
|
||||
JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, CClosure::Finalize,
|
||||
nullptr, nullptr, nullptr, CClosure::Trace
|
||||
|
|
|
@ -273,7 +273,8 @@ function isRootedGCPointerTypeName(name)
|
|||
name == "WrappableJSErrorResult" ||
|
||||
name == "frontend::TokenStream" ||
|
||||
name == "frontend::TokenStream::Position" ||
|
||||
name == "ModuleCompiler")
|
||||
name == "ModuleCompiler" ||
|
||||
name == "ModuleValidator")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1808,7 +1808,7 @@ BytecodeEmitter::bindNameToSlotHelper(ParseNode* pn)
|
|||
MOZ_ASSERT(pn->pn_atom == fun->atom());
|
||||
|
||||
/*
|
||||
* Leave pn->isOp(JSOP_GETNAME) if this->fun is heavyweight to
|
||||
* Leave pn->isOp(JSOP_GETNAME) if this->fun needs a CallObject to
|
||||
* address two cases: a new binding introduced by eval, and
|
||||
* assignment to the name in strict mode.
|
||||
*
|
||||
|
@ -1828,10 +1828,10 @@ BytecodeEmitter::bindNameToSlotHelper(ParseNode* pn)
|
|||
* has no effect. But in strict mode, this attempt to mutate an
|
||||
* immutable binding must throw a TypeError. We implement this by
|
||||
* not optimizing such assignments and by marking such functions as
|
||||
* heavyweight, ensuring that the function name is represented in
|
||||
* needsCallObject, ensuring that the function name is represented in
|
||||
* the scope chain so that assignment will throw a TypeError.
|
||||
*/
|
||||
if (!sc->asFunctionBox()->isHeavyweight()) {
|
||||
if (!sc->asFunctionBox()->needsCallObject()) {
|
||||
op = JSOP_CALLEE;
|
||||
pn->pn_dflags |= PND_CONST;
|
||||
}
|
||||
|
@ -3599,7 +3599,7 @@ BytecodeEmitter::maybeEmitVarDecl(JSOp prologueOp, ParseNode* pn, jsatomid* resu
|
|||
}
|
||||
|
||||
if (JOF_OPTYPE(pn->getOp()) == JOF_ATOM &&
|
||||
(!sc->isFunctionBox() || sc->asFunctionBox()->isHeavyweight()))
|
||||
(!sc->isFunctionBox() || sc->asFunctionBox()->needsCallObject()))
|
||||
{
|
||||
switchToPrologue();
|
||||
if (!updateSourceCoordNotes(pn->pn_pos.begin))
|
||||
|
|
|
@ -1081,12 +1081,13 @@ Parser<FullParseHandler>::checkFunctionArguments()
|
|||
|
||||
/*
|
||||
* Check whether any parameters have been assigned within this
|
||||
* function. In strict mode parameters do not alias arguments[i], and
|
||||
* to make the arguments object reflect initial parameter values prior
|
||||
* to any mutation we create it eagerly whenever parameters are (or
|
||||
* might, in the case of calls to eval) be assigned.
|
||||
* function. If the arguments object is unmapped (strict mode or
|
||||
* function with default/rest/destructing args), parameters do not alias
|
||||
* arguments[i], and to make the arguments object reflect initial
|
||||
* parameter values prior to any mutation we create it eagerly whenever
|
||||
* parameters are (or might, in the case of calls to eval) assigned.
|
||||
*/
|
||||
if (pc->sc->needStrictChecks()) {
|
||||
if (!funbox->hasMappedArgsObj()) {
|
||||
for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) {
|
||||
DefinitionList& dlist = r.front().value();
|
||||
for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) {
|
||||
|
@ -8358,7 +8359,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool
|
|||
JSOp op = JSOP_CALL;
|
||||
if (PropertyName* name = handler.maybeNameAnyParentheses(lhs)) {
|
||||
if (tt == TOK_LP && name == context->names().eval) {
|
||||
/* Select JSOP_EVAL and flag pc as heavyweight. */
|
||||
/* Select JSOP_EVAL and flag pc as needsCallObject. */
|
||||
op = pc->sc->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
|
||||
pc->sc->setBindingsAccessedDynamically();
|
||||
pc->sc->setHasDirectEval();
|
||||
|
|
|
@ -240,7 +240,7 @@ class SharedContext
|
|||
|
||||
inline bool allLocalsAliased();
|
||||
|
||||
bool strict() {
|
||||
bool strict() const {
|
||||
return strictScript || localStrict;
|
||||
}
|
||||
bool setLocalStrictMode(bool strict) {
|
||||
|
@ -250,7 +250,7 @@ class SharedContext
|
|||
}
|
||||
|
||||
// JSOPTION_EXTRA_WARNINGS warnings or strict mode errors.
|
||||
bool needStrictChecks() {
|
||||
bool needStrictChecks() const {
|
||||
return strict() || extraWarnings;
|
||||
}
|
||||
|
||||
|
@ -347,6 +347,10 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
return length != function()->nargs() - function()->hasRest();
|
||||
}
|
||||
|
||||
bool hasMappedArgsObj() const {
|
||||
return !strict() && !function()->hasRest() && !hasDefaults() && !hasDestructuringArgs;
|
||||
}
|
||||
|
||||
// Return whether this or an enclosing function is being parsed and
|
||||
// validated as asm.js. Note: if asm.js validation fails, this will be false
|
||||
// while the function is being reparsed. This flag can be used to disable
|
||||
|
@ -362,9 +366,9 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
startColumn = tokenStream.getColumn();
|
||||
}
|
||||
|
||||
bool isHeavyweight()
|
||||
bool needsCallObject()
|
||||
{
|
||||
// Note: this should be kept in sync with JSFunction::isHeavyweight().
|
||||
// Note: this should be kept in sync with JSFunction::needsCallObject().
|
||||
return bindings.hasAnyAliasedBindings() ||
|
||||
hasExtensibleScope() ||
|
||||
needsDeclEnvObject() ||
|
||||
|
|
|
@ -1182,13 +1182,6 @@ CallTraceHook(Functor f, JSTracer* trc, JSObject* obj, CheckGeneration check, Ar
|
|||
if (!clasp->trace)
|
||||
return &obj->as<NativeObject>();
|
||||
|
||||
// Global objects all have the same trace hook. That hook is safe without barriers
|
||||
// if the global has no custom trace hook of its own, or has been moved to a different
|
||||
// compartment, and so can't have one.
|
||||
MOZ_ASSERT_IF(!(clasp->trace == JS_GlobalObjectTraceHook &&
|
||||
(!obj->compartment()->options().getTrace() || !obj->isOwnGlobal())),
|
||||
clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
|
||||
|
||||
if (clasp->trace == InlineTypedObject::obj_trace) {
|
||||
Shape** pshape = obj->as<InlineTypedObject>().addressOfShapeFromGC();
|
||||
f(pshape, mozilla::Forward<Args>(args)...);
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// An unmapped arguments object is created for strict functions or functions
|
||||
// with default/rest/destructuring args.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function testDefaults(a, b=3) {
|
||||
a = 3;
|
||||
b = 4;
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(arguments[0], 1);
|
||||
assertEq(arguments[1], undefined);
|
||||
arguments[0] = 5;
|
||||
assertEq(a, 3);
|
||||
assertThrowsInstanceOf(() => arguments.callee, TypeError);
|
||||
}
|
||||
testDefaults(1);
|
||||
|
||||
function testRest(a, ...rest) {
|
||||
a = 3;
|
||||
assertEq(arguments.length, 3);
|
||||
assertEq(arguments[0], 1);
|
||||
assertEq(arguments[1], 2);
|
||||
arguments[0] = 5;
|
||||
assertEq(a, 3);
|
||||
arguments[1] = 6;
|
||||
assertEq(arguments[1], 6);
|
||||
assertEq(rest.toString(), "2,3");
|
||||
assertThrowsInstanceOf(() => arguments.callee, TypeError);
|
||||
}
|
||||
testRest(1, 2, 3);
|
||||
|
||||
function testDestructuring(a, {foo, bar}, b) {
|
||||
a = 3;
|
||||
bar = 4;
|
||||
b = 1;
|
||||
assertEq(arguments.length, 3);
|
||||
assertEq(arguments[0], 1);
|
||||
assertEq(arguments[1].bar, 2);
|
||||
assertEq(arguments[2], 9);
|
||||
assertThrowsInstanceOf(() => arguments.callee, TypeError);
|
||||
}
|
||||
testDestructuring(1, {foo: 1, bar: 2}, 9);
|
||||
|
||||
function testStrict(a) {
|
||||
"use strict";
|
||||
a = 3;
|
||||
assertEq(arguments[0], 1);
|
||||
arguments[0] = 8;
|
||||
assertEq(a, 3);
|
||||
assertThrowsInstanceOf(() => arguments.callee, TypeError);
|
||||
}
|
||||
testStrict(1, 2);
|
||||
|
||||
function testMapped(a) {
|
||||
a = 3;
|
||||
assertEq(arguments[0], 3);
|
||||
arguments[0] = 5;
|
||||
assertEq(a, 5);
|
||||
assertEq(arguments.callee, testMapped);
|
||||
}
|
||||
testMapped(1);
|
|
@ -1,8 +1,5 @@
|
|||
// 'arguments' is allowed with rest parameters.
|
||||
|
||||
// FIXME: We should create an unmapped arguments object in this case,
|
||||
// see bug 1175394. This test is not correct until then.
|
||||
|
||||
var args;
|
||||
|
||||
function restWithArgs(a, b, ...rest) {
|
||||
|
@ -11,8 +8,7 @@ function restWithArgs(a, b, ...rest) {
|
|||
|
||||
args = restWithArgs(1, 3, 6, 9);
|
||||
assertEq(args.length, 4);
|
||||
assertEq(JSON.stringify(args), '{"0":1,"1":3,"2":[6,9],"3":9}',
|
||||
"Did you just fix bug 1175394?");
|
||||
assertEq(JSON.stringify(args), '{"0":1,"1":3,"2":6,"3":9}');
|
||||
|
||||
args = restWithArgs();
|
||||
assertEq(args.length, 0);
|
||||
|
@ -27,8 +23,7 @@ function restWithArgsEval(a, b, ...rest) {
|
|||
|
||||
args = restWithArgsEval(1, 3, 6, 9);
|
||||
assertEq(args.length, 4);
|
||||
assertEq(JSON.stringify(args), '{"0":1,"1":3,"2":[6,9],"3":9}',
|
||||
"Did you just fix bug 1175394?");
|
||||
assertEq(JSON.stringify(args), '{"0":1,"1":3,"2":6,"3":9}');
|
||||
|
||||
function g(...rest) {
|
||||
h();
|
||||
|
|
|
@ -9,7 +9,7 @@ function restAndArgs(...rest) {
|
|||
|
||||
var args = restAndArgs(1, 2, 3)();
|
||||
assertEq(args.length, 3);
|
||||
assertDeepEq(args[0], [1, 2, 3], "This is bogus, see bug 1175394");
|
||||
assertEq(args[0], 1);
|
||||
assertEq(args[1], 2);
|
||||
assertEq(args[2], 3);
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,6 +1,8 @@
|
|||
if (!this.SharedArrayBuffer)
|
||||
quit(0);
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function m(stdlib, ffi, heap) {
|
||||
"use asm";
|
||||
var HEAP32 = new stdlib.SharedInt32Array(heap);
|
||||
|
@ -20,4 +22,5 @@ if (isAsmJSCompilationAvailable())
|
|||
|
||||
var sab = new SharedArrayBuffer(65536);
|
||||
var {add_sharedEv} = m(this, {}, sab);
|
||||
add_sharedEv(sab.byteLength);
|
||||
assertErrorMessage(() => add_sharedEv(sab.byteLength), RangeError, /out-of-range index/);
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
//
|
||||
// These do not test the futex operations.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var DEBUG = false; // Set to true for useful printouts
|
||||
|
||||
function dprint(...xs) {
|
||||
|
@ -204,18 +206,23 @@ function testTypeBinop(a, op) {
|
|||
op(a, 0);
|
||||
}
|
||||
|
||||
var globlength = 0; // Will be set later
|
||||
|
||||
function testRangeCAS(a) {
|
||||
dprint("Range: " + a.constructor.name);
|
||||
|
||||
assertEq(Atomics.compareExchange(a, -1, 0, 1), undefined); // out of range => undefined, no effect
|
||||
assertEq(a[0], 0);
|
||||
a[0] = 0;
|
||||
var msg = /out-of-range index for atomic access/;
|
||||
|
||||
assertEq(Atomics.compareExchange(a, "hi", 0, 1), undefined); // invalid => undefined, no effect
|
||||
assertErrorMessage(() => Atomics.compareExchange(a, -1, 0, 1), RangeError, msg);
|
||||
assertEq(a[0], 0);
|
||||
a[0] = 0;
|
||||
|
||||
assertEq(Atomics.compareExchange(a, a.length + 5, 0, 1), undefined); // out of range => undefined, no effect
|
||||
assertErrorMessage(() => Atomics.compareExchange(a, "hi", 0, 1), RangeError, msg);
|
||||
assertEq(a[0], 0);
|
||||
|
||||
assertErrorMessage(() => Atomics.compareExchange(a, a.length + 5, 0, 1), RangeError, msg);
|
||||
assertEq(a[0], 0);
|
||||
|
||||
assertErrorMessage(() => Atomics.compareExchange(a, globlength, 0, 1), RangeError, msg);
|
||||
assertEq(a[0], 0);
|
||||
}
|
||||
|
||||
|
@ -479,7 +486,9 @@ function runTests() {
|
|||
CLONE(testTypeBinop)(v32, Atomics.xor);
|
||||
|
||||
// Test out-of-range references
|
||||
globlength = v8.length + 5;
|
||||
CLONE(testRangeCAS)(v8);
|
||||
globlength = v32.length + 5;
|
||||
CLONE(testRangeCAS)(v32);
|
||||
|
||||
// Test extreme values
|
||||
|
|
|
@ -25,13 +25,14 @@ withJitOptions(Opts_Ion2NoOffthreadCompilation, function () {
|
|||
}
|
||||
};
|
||||
|
||||
g.eval("" + function f(d, x) { "use strict"; g(d, x); });
|
||||
g.eval("" + function f(d, x) {
|
||||
"use strict";
|
||||
eval("g(d, x)"); // `eval` to avoid inlining g.
|
||||
});
|
||||
|
||||
g.eval("" + function g(d, x) {
|
||||
"use strict";
|
||||
for (var i = 0; i < 200; i++);
|
||||
// Hack to prevent inlining.
|
||||
function inner() { i = 42; };
|
||||
toggle(d);
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
function f() {
|
||||
assertEq(Array.isArray(10), false);
|
||||
assertEq(Array.isArray([]), true);
|
||||
assertEq(Array.isArray(Math), false);
|
||||
|
||||
var objs = [{}, []];
|
||||
for (var i=0; i<objs.length; i++)
|
||||
assertEq(Array.isArray(objs[i]), i === 1);
|
||||
var arr = [[], [], 1];
|
||||
for (var i=0; i<arr.length; i++)
|
||||
assertEq(Array.isArray(arr[i]), i < 2);
|
||||
}
|
||||
f();
|
||||
f();
|
||||
f();
|
|
@ -253,7 +253,7 @@ bool
|
|||
jit::EnsureHasScopeObjects(JSContext* cx, AbstractFramePtr fp)
|
||||
{
|
||||
if (fp.isFunctionFrame() &&
|
||||
fp.fun()->isHeavyweight() &&
|
||||
fp.fun()->needsCallObject() &&
|
||||
!fp.hasCallObj())
|
||||
{
|
||||
return fp.initFunctionScopeObjects(cx);
|
||||
|
|
|
@ -685,7 +685,7 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
|
|||
Value v = iter.read();
|
||||
if (v.isObject()) {
|
||||
scopeChain = &v.toObject();
|
||||
if (fun && fun->isHeavyweight())
|
||||
if (fun && fun->needsCallObject())
|
||||
flags |= BaselineFrame::HAS_CALL_OBJ;
|
||||
} else {
|
||||
MOZ_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
|
||||
|
|
|
@ -123,7 +123,7 @@ BaselineCompiler::compile()
|
|||
JSObject* templateScope = nullptr;
|
||||
if (script->functionNonDelazifying()) {
|
||||
RootedFunction fun(cx, script->functionNonDelazifying());
|
||||
if (fun->isHeavyweight()) {
|
||||
if (fun->needsCallObject()) {
|
||||
RootedScript scriptRoot(cx, script);
|
||||
templateScope = CallObject::createTemplateObject(cx, scriptRoot, gc::TenuredHeap);
|
||||
if (!templateScope)
|
||||
|
@ -630,13 +630,13 @@ BaselineCompiler::emitDebugPrologue()
|
|||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*StrictEvalPrologueFn)(JSContext*, BaselineFrame*);
|
||||
static const VMFunction StrictEvalPrologueInfo =
|
||||
FunctionInfo<StrictEvalPrologueFn>(jit::StrictEvalPrologue);
|
||||
typedef bool (*InitStrictEvalScopeObjectsFn)(JSContext*, BaselineFrame*);
|
||||
static const VMFunction InitStrictEvalScopeObjectsInfo =
|
||||
FunctionInfo<InitStrictEvalScopeObjectsFn>(jit::InitStrictEvalScopeObjects);
|
||||
|
||||
typedef bool (*HeavyweightFunPrologueFn)(JSContext*, BaselineFrame*);
|
||||
static const VMFunction HeavyweightFunPrologueInfo =
|
||||
FunctionInfo<HeavyweightFunPrologueFn>(jit::HeavyweightFunPrologue);
|
||||
typedef bool (*InitFunctionScopeObjectsFn)(JSContext*, BaselineFrame*);
|
||||
static const VMFunction InitFunctionScopeObjectsInfo =
|
||||
FunctionInfo<InitFunctionScopeObjectsFn>(jit::InitFunctionScopeObjects);
|
||||
|
||||
bool
|
||||
BaselineCompiler::initScopeChain()
|
||||
|
@ -648,7 +648,7 @@ BaselineCompiler::initScopeChain()
|
|||
RootedFunction fun(cx, function());
|
||||
if (fun) {
|
||||
// Use callee->environment as scope chain. Note that we do
|
||||
// this also for heavy-weight functions, so that the scope
|
||||
// this also for needsCallObject functions, so that the scope
|
||||
// chain slot is properly initialized if the call triggers GC.
|
||||
Register callee = R0.scratchReg();
|
||||
Register scope = R1.scratchReg();
|
||||
|
@ -656,14 +656,14 @@ BaselineCompiler::initScopeChain()
|
|||
masm.loadPtr(Address(callee, JSFunction::offsetOfEnvironment()), scope);
|
||||
masm.storePtr(scope, frame.addressOfScopeChain());
|
||||
|
||||
if (fun->isHeavyweight()) {
|
||||
if (fun->needsCallObject()) {
|
||||
// Call into the VM to create a new call object.
|
||||
prepareVMCall();
|
||||
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
if (!callVMNonOp(HeavyweightFunPrologueInfo, phase))
|
||||
if (!callVMNonOp(InitFunctionScopeObjectsInfo, phase))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
@ -677,7 +677,7 @@ BaselineCompiler::initScopeChain()
|
|||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
if (!callVMNonOp(StrictEvalPrologueInfo, phase))
|
||||
if (!callVMNonOp(InitStrictEvalScopeObjectsInfo, phase))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2621,9 +2621,9 @@ BaselineCompiler::emit_JSOP_SETLOCAL()
|
|||
bool
|
||||
BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get)
|
||||
{
|
||||
// Fast path: the script does not use |arguments|, or is strict. In strict
|
||||
// mode, formals do not alias the arguments object.
|
||||
if (!script->argumentsHasVarBinding() || script->strict()) {
|
||||
// Fast path: the script does not use |arguments| or formals don't
|
||||
// alias the arguments object.
|
||||
if (!script->argumentsAliasesFormals()) {
|
||||
if (get) {
|
||||
frame.pushArg(arg);
|
||||
} else {
|
||||
|
|
|
@ -89,7 +89,7 @@ inline CallObject&
|
|||
BaselineFrame::callObj() const
|
||||
{
|
||||
MOZ_ASSERT(hasCallObj());
|
||||
MOZ_ASSERT(fun()->isHeavyweight());
|
||||
MOZ_ASSERT(fun()->needsCallObject());
|
||||
|
||||
JSObject* obj = scopeChain();
|
||||
while (!obj->is<CallObject>())
|
||||
|
|
|
@ -109,7 +109,7 @@ BaselineFrame::copyRawFrameSlots(AutoValueVector* vec) const
|
|||
}
|
||||
|
||||
bool
|
||||
BaselineFrame::strictEvalPrologue(JSContext* cx)
|
||||
BaselineFrame::initStrictEvalScopeObjects(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(isStrictEvalFrame());
|
||||
|
||||
|
@ -122,17 +122,11 @@ BaselineFrame::strictEvalPrologue(JSContext* cx)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineFrame::heavyweightFunPrologue(JSContext* cx)
|
||||
{
|
||||
return initFunctionScopeObjects(cx);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineFrame::initFunctionScopeObjects(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(isNonEvalFunctionFrame());
|
||||
MOZ_ASSERT(fun()->isHeavyweight());
|
||||
MOZ_ASSERT(fun()->needsCallObject());
|
||||
|
||||
CallObject* callobj = CallObject::createForFunction(cx, this);
|
||||
if (!callobj)
|
||||
|
|
|
@ -279,8 +279,7 @@ class BaselineFrame
|
|||
inline void popBlock(JSContext* cx);
|
||||
inline bool freshenBlock(JSContext* cx);
|
||||
|
||||
bool strictEvalPrologue(JSContext* cx);
|
||||
bool heavyweightFunPrologue(JSContext* cx);
|
||||
bool initStrictEvalScopeObjects(JSContext* cx);
|
||||
bool initFunctionScopeObjects(JSContext* cx);
|
||||
|
||||
void initArgsObjUnchecked(ArgumentsObject& argsobj) {
|
||||
|
|
|
@ -2838,9 +2838,9 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_
|
|||
|
||||
// Check for ArgumentsObj[int] accesses
|
||||
if (obj->is<ArgumentsObject>() && rhs.isInt32()) {
|
||||
ICGetElem_Arguments::Which which = ICGetElem_Arguments::Normal;
|
||||
if (obj->is<StrictArgumentsObject>())
|
||||
which = ICGetElem_Arguments::Strict;
|
||||
ICGetElem_Arguments::Which which = ICGetElem_Arguments::Mapped;
|
||||
if (obj->is<UnmappedArgumentsObject>())
|
||||
which = ICGetElem_Arguments::Unmapped;
|
||||
if (!ArgumentsGetElemStubExists(stub, which)) {
|
||||
JitSpew(JitSpew_BaselineIC, " Generating GetElem(ArgsObj[Int32]) stub");
|
||||
ICGetElem_Arguments::Compiler compiler(
|
||||
|
@ -3829,11 +3829,12 @@ ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(which_ == ICGetElem_Arguments::Strict ||
|
||||
which_ == ICGetElem_Arguments::Normal);
|
||||
MOZ_ASSERT(which_ == ICGetElem_Arguments::Mapped ||
|
||||
which_ == ICGetElem_Arguments::Unmapped);
|
||||
|
||||
bool isStrict = which_ == ICGetElem_Arguments::Strict;
|
||||
const Class* clasp = isStrict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_;
|
||||
const Class* clasp = (which_ == ICGetElem_Arguments::Mapped)
|
||||
? &MappedArgumentsObject::class_
|
||||
: &UnmappedArgumentsObject::class_;
|
||||
|
||||
AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
|
||||
Register scratchReg = regs.takeAny();
|
||||
|
@ -5865,7 +5866,7 @@ TryAttachMagicArgumentsGetPropStub(JSContext* cx, JSScript* script, ICGetProp_Fa
|
|||
|
||||
// Try handling arguments.callee on optimized arguments.
|
||||
if (name == cx->names().callee) {
|
||||
MOZ_ASSERT(!script->strict());
|
||||
MOZ_ASSERT(script->hasMappedArgsObj());
|
||||
|
||||
JitSpew(JitSpew_BaselineIC, " Generating GetProp(MagicArgs.callee) stub");
|
||||
|
||||
|
@ -5947,10 +5948,10 @@ TryAttachLengthStub(JSContext* cx, JSScript* script, ICGetProp_Fallback* stub, H
|
|||
|
||||
if (obj->is<ArgumentsObject>() && res.isInt32()) {
|
||||
JitSpew(JitSpew_BaselineIC, " Generating GetProp(ArgsObj.length %s) stub",
|
||||
obj->is<StrictArgumentsObject>() ? "Strict" : "Normal");
|
||||
ICGetProp_ArgumentsLength::Which which = ICGetProp_ArgumentsLength::Normal;
|
||||
if (obj->is<StrictArgumentsObject>())
|
||||
which = ICGetProp_ArgumentsLength::Strict;
|
||||
obj->is<MappedArgumentsObject>() ? "Mapped" : "Unmapped");
|
||||
ICGetProp_ArgumentsLength::Which which = ICGetProp_ArgumentsLength::Mapped;
|
||||
if (obj->is<UnmappedArgumentsObject>())
|
||||
which = ICGetProp_ArgumentsLength::Unmapped;
|
||||
ICGetProp_ArgumentsLength::Compiler compiler(cx, which);
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
|
@ -6479,7 +6480,7 @@ ComputeGetPropResult(JSContext* cx, BaselineFrame* frame, JSOp op, HandlePropert
|
|||
res.setInt32(frame->numActualArgs());
|
||||
} else {
|
||||
MOZ_ASSERT(name == cx->names().callee);
|
||||
MOZ_ASSERT(!frame->script()->strict());
|
||||
MOZ_ASSERT(frame->script()->hasMappedArgsObj());
|
||||
res.setObject(*frame->callee());
|
||||
}
|
||||
} else {
|
||||
|
@ -7417,11 +7418,12 @@ ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
EmitStubGuardFailure(masm);
|
||||
return true;
|
||||
}
|
||||
MOZ_ASSERT(which_ == ICGetProp_ArgumentsLength::Strict ||
|
||||
which_ == ICGetProp_ArgumentsLength::Normal);
|
||||
MOZ_ASSERT(which_ == ICGetProp_ArgumentsLength::Mapped ||
|
||||
which_ == ICGetProp_ArgumentsLength::Unmapped);
|
||||
|
||||
bool isStrict = which_ == ICGetProp_ArgumentsLength::Strict;
|
||||
const Class* clasp = isStrict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_;
|
||||
const Class* clasp = (which_ == ICGetProp_ArgumentsLength::Mapped)
|
||||
? &MappedArgumentsObject::class_
|
||||
: &UnmappedArgumentsObject::class_;
|
||||
|
||||
Register scratchReg = R1.scratchReg();
|
||||
|
||||
|
|
|
@ -1768,7 +1768,7 @@ class ICGetElem_Arguments : public ICMonitoredStub
|
|||
{
|
||||
friend class ICStubSpace;
|
||||
public:
|
||||
enum Which { Normal, Strict, Magic };
|
||||
enum Which { Mapped, Unmapped, Magic };
|
||||
|
||||
private:
|
||||
ICGetElem_Arguments(JitCode* stubCode, ICStub* firstMonitorStub, Which which)
|
||||
|
@ -3519,7 +3519,7 @@ class ICGetProp_ArgumentsLength : public ICStub
|
|||
{
|
||||
friend class ICStubSpace;
|
||||
public:
|
||||
enum Which { Normal, Strict, Magic };
|
||||
enum Which { Mapped, Unmapped, Magic };
|
||||
|
||||
protected:
|
||||
explicit ICGetProp_ArgumentsLength(JitCode* stubCode)
|
||||
|
|
|
@ -121,8 +121,9 @@ struct BaselineScript
|
|||
// Code pointer containing the actual method.
|
||||
RelocatablePtrJitCode method_;
|
||||
|
||||
// For heavyweight scripts, template objects to use for the call object and
|
||||
// decl env object (linked via the call object's enclosing scope).
|
||||
// For functions with a call object, template objects to use for the call
|
||||
// object and decl env object (linked via the call object's enclosing
|
||||
// scope).
|
||||
RelocatablePtrObject templateScope_;
|
||||
|
||||
// Allocated space for fallback stubs.
|
||||
|
|
|
@ -46,9 +46,10 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
|
|||
if (!infos_.growByUninitialized(script_->length()))
|
||||
return false;
|
||||
|
||||
// We need a scope chain if the function is heavyweight.
|
||||
// Initialize the scope chain slot if either the function needs a CallObject
|
||||
// or the script uses the scope chain. The latter case is handled below.
|
||||
usesScopeChain_ = (script_->functionDelazifying() &&
|
||||
script_->functionDelazifying()->isHeavyweight());
|
||||
script_->functionDelazifying()->needsCallObject());
|
||||
MOZ_ASSERT_IF(script_->hasAnyAliasedBindings(), usesScopeChain_);
|
||||
|
||||
jsbytecode* end = script_->codeEnd();
|
||||
|
|
|
@ -460,7 +460,7 @@ class CompileInfo
|
|||
return scriptNeedsArgsObj_;
|
||||
}
|
||||
bool argsObjAliasesFormals() const {
|
||||
return scriptNeedsArgsObj_ && !script()->strict();
|
||||
return scriptNeedsArgsObj_ && script()->hasMappedArgsObj();
|
||||
}
|
||||
|
||||
AnalysisMode analysisMode() const {
|
||||
|
@ -493,7 +493,7 @@ class CompileInfo
|
|||
if (slot == thisSlot())
|
||||
return true;
|
||||
|
||||
if (funMaybeLazy()->isHeavyweight() && slot == scopeChainSlot())
|
||||
if (funMaybeLazy()->needsCallObject() && slot == scopeChainSlot())
|
||||
return true;
|
||||
|
||||
// If the function may need an arguments object, then make sure to
|
||||
|
|
|
@ -3445,10 +3445,11 @@ ArgumentsUseCanBeLazy(JSContext* cx, JSScript* script, MInstruction* ins, size_t
|
|||
return true;
|
||||
|
||||
// arguments.length length can read fp->numActualArgs() directly.
|
||||
// arguments.callee can read fp->callee() directly in non-strict code.
|
||||
// arguments.callee can read fp->callee() directly if the arguments object
|
||||
// is mapped.
|
||||
if (ins->isCallGetProperty() && index == 0 &&
|
||||
(ins->toCallGetProperty()->name() == cx->names().length ||
|
||||
(!script->strict() && ins->toCallGetProperty()->name() == cx->names().callee)))
|
||||
(script->hasMappedArgsObj() && ins->toCallGetProperty()->name() == cx->names().callee)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -524,11 +524,6 @@ IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
|
|||
return DontInline(inlineScript, "Common inlining path");
|
||||
}
|
||||
|
||||
if (target->isHeavyweight()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineHeavyweight);
|
||||
return DontInline(inlineScript, "Heavyweight function");
|
||||
}
|
||||
|
||||
if (inlineScript->uninlineable()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
|
||||
return DontInline(inlineScript, "Uninlineable script");
|
||||
|
@ -1223,7 +1218,7 @@ IonBuilder::initScopeChain(MDefinition* callee)
|
|||
// This reproduce what is done in CallObject::createForFunction. Skip
|
||||
// this for analyses, as the script might not have a baseline script
|
||||
// with template objects yet.
|
||||
if (fun->isHeavyweight() && !info().isAnalysis()) {
|
||||
if (fun->needsCallObject() && !info().isAnalysis()) {
|
||||
if (fun->isNamedLambda()) {
|
||||
scope = createDeclEnvObject(callee, scope);
|
||||
if (!scope)
|
||||
|
@ -10633,7 +10628,7 @@ IonBuilder::getPropTryArgumentsCallee(bool* emitted, MDefinition* obj, PropertyN
|
|||
if (name != names().callee)
|
||||
return true;
|
||||
|
||||
MOZ_ASSERT(!script()->strict());
|
||||
MOZ_ASSERT(script()->hasMappedArgsObj());
|
||||
|
||||
obj->setImplicitlyUsedUnchecked();
|
||||
current->push(getCallee());
|
||||
|
|
|
@ -752,6 +752,7 @@ class IonBuilder
|
|||
|
||||
// Array natives.
|
||||
InliningStatus inlineArray(CallInfo& callInfo);
|
||||
InliningStatus inlineArrayIsArray(CallInfo& callInfo);
|
||||
InliningStatus inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode);
|
||||
InliningStatus inlineArrayPush(CallInfo& callInfo);
|
||||
InliningStatus inlineArrayConcat(CallInfo& callInfo);
|
||||
|
|
|
@ -1858,7 +1858,7 @@ GetPropertyIC::tryAttachArgumentsLength(JSContext* cx, HandleScript outerScript,
|
|||
if (!(outputType == MIRType_Value || outputType == MIRType_Int32))
|
||||
return true;
|
||||
|
||||
if (hasArgumentsLengthStub(obj->is<StrictArgumentsObject>()))
|
||||
if (hasArgumentsLengthStub(obj->is<MappedArgumentsObject>()))
|
||||
return true;
|
||||
|
||||
*emitted = true;
|
||||
|
@ -1878,10 +1878,7 @@ GetPropertyIC::tryAttachArgumentsLength(JSContext* cx, HandleScript outerScript,
|
|||
}
|
||||
MOZ_ASSERT(object() != tmpReg);
|
||||
|
||||
const Class* clasp = obj->is<StrictArgumentsObject>() ? &StrictArgumentsObject::class_
|
||||
: &NormalArgumentsObject::class_;
|
||||
|
||||
masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, clasp, &failures);
|
||||
masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, obj->getClass(), &failures);
|
||||
|
||||
// Get initial ArgsObj length value, test if length has been overridden.
|
||||
masm.unboxInt32(Address(object(), ArgumentsObject::getInitialLengthSlotOffset()), tmpReg);
|
||||
|
@ -1901,16 +1898,16 @@ GetPropertyIC::tryAttachArgumentsLength(JSContext* cx, HandleScript outerScript,
|
|||
masm.bind(&failures);
|
||||
attacher.jumpNextStub(masm);
|
||||
|
||||
if (obj->is<StrictArgumentsObject>()) {
|
||||
MOZ_ASSERT(!hasStrictArgumentsLengthStub_);
|
||||
hasStrictArgumentsLengthStub_ = true;
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj length (strict)",
|
||||
if (obj->is<UnmappedArgumentsObject>()) {
|
||||
MOZ_ASSERT(!hasUnmappedArgumentsLengthStub_);
|
||||
hasUnmappedArgumentsLengthStub_ = true;
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj length (unmapped)",
|
||||
JS::TrackedOutcome::ICGetPropStub_ArgumentsLength);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!hasNormalArgumentsLengthStub_);
|
||||
hasNormalArgumentsLengthStub_ = true;
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj length (normal)",
|
||||
MOZ_ASSERT(!hasMappedArgumentsLengthStub_);
|
||||
hasMappedArgumentsLengthStub_ = true;
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj length (mapped)",
|
||||
JS::TrackedOutcome::ICGetPropStub_ArgumentsLength);
|
||||
}
|
||||
|
||||
|
@ -2028,8 +2025,8 @@ GetPropertyIC::reset(ReprotectCode reprotect)
|
|||
IonCache::reset(reprotect);
|
||||
hasTypedArrayLengthStub_ = false;
|
||||
hasSharedTypedArrayLengthStub_ = false;
|
||||
hasStrictArgumentsLengthStub_ = false;
|
||||
hasNormalArgumentsLengthStub_ = false;
|
||||
hasMappedArgumentsLengthStub_ = false;
|
||||
hasUnmappedArgumentsLengthStub_ = false;
|
||||
hasGenericProxyStub_ = false;
|
||||
}
|
||||
|
||||
|
@ -3931,10 +3928,7 @@ GetElementIC::attachArgumentsElement(JSContext* cx, HandleScript outerScript, Io
|
|||
Register tmpReg = output().scratchReg().gpr();
|
||||
MOZ_ASSERT(tmpReg != InvalidReg);
|
||||
|
||||
const Class* clasp = obj->is<StrictArgumentsObject>() ? &StrictArgumentsObject::class_
|
||||
: &NormalArgumentsObject::class_;
|
||||
|
||||
masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, clasp, &failures);
|
||||
masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, obj->getClass(), &failures);
|
||||
|
||||
// Get initial ArgsObj length value, test if length has been overridden.
|
||||
masm.unboxInt32(Address(object(), ArgumentsObject::getInitialLengthSlotOffset()), tmpReg);
|
||||
|
@ -4016,18 +4010,17 @@ GetElementIC::attachArgumentsElement(JSContext* cx, HandleScript outerScript, Io
|
|||
masm.bind(&failures);
|
||||
attacher.jumpNextStub(masm);
|
||||
|
||||
|
||||
if (obj->is<StrictArgumentsObject>()) {
|
||||
MOZ_ASSERT(!hasStrictArgumentsStub_);
|
||||
hasStrictArgumentsStub_ = true;
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (strict)",
|
||||
JS::TrackedOutcome::ICGetElemStub_ArgsElementStrict);
|
||||
if (obj->is<UnmappedArgumentsObject>()) {
|
||||
MOZ_ASSERT(!hasUnmappedArgumentsStub_);
|
||||
hasUnmappedArgumentsStub_ = true;
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (unmapped)",
|
||||
JS::TrackedOutcome::ICGetElemStub_ArgsElementUnmapped);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!hasNormalArgumentsStub_);
|
||||
hasNormalArgumentsStub_ = true;
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (normal)",
|
||||
JS::TrackedOutcome::ICGetElemStub_ArgsElement);
|
||||
MOZ_ASSERT(!hasMappedArgumentsStub_);
|
||||
hasMappedArgumentsStub_ = true;
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (mapped)",
|
||||
JS::TrackedOutcome::ICGetElemStub_ArgsElementMapped);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -4058,7 +4051,7 @@ GetElementIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
|
|||
bool attachedStub = false;
|
||||
if (cache.canAttachStub()) {
|
||||
if (IsOptimizableArgumentsObjectForGetElem(obj, idval) &&
|
||||
!cache.hasArgumentsStub(obj->is<StrictArgumentsObject>()) &&
|
||||
!cache.hasArgumentsStub(obj->is<MappedArgumentsObject>()) &&
|
||||
(cache.index().hasValue() ||
|
||||
cache.index().type() == MIRType_Int32) &&
|
||||
(cache.output().hasValue() || !cache.output().typedReg().isFloat()))
|
||||
|
@ -4116,8 +4109,8 @@ GetElementIC::reset(ReprotectCode reprotect)
|
|||
{
|
||||
IonCache::reset(reprotect);
|
||||
hasDenseStub_ = false;
|
||||
hasStrictArgumentsStub_ = false;
|
||||
hasNormalArgumentsStub_ = false;
|
||||
hasMappedArgumentsStub_ = false;
|
||||
hasUnmappedArgumentsStub_ = false;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
|
@ -392,8 +392,8 @@ class GetPropertyIC : public IonCache
|
|||
bool monitoredResult_ : 1;
|
||||
bool hasTypedArrayLengthStub_ : 1;
|
||||
bool hasSharedTypedArrayLengthStub_ : 1;
|
||||
bool hasStrictArgumentsLengthStub_ : 1;
|
||||
bool hasNormalArgumentsLengthStub_ : 1;
|
||||
bool hasMappedArgumentsLengthStub_ : 1;
|
||||
bool hasUnmappedArgumentsLengthStub_ : 1;
|
||||
bool hasGenericProxyStub_ : 1;
|
||||
|
||||
public:
|
||||
|
@ -410,8 +410,8 @@ class GetPropertyIC : public IonCache
|
|||
monitoredResult_(monitoredResult),
|
||||
hasTypedArrayLengthStub_(false),
|
||||
hasSharedTypedArrayLengthStub_(false),
|
||||
hasStrictArgumentsLengthStub_(false),
|
||||
hasNormalArgumentsLengthStub_(false),
|
||||
hasMappedArgumentsLengthStub_(false),
|
||||
hasUnmappedArgumentsLengthStub_(false),
|
||||
hasGenericProxyStub_(false)
|
||||
{
|
||||
}
|
||||
|
@ -435,8 +435,8 @@ class GetPropertyIC : public IonCache
|
|||
bool hasAnyTypedArrayLengthStub(HandleObject obj) const {
|
||||
return obj->is<TypedArrayObject>() ? hasTypedArrayLengthStub_ : hasSharedTypedArrayLengthStub_;
|
||||
}
|
||||
bool hasArgumentsLengthStub(bool strict) const {
|
||||
return strict ? hasStrictArgumentsLengthStub_ : hasNormalArgumentsLengthStub_;
|
||||
bool hasArgumentsLengthStub(bool mapped) const {
|
||||
return mapped ? hasMappedArgumentsLengthStub_ : hasUnmappedArgumentsLengthStub_;
|
||||
}
|
||||
bool hasGenericProxyStub() const {
|
||||
return hasGenericProxyStub_;
|
||||
|
@ -622,8 +622,8 @@ class GetElementIC : public IonCache
|
|||
bool monitoredResult_ : 1;
|
||||
bool allowDoubleResult_ : 1;
|
||||
bool hasDenseStub_ : 1;
|
||||
bool hasStrictArgumentsStub_ : 1;
|
||||
bool hasNormalArgumentsStub_ : 1;
|
||||
bool hasMappedArgumentsStub_ : 1;
|
||||
bool hasUnmappedArgumentsStub_ : 1;
|
||||
|
||||
size_t failedUpdates_;
|
||||
|
||||
|
@ -639,8 +639,8 @@ class GetElementIC : public IonCache
|
|||
monitoredResult_(monitoredResult),
|
||||
allowDoubleResult_(allowDoubleResult),
|
||||
hasDenseStub_(false),
|
||||
hasStrictArgumentsStub_(false),
|
||||
hasNormalArgumentsStub_(false),
|
||||
hasMappedArgumentsStub_(false),
|
||||
hasUnmappedArgumentsStub_(false),
|
||||
failedUpdates_(0)
|
||||
{
|
||||
}
|
||||
|
@ -667,8 +667,8 @@ class GetElementIC : public IonCache
|
|||
bool hasDenseStub() const {
|
||||
return hasDenseStub_;
|
||||
}
|
||||
bool hasArgumentsStub(bool strict) const {
|
||||
return strict ? hasStrictArgumentsStub_ : hasNormalArgumentsStub_;
|
||||
bool hasArgumentsStub(bool mapped) const {
|
||||
return mapped ? hasMappedArgumentsStub_ : hasUnmappedArgumentsStub_;
|
||||
}
|
||||
void setHasDenseStub() {
|
||||
MOZ_ASSERT(!hasDenseStub());
|
||||
|
|
|
@ -2531,19 +2531,19 @@ InlineFrameIterator::computeScopeChain(Value scopeChainValue, MaybeReadFallback&
|
|||
if (hasCallObj) {
|
||||
if (fallback.canRecoverResults()) {
|
||||
RootedObject obj(fallback.maybeCx, &scopeChainValue.toObject());
|
||||
*hasCallObj = isFunctionFrame() && callee(fallback)->isHeavyweight();
|
||||
*hasCallObj = isFunctionFrame() && callee(fallback)->needsCallObject();
|
||||
return obj;
|
||||
} else {
|
||||
JS::AutoSuppressGCAnalysis nogc; // If we cannot recover then we cannot GC.
|
||||
*hasCallObj = isFunctionFrame() && callee(fallback)->isHeavyweight();
|
||||
*hasCallObj = isFunctionFrame() && callee(fallback)->needsCallObject();
|
||||
}
|
||||
}
|
||||
|
||||
return &scopeChainValue.toObject();
|
||||
}
|
||||
|
||||
// Note we can hit this case even for heavyweight functions, in case we
|
||||
// are walking the frame during the function prologue, before the scope
|
||||
// Note we can hit this case even for functions with a CallObject, in case
|
||||
// we are walking the frame during the function prologue, before the scope
|
||||
// chain has been initialized.
|
||||
if (isFunctionFrame())
|
||||
return callee(fallback)->environment();
|
||||
|
|
|
@ -80,6 +80,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
|||
// Array natives.
|
||||
if (native == ArrayConstructor)
|
||||
return inlineArray(callInfo);
|
||||
if (native == js::array_isArray)
|
||||
return inlineArrayIsArray(callInfo);
|
||||
if (native == js::array_pop)
|
||||
return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
|
||||
if (native == js::array_shift)
|
||||
|
@ -618,6 +620,40 @@ IonBuilder::inlineArray(CallInfo& callInfo)
|
|||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArrayIsArray(CallInfo& callInfo)
|
||||
{
|
||||
if (callInfo.constructing() || callInfo.argc() != 1) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_Boolean)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MDefinition* arg = callInfo.getArg(0);
|
||||
|
||||
bool isArray;
|
||||
if (!arg->mightBeType(MIRType_Object)) {
|
||||
isArray = false;
|
||||
} else {
|
||||
if (arg->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
TemporaryTypeSet* types = arg->resultTypeSet();
|
||||
const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
|
||||
if (!clasp || clasp->isProxy())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
isArray = (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_);
|
||||
}
|
||||
|
||||
pushConstant(BooleanValue(isArray));
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
||||
{
|
||||
|
|
|
@ -13174,11 +13174,19 @@ class MAsmJSHeapAccess
|
|||
Scalar::Type accessType_ : 8;
|
||||
bool needsBoundsCheck_;
|
||||
unsigned numSimdElems_;
|
||||
MemoryBarrierBits barrierBefore_;
|
||||
MemoryBarrierBits barrierAfter_;
|
||||
|
||||
public:
|
||||
MAsmJSHeapAccess(Scalar::Type accessType, bool needsBoundsCheck, unsigned numSimdElems = 0)
|
||||
: offset_(0), accessType_(accessType),
|
||||
needsBoundsCheck_(needsBoundsCheck), numSimdElems_(numSimdElems)
|
||||
MAsmJSHeapAccess(Scalar::Type accessType, bool needsBoundsCheck, unsigned numSimdElems = 0,
|
||||
MemoryBarrierBits barrierBefore = MembarNobits,
|
||||
MemoryBarrierBits barrierAfter = MembarNobits)
|
||||
: offset_(0),
|
||||
accessType_(accessType),
|
||||
needsBoundsCheck_(needsBoundsCheck),
|
||||
numSimdElems_(numSimdElems),
|
||||
barrierBefore_(barrierBefore),
|
||||
barrierAfter_(barrierAfter)
|
||||
{
|
||||
MOZ_ASSERT(numSimdElems <= ScalarTypeToLength(accessType));
|
||||
}
|
||||
|
@ -13198,6 +13206,9 @@ class MAsmJSHeapAccess
|
|||
MOZ_ASSERT(o >= 0);
|
||||
offset_ = o;
|
||||
}
|
||||
MemoryBarrierBits barrierBefore() const { return barrierBefore_; }
|
||||
MemoryBarrierBits barrierAfter() const { return barrierAfter_; }
|
||||
bool isAtomicAccess() const { return (barrierBefore_|barrierAfter_) != MembarNobits; }
|
||||
};
|
||||
|
||||
class MAsmJSLoadHeap
|
||||
|
@ -13205,15 +13216,10 @@ class MAsmJSLoadHeap
|
|||
public MAsmJSHeapAccess,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
MemoryBarrierBits barrierBefore_;
|
||||
MemoryBarrierBits barrierAfter_;
|
||||
|
||||
MAsmJSLoadHeap(Scalar::Type accessType, MDefinition* ptr, bool needsBoundsCheck,
|
||||
unsigned numSimdElems, MemoryBarrierBits before, MemoryBarrierBits after)
|
||||
: MUnaryInstruction(ptr),
|
||||
MAsmJSHeapAccess(accessType, needsBoundsCheck, numSimdElems),
|
||||
barrierBefore_(before),
|
||||
barrierAfter_(after)
|
||||
MAsmJSHeapAccess(accessType, needsBoundsCheck, numSimdElems, before, after)
|
||||
{
|
||||
if (before|after)
|
||||
setGuard(); // Not removable
|
||||
|
@ -13262,14 +13268,12 @@ class MAsmJSLoadHeap
|
|||
|
||||
MDefinition* ptr() const { return getOperand(0); }
|
||||
void replacePtr(MDefinition* newPtr) { replaceOperand(0, newPtr); }
|
||||
MemoryBarrierBits barrierBefore() const { return barrierBefore_; }
|
||||
MemoryBarrierBits barrierAfter() const { return barrierAfter_; }
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override;
|
||||
AliasSet getAliasSet() const override {
|
||||
// When a barrier is needed make the instruction effectful by
|
||||
// giving it a "store" effect.
|
||||
if (barrierBefore_|barrierAfter_)
|
||||
if (isAtomicAccess())
|
||||
return AliasSet::Store(AliasSet::AsmJSHeap);
|
||||
return AliasSet::Load(AliasSet::AsmJSHeap);
|
||||
}
|
||||
|
@ -13281,15 +13285,10 @@ class MAsmJSStoreHeap
|
|||
public MAsmJSHeapAccess,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
MemoryBarrierBits barrierBefore_;
|
||||
MemoryBarrierBits barrierAfter_;
|
||||
|
||||
MAsmJSStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v, bool needsBoundsCheck,
|
||||
unsigned numSimdElems, MemoryBarrierBits before, MemoryBarrierBits after)
|
||||
: MBinaryInstruction(ptr, v),
|
||||
MAsmJSHeapAccess(accessType, needsBoundsCheck, numSimdElems),
|
||||
barrierBefore_(before),
|
||||
barrierAfter_(after)
|
||||
MAsmJSHeapAccess(accessType, needsBoundsCheck, numSimdElems, before, after)
|
||||
{
|
||||
if (before|after)
|
||||
setGuard(); // Not removable
|
||||
|
@ -13311,8 +13310,6 @@ class MAsmJSStoreHeap
|
|||
MDefinition* ptr() const { return getOperand(0); }
|
||||
void replacePtr(MDefinition* newPtr) { replaceOperand(0, newPtr); }
|
||||
MDefinition* value() const { return getOperand(1); }
|
||||
MemoryBarrierBits barrierBefore() const { return barrierBefore_; }
|
||||
MemoryBarrierBits barrierAfter() const { return barrierAfter_; }
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::AsmJSHeap);
|
||||
|
|
|
@ -151,13 +151,6 @@ class MIRGenerator
|
|||
// Traverses the graph to find if there's any SIMD instruction. Costful but
|
||||
// the value is cached, so don't worry about calling it several times.
|
||||
bool usesSimd();
|
||||
void initMinAsmJSHeapLength(uint32_t len) {
|
||||
MOZ_ASSERT(minAsmJSHeapLength_ == 0);
|
||||
minAsmJSHeapLength_ = len;
|
||||
}
|
||||
uint32_t minAsmJSHeapLength() const {
|
||||
return minAsmJSHeapLength_;
|
||||
}
|
||||
|
||||
bool modifiesFrameArguments() const {
|
||||
return modifiesFrameArguments_;
|
||||
|
@ -192,7 +185,6 @@ class MIRGenerator
|
|||
bool performsCall_;
|
||||
bool usesSimd_;
|
||||
bool usesSimdCached_;
|
||||
uint32_t minAsmJSHeapLength_;
|
||||
|
||||
// Keep track of whether frame arguments are modified during execution.
|
||||
// RegAlloc needs to know this as spilling values back to their register
|
||||
|
|
|
@ -38,7 +38,6 @@ MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOpti
|
|||
performsCall_(false),
|
||||
usesSimd_(false),
|
||||
usesSimdCached_(false),
|
||||
minAsmJSHeapLength_(0),
|
||||
modifiesFrameArguments_(false),
|
||||
instrumentedProfiling_(false),
|
||||
instrumentedProfilingIsCached_(false),
|
||||
|
@ -118,9 +117,10 @@ MIRGenerator::needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const
|
|||
// A heap access needs a bounds-check branch if we're not relying on signal
|
||||
// handlers to catch errors, and if it's not proven to be within bounds.
|
||||
// We use signal-handlers on x64, but on x86 there isn't enough address
|
||||
// space for a guard region.
|
||||
// space for a guard region. Also, on x64 the atomic loads and stores
|
||||
// can't (yet) use the signal handlers.
|
||||
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
if (usesSignalHandlersForAsmJSOOB_)
|
||||
if (usesSignalHandlersForAsmJSOOB_ && !access->isAtomicAccess())
|
||||
return false;
|
||||
#endif
|
||||
return access->needsBoundsCheck();
|
||||
|
@ -605,8 +605,9 @@ MBasicBlock::linkOsrValues(MStart* start)
|
|||
MOZ_ASSERT(def->isOsrValue() || def->isGetArgumentsObjectArg() || def->isConstant() ||
|
||||
def->isParameter());
|
||||
|
||||
// A constant Undefined can show up here for an argument slot when the function uses
|
||||
// a heavyweight argsobj, but the argument in question is stored on the scope chain.
|
||||
// A constant Undefined can show up here for an argument slot when
|
||||
// the function has an arguments object, but the argument in
|
||||
// question is stored on the scope chain.
|
||||
MOZ_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue());
|
||||
|
||||
if (def->isOsrValue())
|
||||
|
|
|
@ -2248,32 +2248,8 @@ RangeAnalysis::analyze()
|
|||
|
||||
// First pass at collecting range info - while the beta nodes are still
|
||||
// around and before truncation.
|
||||
for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
|
||||
for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++)
|
||||
iter->collectRangeInfoPreTrunc();
|
||||
|
||||
// Would have been nice to implement this using collectRangeInfoPreTrunc()
|
||||
// methods but it needs the minAsmJSHeapLength().
|
||||
if (mir->compilingAsmJS()) {
|
||||
uint32_t minHeapLength = mir->minAsmJSHeapLength();
|
||||
if (iter->isAsmJSLoadHeap()) {
|
||||
MAsmJSLoadHeap* ins = iter->toAsmJSLoadHeap();
|
||||
Range* range = ins->ptr()->range();
|
||||
uint32_t elemSize = TypedArrayElemSize(ins->accessType());
|
||||
if (range && range->hasInt32LowerBound() && range->lower() >= 0 &&
|
||||
range->hasInt32UpperBound() && uint32_t(range->upper()) + elemSize <= minHeapLength) {
|
||||
ins->removeBoundsCheck();
|
||||
}
|
||||
} else if (iter->isAsmJSStoreHeap()) {
|
||||
MAsmJSStoreHeap* ins = iter->toAsmJSStoreHeap();
|
||||
Range* range = ins->ptr()->range();
|
||||
uint32_t elemSize = TypedArrayElemSize(ins->accessType());
|
||||
if (range && range->hasInt32LowerBound() && range->lower() >= 0 &&
|
||||
range->hasInt32UpperBound() && uint32_t(range->upper()) + elemSize <= minHeapLength) {
|
||||
ins->removeBoundsCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -143,7 +143,7 @@ bool
|
|||
RematerializedFrame::initFunctionScopeObjects(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(isNonEvalFunctionFrame());
|
||||
MOZ_ASSERT(fun()->isHeavyweight());
|
||||
MOZ_ASSERT(fun()->needsCallObject());
|
||||
CallObject* callobj = CallObject::createForFunction(cx, this);
|
||||
if (!callobj)
|
||||
return false;
|
||||
|
|
|
@ -124,7 +124,7 @@ class RematerializedFrame
|
|||
bool initFunctionScopeObjects(JSContext* cx);
|
||||
|
||||
bool hasCallObj() const {
|
||||
MOZ_ASSERT(fun()->isHeavyweight());
|
||||
MOZ_ASSERT(fun()->needsCallObject());
|
||||
return hasCallObj_;
|
||||
}
|
||||
CallObject& callObj() const;
|
||||
|
|
|
@ -835,15 +835,15 @@ GeneratorThrowOrClose(JSContext* cx, BaselineFrame* frame, Handle<GeneratorObjec
|
|||
}
|
||||
|
||||
bool
|
||||
StrictEvalPrologue(JSContext* cx, BaselineFrame* frame)
|
||||
InitStrictEvalScopeObjects(JSContext* cx, BaselineFrame* frame)
|
||||
{
|
||||
return frame->strictEvalPrologue(cx);
|
||||
return frame->initStrictEvalScopeObjects(cx);
|
||||
}
|
||||
|
||||
bool
|
||||
HeavyweightFunPrologue(JSContext* cx, BaselineFrame* frame)
|
||||
InitFunctionScopeObjects(JSContext* cx, BaselineFrame* frame)
|
||||
{
|
||||
return frame->heavyweightFunPrologue(cx);
|
||||
return frame->initFunctionScopeObjects(cx);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -657,8 +657,8 @@ bool DebugAfterYield(JSContext* cx, BaselineFrame* frame);
|
|||
bool GeneratorThrowOrClose(JSContext* cx, BaselineFrame* frame, Handle<GeneratorObject*> genObj,
|
||||
HandleValue arg, uint32_t resumeKind);
|
||||
|
||||
bool StrictEvalPrologue(JSContext* cx, BaselineFrame* frame);
|
||||
bool HeavyweightFunPrologue(JSContext* cx, BaselineFrame* frame);
|
||||
bool InitStrictEvalScopeObjects(JSContext* cx, BaselineFrame* frame);
|
||||
bool InitFunctionScopeObjects(JSContext* cx, BaselineFrame* frame);
|
||||
|
||||
bool NewArgumentsObject(JSContext* cx, BaselineFrame* frame, MutableHandleValue res);
|
||||
|
||||
|
|
|
@ -1805,7 +1805,10 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
|
|||
}
|
||||
} else {
|
||||
Register d = ToRegister(ins->output());
|
||||
masm.ma_mov(Imm32(0), d, LeaveCC, Assembler::AboveOrEqual);
|
||||
if (mir->isAtomicAccess())
|
||||
masm.ma_b(gen->outOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
else
|
||||
masm.ma_mov(Imm32(0), d, LeaveCC, Assembler::AboveOrEqual);
|
||||
masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, d, Offset, Assembler::Below);
|
||||
}
|
||||
memoryBarrier(mir->barrierAfter());
|
||||
|
@ -1876,6 +1879,8 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
|
|||
else
|
||||
masm.ma_vstr(vd, HeapReg, ptrReg, 0, 0, Assembler::Below);
|
||||
} else {
|
||||
if (mir->isAtomicAccess())
|
||||
masm.ma_b(gen->outOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
|
||||
ToRegister(ins->value()), Offset, Assembler::Below);
|
||||
}
|
||||
|
@ -1896,26 +1901,17 @@ CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
|
|||
Register oldval = ToRegister(ins->oldValue());
|
||||
Register newval = ToRegister(ins->newValue());
|
||||
|
||||
Label rejoin;
|
||||
uint32_t maybeCmpOffset = 0;
|
||||
if (mir->needsBoundsCheck()) {
|
||||
Label goahead;
|
||||
BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
|
||||
Register out = ToRegister(ins->output());
|
||||
maybeCmpOffset = bo.getOffset();
|
||||
masm.ma_b(&goahead, Assembler::Below);
|
||||
memoryBarrier(MembarFull);
|
||||
masm.as_eor(out, out, O2Reg(out));
|
||||
masm.ma_b(&rejoin, Assembler::Always);
|
||||
masm.bind(&goahead);
|
||||
masm.ma_b(gen->outOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
}
|
||||
masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
|
||||
srcAddr, oldval, newval, InvalidReg,
|
||||
ToAnyRegister(ins->output()));
|
||||
if (rejoin.used()) {
|
||||
masm.bind(&rejoin);
|
||||
if (mir->needsBoundsCheck())
|
||||
masm.append(AsmJSHeapAccess(maybeCmpOffset));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1946,32 +1942,23 @@ CodeGeneratorARM::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
|
|||
{
|
||||
MAsmJSAtomicExchangeHeap* mir = ins->mir();
|
||||
Scalar::Type vt = mir->accessType();
|
||||
const LAllocation* ptr = ins->ptr();
|
||||
Register ptrReg = ToRegister(ptr);
|
||||
Register ptrReg = ToRegister(ins->ptr());
|
||||
Register value = ToRegister(ins->value());
|
||||
BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
|
||||
MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
|
||||
|
||||
Register value = ToRegister(ins->value());
|
||||
|
||||
Label rejoin;
|
||||
uint32_t maybeCmpOffset = 0;
|
||||
if (mir->needsBoundsCheck()) {
|
||||
Label goahead;
|
||||
BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
|
||||
Register out = ToRegister(ins->output());
|
||||
maybeCmpOffset = bo.getOffset();
|
||||
masm.ma_b(&goahead, Assembler::Below);
|
||||
memoryBarrier(MembarFull);
|
||||
masm.as_eor(out, out, O2Reg(out));
|
||||
masm.ma_b(&rejoin, Assembler::Always);
|
||||
masm.bind(&goahead);
|
||||
masm.ma_b(gen->outOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
}
|
||||
|
||||
masm.atomicExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
|
||||
srcAddr, value, InvalidReg, ToAnyRegister(ins->output()));
|
||||
if (rejoin.used()) {
|
||||
masm.bind(&rejoin);
|
||||
|
||||
if (mir->needsBoundsCheck())
|
||||
masm.append(AsmJSHeapAccess(maybeCmpOffset));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2011,19 +1998,13 @@ CodeGeneratorARM::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
|
|||
|
||||
BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
|
||||
|
||||
Label rejoin;
|
||||
uint32_t maybeCmpOffset = 0;
|
||||
if (mir->needsBoundsCheck()) {
|
||||
Label goahead;
|
||||
BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
|
||||
Register out = ToRegister(ins->output());
|
||||
maybeCmpOffset = bo.getOffset();
|
||||
masm.ma_b(&goahead, Assembler::Below);
|
||||
memoryBarrier(MembarFull);
|
||||
masm.as_eor(out, out, O2Reg(out));
|
||||
masm.ma_b(&rejoin, Assembler::Always);
|
||||
masm.bind(&goahead);
|
||||
masm.ma_b(gen->outOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
}
|
||||
|
||||
if (value->isConstant())
|
||||
masm.atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
|
||||
Imm32(ToInt32(value)), srcAddr, temp, InvalidReg,
|
||||
|
@ -2032,10 +2013,9 @@ CodeGeneratorARM::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
|
|||
masm.atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
|
||||
ToRegister(value), srcAddr, temp, InvalidReg,
|
||||
ToAnyRegister(ins->output()));
|
||||
if (rejoin.used()) {
|
||||
masm.bind(&rejoin);
|
||||
|
||||
if (mir->needsBoundsCheck())
|
||||
masm.append(AsmJSHeapAccess(maybeCmpOffset));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2053,16 +2033,11 @@ CodeGeneratorARM::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEff
|
|||
|
||||
BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
|
||||
|
||||
Label rejoin;
|
||||
uint32_t maybeCmpOffset = 0;
|
||||
if (mir->needsBoundsCheck()) {
|
||||
Label goahead;
|
||||
BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
|
||||
maybeCmpOffset = bo.getOffset();
|
||||
masm.ma_b(&goahead, Assembler::Below);
|
||||
memoryBarrier(MembarFull);
|
||||
masm.ma_b(&rejoin, Assembler::Always);
|
||||
masm.bind(&goahead);
|
||||
masm.ma_b(gen->outOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
}
|
||||
|
||||
if (value->isConstant())
|
||||
|
@ -2070,10 +2045,8 @@ CodeGeneratorARM::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEff
|
|||
else
|
||||
masm.atomicBinopToTypedIntArray(op, vt, ToRegister(value), srcAddr);
|
||||
|
||||
if (rejoin.used()) {
|
||||
masm.bind(&rejoin);
|
||||
if (mir->needsBoundsCheck())
|
||||
masm.append(AsmJSHeapAccess(maybeCmpOffset));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -375,9 +375,15 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
|
|||
OutOfLineLoadTypedArrayOutOfBounds* ool = nullptr;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
if (gen->needsAsmJSBoundsCheckBranch(mir)) {
|
||||
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), accessType);
|
||||
addOutOfLineCode(ool, mir);
|
||||
maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), ool->entry());
|
||||
Label* jumpTo = nullptr;
|
||||
if (mir->isAtomicAccess()) {
|
||||
jumpTo = gen->outOfBoundsLabel();
|
||||
} else {
|
||||
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), accessType);
|
||||
addOutOfLineCode(ool, mir);
|
||||
jumpTo = ool->entry();
|
||||
}
|
||||
maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), jumpTo);
|
||||
}
|
||||
|
||||
uint32_t before = masm.size();
|
||||
|
@ -521,8 +527,12 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
|
|||
Label* rejoin = nullptr;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
if (gen->needsAsmJSBoundsCheckBranch(mir)) {
|
||||
rejoin = alloc().lifoAlloc()->new_<Label>();
|
||||
maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), rejoin);
|
||||
Label* jumpTo = nullptr;
|
||||
if (mir->isAtomicAccess())
|
||||
jumpTo = gen->outOfBoundsLabel();
|
||||
else
|
||||
rejoin = jumpTo = alloc().lifoAlloc()->new_<Label>();
|
||||
maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), jumpTo);
|
||||
}
|
||||
|
||||
uint32_t before = masm.size();
|
||||
|
@ -586,17 +596,10 @@ CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
|
|||
// Note that we can't use
|
||||
// needsAsmJSBoundsCheckBranch/emitAsmJSBoundsCheckBranch/cleanupAfterAsmJSBoundsCheckBranch
|
||||
// since signal-handler bounds checking is not yet implemented for atomic accesses.
|
||||
Label rejoin;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
if (mir->needsBoundsCheck()) {
|
||||
maybeCmpOffset = masm.cmp32WithPatch(ToRegister(ptr), Imm32(-mir->endOffset())).offset();
|
||||
Label goahead;
|
||||
masm.j(Assembler::BelowOrEqual, &goahead);
|
||||
memoryBarrier(MembarFull);
|
||||
Register out = ToRegister(ins->output());
|
||||
masm.xorl(out, out);
|
||||
masm.jmp(&rejoin);
|
||||
masm.bind(&goahead);
|
||||
masm.j(Assembler::Above, gen->outOfBoundsLabel());
|
||||
}
|
||||
uint32_t before = masm.size();
|
||||
masm.compareExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
|
||||
|
@ -605,8 +608,6 @@ CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
|
|||
newval,
|
||||
InvalidReg,
|
||||
ToAnyRegister(ins->output()));
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
MOZ_ASSERT(mir->offset() == 0,
|
||||
"The AsmJS signal handler doesn't yet support emulating "
|
||||
"atomic accesses in the case of a fault from an unwrapped offset");
|
||||
|
@ -630,17 +631,10 @@ CodeGeneratorX64::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
|
|||
// Note that we can't use
|
||||
// needsAsmJSBoundsCheckBranch/emitAsmJSBoundsCheckBranch/cleanupAfterAsmJSBoundsCheckBranch
|
||||
// since signal-handler bounds checking is not yet implemented for atomic accesses.
|
||||
Label rejoin;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
if (mir->needsBoundsCheck()) {
|
||||
maybeCmpOffset = masm.cmp32WithPatch(ToRegister(ptr), Imm32(-mir->endOffset())).offset();
|
||||
Label goahead;
|
||||
masm.j(Assembler::BelowOrEqual, &goahead);
|
||||
memoryBarrier(MembarFull);
|
||||
Register out = ToRegister(ins->output());
|
||||
masm.xorl(out, out);
|
||||
masm.jmp(&rejoin);
|
||||
masm.bind(&goahead);
|
||||
masm.j(Assembler::Above, gen->outOfBoundsLabel());
|
||||
}
|
||||
uint32_t before = masm.size();
|
||||
masm.atomicExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
|
||||
|
@ -648,8 +642,6 @@ CodeGeneratorX64::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
|
|||
value,
|
||||
InvalidReg,
|
||||
ToAnyRegister(ins->output()));
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
MOZ_ASSERT(mir->offset() == 0,
|
||||
"The AsmJS signal handler doesn't yet support emulating "
|
||||
"atomic accesses in the case of a fault from an unwrapped offset");
|
||||
|
@ -674,17 +666,10 @@ CodeGeneratorX64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
|
|||
// Note that we can't use
|
||||
// needsAsmJSBoundsCheckBranch/emitAsmJSBoundsCheckBranch/cleanupAfterAsmJSBoundsCheckBranch
|
||||
// since signal-handler bounds checking is not yet implemented for atomic accesses.
|
||||
Label rejoin;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
if (mir->needsBoundsCheck()) {
|
||||
maybeCmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(-mir->endOffset())).offset();
|
||||
Label goahead;
|
||||
masm.j(Assembler::BelowOrEqual, &goahead);
|
||||
memoryBarrier(MembarFull);
|
||||
Register out = ToRegister(ins->output());
|
||||
masm.xorl(out,out);
|
||||
masm.jmp(&rejoin);
|
||||
masm.bind(&goahead);
|
||||
masm.j(Assembler::Above, gen->outOfBoundsLabel());
|
||||
}
|
||||
uint32_t before = masm.size();
|
||||
if (value->isConstant()) {
|
||||
|
@ -702,8 +687,6 @@ CodeGeneratorX64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
|
|||
InvalidReg,
|
||||
ToAnyRegister(ins->output()));
|
||||
}
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
MOZ_ASSERT(mir->offset() == 0,
|
||||
"The AsmJS signal handler doesn't yet support emulating "
|
||||
"atomic accesses in the case of a fault from an unwrapped offset");
|
||||
|
@ -727,15 +710,10 @@ CodeGeneratorX64::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEff
|
|||
// Note that we can't use
|
||||
// needsAsmJSBoundsCheckBranch/emitAsmJSBoundsCheckBranch/cleanupAfterAsmJSBoundsCheckBranch
|
||||
// since signal-handler bounds checking is not yet implemented for atomic accesses.
|
||||
Label rejoin;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
if (mir->needsBoundsCheck()) {
|
||||
maybeCmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(-mir->endOffset())).offset();
|
||||
Label goahead;
|
||||
masm.j(Assembler::BelowOrEqual, &goahead);
|
||||
memoryBarrier(MembarFull);
|
||||
masm.jmp(&rejoin);
|
||||
masm.bind(&goahead);
|
||||
masm.j(Assembler::Above, gen->outOfBoundsLabel());
|
||||
}
|
||||
|
||||
uint32_t before = masm.size();
|
||||
|
@ -743,9 +721,6 @@ CodeGeneratorX64::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEff
|
|||
masm.atomicBinopToTypedIntArray(op, accessType, Imm32(ToInt32(value)), srcAddr);
|
||||
else
|
||||
masm.atomicBinopToTypedIntArray(op, accessType, ToRegister(value), srcAddr);
|
||||
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
MOZ_ASSERT(mir->offset() == 0,
|
||||
"The AsmJS signal handler doesn't yet support emulating "
|
||||
"atomic accesses in the case of a fault from an unwrapped offset");
|
||||
|
|
|
@ -456,9 +456,15 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
|
|||
OutOfLineLoadTypedArrayOutOfBounds* ool = nullptr;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
if (gen->needsAsmJSBoundsCheckBranch(mir)) {
|
||||
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), accessType);
|
||||
addOutOfLineCode(ool, mir);
|
||||
maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), ool->entry());
|
||||
Label* jumpTo = nullptr;
|
||||
if (mir->isAtomicAccess()) {
|
||||
jumpTo = gen->outOfBoundsLabel();
|
||||
} else {
|
||||
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), accessType);
|
||||
addOutOfLineCode(ool, mir);
|
||||
jumpTo = ool->entry();
|
||||
}
|
||||
maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), jumpTo);
|
||||
}
|
||||
|
||||
uint32_t before = masm.size();
|
||||
|
@ -627,8 +633,12 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
|
|||
Label* rejoin = nullptr;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
if (gen->needsAsmJSBoundsCheckBranch(mir)) {
|
||||
rejoin = alloc().lifoAlloc()->new_<Label>();
|
||||
maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), rejoin);
|
||||
Label* jumpTo = nullptr;
|
||||
if (mir->isAtomicAccess())
|
||||
jumpTo = gen->outOfBoundsLabel();
|
||||
else
|
||||
rejoin = jumpTo = alloc().lifoAlloc()->new_<Label>();
|
||||
maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), jumpTo);
|
||||
}
|
||||
|
||||
uint32_t before = masm.size();
|
||||
|
@ -647,36 +657,13 @@ CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
|
|||
{
|
||||
MAsmJSCompareExchangeHeap* mir = ins->mir();
|
||||
Scalar::Type accessType = mir->accessType();
|
||||
const LAllocation* ptr = ins->ptr();
|
||||
Register ptrReg = ToRegister(ins->ptr());
|
||||
Register oldval = ToRegister(ins->oldValue());
|
||||
Register newval = ToRegister(ins->newValue());
|
||||
Register addrTemp = ToRegister(ins->addrTemp());
|
||||
|
||||
MOZ_ASSERT(ptr->isRegister());
|
||||
// Set up the offset within the heap in the pointer reg.
|
||||
Register ptrReg = ToRegister(ptr);
|
||||
|
||||
Label rejoin;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
|
||||
if (mir->needsBoundsCheck()) {
|
||||
maybeCmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(-mir->endOffset())).offset();
|
||||
Label goahead;
|
||||
masm.j(Assembler::BelowOrEqual, &goahead);
|
||||
memoryBarrier(MembarFull);
|
||||
Register out = ToRegister(ins->output());
|
||||
masm.xorl(out,out);
|
||||
masm.jmp(&rejoin);
|
||||
masm.bind(&goahead);
|
||||
}
|
||||
|
||||
// Add in the actual heap pointer explicitly, to avoid opening up
|
||||
// the abstraction that is compareExchangeToTypedIntArray at this time.
|
||||
masm.movl(ptrReg, addrTemp);
|
||||
uint32_t before = masm.size();
|
||||
masm.addlWithPatch(Imm32(mir->offset()), addrTemp);
|
||||
uint32_t after = masm.size();
|
||||
masm.append(AsmJSHeapAccess(before, after, maybeCmpOffset));
|
||||
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir->needsBoundsCheck(), mir->offset(),
|
||||
mir->endOffset());
|
||||
|
||||
Address memAddr(addrTemp, mir->offset());
|
||||
masm.compareExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
|
||||
|
@ -685,31 +672,21 @@ CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
|
|||
newval,
|
||||
InvalidReg,
|
||||
ToAnyRegister(ins->output()));
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
}
|
||||
|
||||
// Perform bounds checking on the access if necessary; if it fails,
|
||||
// perform a barrier and clear out the result register (if valid)
|
||||
// before jumping to rejoin. If the bounds check passes, set up the
|
||||
// heap address in addrTemp.
|
||||
// jump to out-of-line code that throws. If the bounds check passes,
|
||||
// set up the heap address in addrTemp.
|
||||
|
||||
void
|
||||
CodeGeneratorX86::asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
|
||||
int32_t offset, int32_t endOffset, Register out,
|
||||
Label& rejoin)
|
||||
int32_t offset, int32_t endOffset)
|
||||
{
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
|
||||
if (boundsCheck) {
|
||||
maybeCmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(-endOffset)).offset();
|
||||
Label goahead;
|
||||
masm.j(Assembler::BelowOrEqual, &goahead);
|
||||
memoryBarrier(MembarFull);
|
||||
if (out != InvalidReg)
|
||||
masm.xorl(out,out);
|
||||
masm.jmp(&rejoin);
|
||||
masm.bind(&goahead);
|
||||
masm.j(Assembler::Above, gen->outOfBoundsLabel());
|
||||
}
|
||||
|
||||
// Add in the actual heap pointer explicitly, to avoid opening up
|
||||
|
@ -726,14 +703,12 @@ CodeGeneratorX86::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
|
|||
{
|
||||
MAsmJSAtomicExchangeHeap* mir = ins->mir();
|
||||
Scalar::Type accessType = mir->accessType();
|
||||
const LAllocation* ptr = ins->ptr();
|
||||
Register ptrReg = ToRegister(ptr);
|
||||
Register ptrReg = ToRegister(ins->ptr());
|
||||
Register value = ToRegister(ins->value());
|
||||
Register addrTemp = ToRegister(ins->addrTemp());
|
||||
Label rejoin;
|
||||
|
||||
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir->needsBoundsCheck(), mir->offset(),
|
||||
mir->endOffset(), ToRegister(ins->output()), rejoin);
|
||||
mir->endOffset());
|
||||
|
||||
Address memAddr(addrTemp, mir->offset());
|
||||
masm.atomicExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
|
||||
|
@ -741,9 +716,6 @@ CodeGeneratorX86::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
|
|||
value,
|
||||
InvalidReg,
|
||||
ToAnyRegister(ins->output()));
|
||||
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -756,10 +728,9 @@ CodeGeneratorX86::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
|
|||
Register addrTemp = ToRegister(ins->addrTemp());
|
||||
const LAllocation* value = ins->value();
|
||||
AtomicOp op = mir->operation();
|
||||
Label rejoin;
|
||||
|
||||
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir->needsBoundsCheck(), mir->offset(),
|
||||
mir->endOffset(), ToRegister(ins->output()), rejoin);
|
||||
mir->endOffset());
|
||||
|
||||
Address memAddr(addrTemp, mir->offset());
|
||||
if (value->isConstant()) {
|
||||
|
@ -777,8 +748,6 @@ CodeGeneratorX86::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
|
|||
InvalidReg,
|
||||
ToAnyRegister(ins->output()));
|
||||
}
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -790,20 +759,17 @@ CodeGeneratorX86::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEff
|
|||
Register addrTemp = ToRegister(ins->addrTemp());
|
||||
const LAllocation* value = ins->value();
|
||||
AtomicOp op = mir->operation();
|
||||
Label rejoin;
|
||||
|
||||
MOZ_ASSERT(!mir->hasUses());
|
||||
|
||||
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir->needsBoundsCheck(), mir->offset(),
|
||||
mir->endOffset(), InvalidReg, rejoin);
|
||||
mir->endOffset());
|
||||
|
||||
Address memAddr(addrTemp, mir->offset());
|
||||
if (value->isConstant())
|
||||
masm.atomicBinopToTypedIntArray(op, accessType, Imm32(ToInt32(value)), memAddr);
|
||||
else
|
||||
masm.atomicBinopToTypedIntArray(op, accessType, ToRegister(value), memAddr);
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -76,8 +76,7 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
|
|||
|
||||
private:
|
||||
void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
|
||||
int32_t offset, int32_t endOffset, Register out,
|
||||
Label& rejoin);
|
||||
int32_t offset, int32_t endOffset);
|
||||
};
|
||||
|
||||
typedef CodeGeneratorX86 CodeGeneratorSpecific;
|
||||
|
|
|
@ -490,6 +490,7 @@ MSG_DEF(JSMSG_SYMBOL_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert symbol t
|
|||
MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY, 0, JSEXN_TYPEERR, "invalid array type for the operation")
|
||||
MSG_DEF(JSMSG_ATOMICS_TOO_LONG, 0, JSEXN_RANGEERR, "timeout value too large")
|
||||
MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED, 0, JSEXN_ERR, "waiting is not allowed on this thread")
|
||||
MSG_DEF(JSMSG_ATOMICS_BAD_INDEX, 0, JSEXN_RANGEERR, "out-of-range index for atomic access")
|
||||
|
||||
// XPConnect wrappers and DOM bindings
|
||||
MSG_DEF(JSMSG_CANT_SET_INTERPOSED, 1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'")
|
||||
|
|
|
@ -22,7 +22,8 @@ int BarkWhenTracedClass::finalizeCount;
|
|||
int BarkWhenTracedClass::traceCount;
|
||||
|
||||
const JSClass BarkWhenTracedClass::class_ = {
|
||||
"BarkWhenTracedClass", JSCLASS_IMPLEMENTS_BARRIERS,
|
||||
"BarkWhenTracedClass",
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
|
|
|
@ -2986,8 +2986,8 @@ js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t en
|
|||
return &argv[0].toObject();
|
||||
}
|
||||
|
||||
static bool
|
||||
array_isArray(JSContext* cx, unsigned argc, Value* vp)
|
||||
bool
|
||||
js::array_isArray(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
bool isArray = false;
|
||||
|
|
|
@ -179,7 +179,7 @@ extern bool
|
|||
array_concat_dense(JSContext* cx, HandleObject arr1, HandleObject arr2,
|
||||
HandleObject result);
|
||||
|
||||
bool
|
||||
extern bool
|
||||
array_join(JSContext* cx, unsigned argc, js::Value* vp);
|
||||
|
||||
extern JSString*
|
||||
|
@ -200,6 +200,9 @@ array_slice(JSContext* cx, unsigned argc, js::Value* vp);
|
|||
extern JSObject*
|
||||
array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end, HandleObject result);
|
||||
|
||||
extern bool
|
||||
array_isArray(JSContext* cx, unsigned argc, js::Value* vp);
|
||||
|
||||
/*
|
||||
* Append the given (non-hole) value to the end of an array. The array must be
|
||||
* a newborn array -- that is, one which has not been exposed to script for
|
||||
|
|
|
@ -79,8 +79,8 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
|
|||
scheduledForDestruction(false),
|
||||
maybeAlive(true),
|
||||
jitCompartment_(nullptr),
|
||||
normalArgumentsTemplate_(nullptr),
|
||||
strictArgumentsTemplate_(nullptr)
|
||||
mappedArgumentsTemplate_(nullptr),
|
||||
unmappedArgumentsTemplate_(nullptr)
|
||||
{
|
||||
PodArrayZero(sawDeprecatedLanguageExtension);
|
||||
runtime_->numCompartments++;
|
||||
|
@ -715,11 +715,11 @@ JSCompartment::sweepCrossCompartmentWrappers()
|
|||
void
|
||||
JSCompartment::sweepTemplateObjects()
|
||||
{
|
||||
if (normalArgumentsTemplate_ && IsAboutToBeFinalized(&normalArgumentsTemplate_))
|
||||
normalArgumentsTemplate_.set(nullptr);
|
||||
if (mappedArgumentsTemplate_ && IsAboutToBeFinalized(&mappedArgumentsTemplate_))
|
||||
mappedArgumentsTemplate_.set(nullptr);
|
||||
|
||||
if (strictArgumentsTemplate_ && IsAboutToBeFinalized(&strictArgumentsTemplate_))
|
||||
strictArgumentsTemplate_.set(nullptr);
|
||||
if (unmappedArgumentsTemplate_ && IsAboutToBeFinalized(&unmappedArgumentsTemplate_))
|
||||
unmappedArgumentsTemplate_.set(nullptr);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
|
|
|
@ -700,8 +700,8 @@ struct JSCompartment
|
|||
private:
|
||||
js::jit::JitCompartment* jitCompartment_;
|
||||
|
||||
js::ReadBarriered<js::ArgumentsObject*> normalArgumentsTemplate_;
|
||||
js::ReadBarriered<js::ArgumentsObject*> strictArgumentsTemplate_;
|
||||
js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_;
|
||||
js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_;
|
||||
|
||||
public:
|
||||
bool ensureJitCompartmentExists(JSContext* cx);
|
||||
|
@ -722,7 +722,7 @@ struct JSCompartment
|
|||
DeprecatedLanguageExtensionCount
|
||||
};
|
||||
|
||||
js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool strict);
|
||||
js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
|
||||
|
||||
private:
|
||||
// Used for collecting telemetry on SpiderMonkey's deprecated language extensions.
|
||||
|
|
|
@ -68,7 +68,6 @@ static const JSFunctionSpec exception_methods[] = {
|
|||
#define IMPLEMENT_ERROR_SUBCLASS(name) \
|
||||
{ \
|
||||
js_Error_str, /* yes, really */ \
|
||||
JSCLASS_IMPLEMENTS_BARRIERS | \
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), \
|
||||
nullptr, /* addProperty */ \
|
||||
|
@ -100,7 +99,6 @@ const Class
|
|||
ErrorObject::classes[JSEXN_LIMIT] = {
|
||||
{
|
||||
js_Error_str,
|
||||
JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Error) |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS),
|
||||
nullptr, /* addProperty */
|
||||
|
|
|
@ -315,7 +315,6 @@ namespace js {
|
|||
name, \
|
||||
js::Class::NON_NATIVE | \
|
||||
JSCLASS_IS_PROXY | \
|
||||
JSCLASS_IMPLEMENTS_BARRIERS | \
|
||||
JSCLASS_DELAY_METADATA_CALLBACK | \
|
||||
flags, \
|
||||
nullptr, /* addProperty */ \
|
||||
|
|
|
@ -811,7 +811,6 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
|
|||
|
||||
const Class JSFunction::class_ = {
|
||||
js_Function_str,
|
||||
JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
|
@ -1337,7 +1336,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
|
|||
// re-lazified. Functions with either of those are on the static scope
|
||||
// chain of their inner functions, or in the case of eval, possibly
|
||||
// eval'd inner functions. This prohibits re-lazification as
|
||||
// StaticScopeIter queries isHeavyweight of those functions, which
|
||||
// StaticScopeIter queries needsCallObject of those functions, which
|
||||
// requires a non-lazy script. Note that if this ever changes,
|
||||
// XDRRelazificationInfo will have to be fixed.
|
||||
bool canRelazify = !lazy->numInnerFunctions() && !lazy->hasDirectEval();
|
||||
|
|
|
@ -143,15 +143,15 @@ class JSFunction : public js::NativeObject
|
|||
|
||||
public:
|
||||
|
||||
/* Call objects must be created for each invocation of a heavyweight function. */
|
||||
bool isHeavyweight() const {
|
||||
/* Call objects must be created for each invocation of this function. */
|
||||
bool needsCallObject() const {
|
||||
MOZ_ASSERT(!isInterpretedLazy());
|
||||
MOZ_ASSERT(!isBeingParsed());
|
||||
|
||||
if (isNative())
|
||||
return false;
|
||||
|
||||
// Note: this should be kept in sync with FunctionBox::isHeavyweight().
|
||||
// Note: this should be kept in sync with FunctionBox::needsCallObject().
|
||||
return nonLazyScript()->hasAnyAliasedBindings() ||
|
||||
nonLazyScript()->funHasExtensibleScope() ||
|
||||
nonLazyScript()->funNeedsDeclEnvObject() ||
|
||||
|
|
|
@ -33,9 +33,6 @@
|
|||
* - the GC mode must have been set to JSGC_MODE_INCREMENTAL with
|
||||
* JS_SetGCParameter()
|
||||
* - no thread may have an AutoKeepAtoms instance on the stack
|
||||
* - all native objects that have their own trace hook must indicate that they
|
||||
* implement read and write barriers with the JSCLASS_IMPLEMENTS_BARRIERS
|
||||
* flag
|
||||
*
|
||||
* The last condition is an engine-internal mechanism to ensure that incremental
|
||||
* collection is not carried out without the correct barriers being implemented.
|
||||
|
|
|
@ -992,7 +992,6 @@ PropertyIteratorObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
|
||||
const Class PropertyIteratorObject::class_ = {
|
||||
"Iterator",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_BACKGROUND_FINALIZE,
|
||||
|
@ -1013,7 +1012,7 @@ const Class PropertyIteratorObject::class_ = {
|
|||
|
||||
static const Class ArrayIteratorPrototypeClass = {
|
||||
"Array Iterator",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS
|
||||
0
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -1025,7 +1024,6 @@ enum {
|
|||
|
||||
const Class ArrayIteratorObject::class_ = {
|
||||
"Array Iterator",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount)
|
||||
};
|
||||
|
||||
|
@ -1037,7 +1035,7 @@ static const JSFunctionSpec array_iterator_methods[] = {
|
|||
|
||||
static const Class StringIteratorPrototypeClass = {
|
||||
"String Iterator",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS
|
||||
0
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -1048,7 +1046,6 @@ enum {
|
|||
|
||||
const Class StringIteratorObject::class_ = {
|
||||
"String Iterator",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount)
|
||||
};
|
||||
|
||||
|
|
|
@ -692,11 +692,6 @@ NewObject(ExclusiveContext* cx, HandleObjectGroup group, gc::AllocKind kind,
|
|||
obj = nobj;
|
||||
}
|
||||
|
||||
bool globalWithoutCustomTrace = clasp->trace == JS_GlobalObjectTraceHook &&
|
||||
!cx->compartment()->options().getTrace();
|
||||
if (clasp->trace && !globalWithoutCustomTrace)
|
||||
MOZ_RELEASE_ASSERT(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
|
||||
|
||||
probes::CreateObject(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -589,6 +589,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript
|
|||
FunHasAnyAliasedFormal,
|
||||
ArgumentsHasVarBinding,
|
||||
NeedsArgsObj,
|
||||
HasMappedArgsObj,
|
||||
IsGeneratorExp,
|
||||
IsLegacyGenerator,
|
||||
IsStarGenerator,
|
||||
|
@ -716,6 +717,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript
|
|||
scriptBits |= (1 << ArgumentsHasVarBinding);
|
||||
if (script->analyzedArgsUsage() && script->needsArgsObj())
|
||||
scriptBits |= (1 << NeedsArgsObj);
|
||||
if (script->hasMappedArgsObj())
|
||||
scriptBits |= (1 << HasMappedArgsObj);
|
||||
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
|
||||
scriptBits |= (1 << OwnSource);
|
||||
if (script->isGeneratorExp())
|
||||
|
@ -856,6 +859,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript
|
|||
script->setArgumentsHasVarBinding();
|
||||
if (scriptBits & (1 << NeedsArgsObj))
|
||||
script->setNeedsArgsObj(true);
|
||||
if (scriptBits & (1 << HasMappedArgsObj))
|
||||
script->hasMappedArgsObj_ = true;
|
||||
if (scriptBits & (1 << IsGeneratorExp))
|
||||
script->isGeneratorExp_ = true;
|
||||
if (scriptBits & (1 << HasSingleton))
|
||||
|
@ -1494,7 +1499,7 @@ ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
const Class ScriptSourceObject::class_ = {
|
||||
"ScriptSource",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
|
||||
JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS,
|
||||
JSCLASS_IS_ANONYMOUS,
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
|
@ -2728,6 +2733,7 @@ JSScript::linkToFunctionFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScrip
|
|||
} else {
|
||||
MOZ_ASSERT(!funbox->definitelyNeedsArgsObj());
|
||||
}
|
||||
script->hasMappedArgsObj_ = funbox->hasMappedArgsObj();
|
||||
|
||||
script->funLength_ = funbox->length;
|
||||
|
||||
|
@ -2819,6 +2825,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
|
|||
MOZ_ASSERT(script->needsHomeObject_ == funbox->needsHomeObject());
|
||||
MOZ_ASSERT(script->isDerivedClassConstructor_ == funbox->isDerivedClassConstructor());
|
||||
MOZ_ASSERT(script->argumentsHasVarBinding() == funbox->argumentsHasLocalBinding());
|
||||
MOZ_ASSERT(script->hasMappedArgsObj() == funbox->hasMappedArgsObj());
|
||||
MOZ_ASSERT(script->functionNonDelazifying() == funbox->function());
|
||||
MOZ_ASSERT(script->isGeneratorExp_ == funbox->inGenexpLambda);
|
||||
MOZ_ASSERT(script->generatorKind() == funbox->generatorKind());
|
||||
|
@ -2828,6 +2835,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
|
|||
MOZ_ASSERT(!script->needsHomeObject_);
|
||||
MOZ_ASSERT(!script->isDerivedClassConstructor_);
|
||||
MOZ_ASSERT(!script->argumentsHasVarBinding());
|
||||
MOZ_ASSERT(!script->hasMappedArgsObj());
|
||||
MOZ_ASSERT(!script->isGeneratorExp_);
|
||||
MOZ_ASSERT(script->generatorKind() == NotGenerator);
|
||||
}
|
||||
|
@ -3342,6 +3350,7 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri
|
|||
if (src->analyzedArgsUsage())
|
||||
dst->setNeedsArgsObj(src->needsArgsObj());
|
||||
}
|
||||
dst->hasMappedArgsObj_ = src->hasMappedArgsObj();
|
||||
dst->cloneHasArray(src);
|
||||
dst->strict_ = src->strict();
|
||||
dst->explicitUseStrict_ = src->explicitUseStrict();
|
||||
|
|
|
@ -1135,6 +1135,10 @@ class JSScript : public js::gc::TenuredCell
|
|||
bool needsArgsAnalysis_:1;
|
||||
bool needsArgsObj_:1;
|
||||
|
||||
// Whether the arguments object for this script, if it needs one, should be
|
||||
// mapped (alias formal parameters).
|
||||
bool hasMappedArgsObj_:1;
|
||||
|
||||
// Generation for this script's TypeScript. If out of sync with the
|
||||
// TypeZone's generation, the TypeScript needs to be swept.
|
||||
//
|
||||
|
@ -1404,7 +1408,7 @@ class JSScript : public js::gc::TenuredCell
|
|||
jsbytecode* argumentsBytecode() const { MOZ_ASSERT(code()[0] == JSOP_ARGUMENTS); return code(); }
|
||||
void setArgumentsHasVarBinding();
|
||||
bool argumentsAliasesFormals() const {
|
||||
return argumentsHasVarBinding() && !strict();
|
||||
return argumentsHasVarBinding() && hasMappedArgsObj();
|
||||
}
|
||||
|
||||
js::GeneratorKind generatorKind() const {
|
||||
|
@ -1450,17 +1454,20 @@ class JSScript : public js::gc::TenuredCell
|
|||
void setNeedsArgsObj(bool needsArgsObj);
|
||||
static bool argumentsOptimizationFailed(JSContext* cx, js::HandleScript script);
|
||||
|
||||
bool hasMappedArgsObj() const {
|
||||
return hasMappedArgsObj_;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arguments access (via JSOP_*ARG* opcodes) must access the canonical
|
||||
* location for the argument. If an arguments object exists AND this is a
|
||||
* non-strict function (where 'arguments' aliases formals), then all access
|
||||
* must go through the arguments object. Otherwise, the local slot is the
|
||||
* canonical location for the arguments. Note: if a formal is aliased
|
||||
* through the scope chain, then script->formalIsAliased and JSOP_*ARG*
|
||||
* opcodes won't be emitted at all.
|
||||
* location for the argument. If an arguments object exists AND it's mapped
|
||||
* ('arguments' aliases formals), then all access must go through the
|
||||
* arguments object. Otherwise, the local slot is the canonical location for
|
||||
* the arguments. Note: if a formal is aliased through the scope chain, then
|
||||
* script->formalIsAliased and JSOP_*ARG* opcodes won't be emitted at all.
|
||||
*/
|
||||
bool argsObjAliasesFormals() const {
|
||||
return needsArgsObj() && !strict();
|
||||
return needsArgsObj() && hasMappedArgsObj();
|
||||
}
|
||||
|
||||
uint32_t typesGeneration() const {
|
||||
|
|
|
@ -3486,10 +3486,10 @@ LambdaIsGetElem(JSContext* cx, JSObject& lambda, MutableHandleNativeObject pobj)
|
|||
|
||||
/*
|
||||
* JSOP_GETALIASEDVAR tells us exactly where to find the base object 'b'.
|
||||
* Rule out the (unlikely) possibility of a heavyweight function since it
|
||||
* would make our scope walk off by 1.
|
||||
* Rule out the (unlikely) possibility of a function with a call object
|
||||
* since it would make our scope walk off by 1.
|
||||
*/
|
||||
if (JSOp(*pc) != JSOP_GETALIASEDVAR || fun->isHeavyweight())
|
||||
if (JSOp(*pc) != JSOP_GETALIASEDVAR || fun->needsCallObject())
|
||||
return true;
|
||||
ScopeCoordinate sc(pc);
|
||||
ScopeObject* scope = &fun->environment()->as<ScopeObject>();
|
||||
|
|
|
@ -614,7 +614,7 @@ WeakMap_construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
const Class WeakMapObject::class_ = {
|
||||
"WeakMap",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
|
|
|
@ -1974,8 +1974,8 @@ DisassembleScript(JSContext* cx, HandleScript script, HandleFunction fun, bool l
|
|||
Sprint(sp, "flags:");
|
||||
if (fun->isLambda())
|
||||
Sprint(sp, " LAMBDA");
|
||||
if (fun->isHeavyweight())
|
||||
Sprint(sp, " HEAVYWEIGHT");
|
||||
if (fun->needsCallObject())
|
||||
Sprint(sp, " NEEDS_CALLOBJECT");
|
||||
if (fun->isConstructor())
|
||||
Sprint(sp, " CONSTRUCTOR");
|
||||
if (fun->isExprBody())
|
||||
|
|
|
@ -39,7 +39,7 @@ ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, ArgumentsObjec
|
|||
ArgumentsData* data)
|
||||
{
|
||||
JSScript* script = frame.script();
|
||||
if (frame.fun()->isHeavyweight() && script->argsObjAliasesFormals()) {
|
||||
if (frame.fun()->needsCallObject() && script->argsObjAliasesFormals()) {
|
||||
obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(frame.callObj()));
|
||||
for (AliasedFormalIter fi(script); fi; fi++)
|
||||
data->args[fi.frameIndex()] = MagicScopeSlotValue(fi.scopeSlot());
|
||||
|
@ -52,7 +52,7 @@ ArgumentsObject::MaybeForwardToCallObject(jit::JitFrameLayout* frame, HandleObje
|
|||
{
|
||||
JSFunction* callee = jit::CalleeTokenToFunction(frame->calleeToken());
|
||||
JSScript* script = callee->nonLazyScript();
|
||||
if (callee->isHeavyweight() && script->argsObjAliasesFormals()) {
|
||||
if (callee->needsCallObject() && script->argsObjAliasesFormals()) {
|
||||
MOZ_ASSERT(callObj && callObj->is<CallObject>());
|
||||
obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(*callObj.get()));
|
||||
for (AliasedFormalIter fi(script); fi; fi++)
|
||||
|
@ -158,9 +158,11 @@ struct CopyScriptFrameIterArgs
|
|||
};
|
||||
|
||||
ArgumentsObject*
|
||||
ArgumentsObject::createTemplateObject(JSContext* cx, bool strict)
|
||||
ArgumentsObject::createTemplateObject(JSContext* cx, bool mapped)
|
||||
{
|
||||
const Class* clasp = strict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_;
|
||||
const Class* clasp = mapped
|
||||
? &MappedArgumentsObject::class_
|
||||
: &UnmappedArgumentsObject::class_;
|
||||
|
||||
RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
|
||||
if (!proto)
|
||||
|
@ -186,16 +188,16 @@ ArgumentsObject::createTemplateObject(JSContext* cx, bool strict)
|
|||
}
|
||||
|
||||
ArgumentsObject*
|
||||
JSCompartment::getOrCreateArgumentsTemplateObject(JSContext* cx, bool strict)
|
||||
JSCompartment::getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped)
|
||||
{
|
||||
ReadBarriered<ArgumentsObject*>& obj =
|
||||
strict ? strictArgumentsTemplate_ : normalArgumentsTemplate_;
|
||||
mapped ? mappedArgumentsTemplate_ : unmappedArgumentsTemplate_;
|
||||
|
||||
ArgumentsObject* templateObj = obj;
|
||||
if (templateObj)
|
||||
return templateObj;
|
||||
|
||||
templateObj = ArgumentsObject::createTemplateObject(cx, strict);
|
||||
templateObj = ArgumentsObject::createTemplateObject(cx, mapped);
|
||||
if (!templateObj)
|
||||
return nullptr;
|
||||
|
||||
|
@ -207,8 +209,8 @@ template <typename CopyArgs>
|
|||
/* static */ ArgumentsObject*
|
||||
ArgumentsObject::create(JSContext* cx, HandleFunction callee, unsigned numActuals, CopyArgs& copy)
|
||||
{
|
||||
bool strict = callee->strict();
|
||||
ArgumentsObject* templateObj = cx->compartment()->getOrCreateArgumentsTemplateObject(cx, strict);
|
||||
bool mapped = callee->nonLazyScript()->hasMappedArgsObj();
|
||||
ArgumentsObject* templateObj = cx->compartment()->getOrCreateArgumentsTemplateObject(cx, mapped);
|
||||
if (!templateObj)
|
||||
return nullptr;
|
||||
|
||||
|
@ -313,8 +315,9 @@ ArgumentsObject::createForIon(JSContext* cx, jit::JitFrameLayout* frame, HandleO
|
|||
return create(cx, callee, frame->numActualArgs(), copy);
|
||||
}
|
||||
|
||||
static bool
|
||||
args_delProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
|
||||
/* static */ bool
|
||||
ArgumentsObject::obj_delProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
|
||||
if (JSID_IS_INT(id)) {
|
||||
|
@ -324,15 +327,15 @@ args_delProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& r
|
|||
} else if (JSID_IS_ATOM(id, cx->names().length)) {
|
||||
argsobj.markLengthOverridden();
|
||||
} else if (JSID_IS_ATOM(id, cx->names().callee)) {
|
||||
argsobj.as<NormalArgumentsObject>().clearCallee();
|
||||
argsobj.as<MappedArgumentsObject>().clearCallee();
|
||||
}
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
static bool
|
||||
ArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
|
||||
MappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
|
||||
{
|
||||
NormalArgumentsObject& argsobj = obj->as<NormalArgumentsObject>();
|
||||
MappedArgumentsObject& argsobj = obj->as<MappedArgumentsObject>();
|
||||
if (JSID_IS_INT(id)) {
|
||||
/*
|
||||
* arg can exceed the number of arguments if a script changed the
|
||||
|
@ -353,12 +356,12 @@ ArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
|
|||
}
|
||||
|
||||
static bool
|
||||
ArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
|
||||
ObjectOpResult& result)
|
||||
MappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
if (!obj->is<NormalArgumentsObject>())
|
||||
if (!obj->is<MappedArgumentsObject>())
|
||||
return result.succeed();
|
||||
Handle<NormalArgumentsObject*> argsobj = obj.as<NormalArgumentsObject>();
|
||||
Handle<MappedArgumentsObject*> argsobj = obj.as<MappedArgumentsObject>();
|
||||
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!GetOwnPropertyDescriptor(cx, argsobj, id, &desc))
|
||||
|
@ -384,20 +387,21 @@ ArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
|
|||
|
||||
/*
|
||||
* For simplicity we use delete/define to replace the property with a
|
||||
* simple data property. Note that we rely on args_delProperty to clear the
|
||||
* corresponding reserved slot so the GC can collect its value. Note also
|
||||
* that we must define the property instead of setting it in case the user
|
||||
* has changed the prototype to an object that has a setter for this id.
|
||||
* simple data property. Note that we rely on ArgumentsObject::obj_delProperty
|
||||
* to clear the corresponding reserved slot so the GC can collect its value.
|
||||
* Note also that we must define the property instead of setting it in case
|
||||
* the user has changed the prototype to an object that has a setter for
|
||||
* this id.
|
||||
*/
|
||||
ObjectOpResult ignored;
|
||||
return NativeDeleteProperty(cx, argsobj, id, ignored) &&
|
||||
NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs, result);
|
||||
}
|
||||
|
||||
static bool
|
||||
args_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
||||
/* static */ bool
|
||||
MappedArgumentsObject::obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
||||
{
|
||||
Rooted<NormalArgumentsObject*> argsobj(cx, &obj->as<NormalArgumentsObject>());
|
||||
Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>());
|
||||
|
||||
unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE | JSPROP_RESOLVING;
|
||||
if (JSID_IS_INT(id)) {
|
||||
|
@ -417,17 +421,20 @@ args_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!NativeDefineProperty(cx, argsobj, id, UndefinedHandleValue, ArgGetter, ArgSetter, attrs))
|
||||
if (!NativeDefineProperty(cx, argsobj, id, UndefinedHandleValue,
|
||||
MappedArgGetter, MappedArgSetter, attrs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*resolvedp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
args_enumerate(JSContext* cx, HandleObject obj)
|
||||
/* static */ bool
|
||||
MappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
Rooted<NormalArgumentsObject*> argsobj(cx, &obj->as<NormalArgumentsObject>());
|
||||
Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>());
|
||||
|
||||
RootedId id(cx);
|
||||
bool found;
|
||||
|
@ -451,9 +458,9 @@ args_enumerate(JSContext* cx, HandleObject obj)
|
|||
}
|
||||
|
||||
static bool
|
||||
StrictArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
|
||||
UnmappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
|
||||
{
|
||||
StrictArgumentsObject& argsobj = obj->as<StrictArgumentsObject>();
|
||||
UnmappedArgumentsObject& argsobj = obj->as<UnmappedArgumentsObject>();
|
||||
|
||||
if (JSID_IS_INT(id)) {
|
||||
/*
|
||||
|
@ -472,12 +479,12 @@ StrictArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue
|
|||
}
|
||||
|
||||
static bool
|
||||
StrictArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
|
||||
ObjectOpResult& result)
|
||||
UnmappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
if (!obj->is<StrictArgumentsObject>())
|
||||
if (!obj->is<UnmappedArgumentsObject>())
|
||||
return result.succeed();
|
||||
Handle<StrictArgumentsObject*> argsobj = obj.as<StrictArgumentsObject>();
|
||||
Handle<UnmappedArgumentsObject*> argsobj = obj.as<UnmappedArgumentsObject>();
|
||||
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!GetOwnPropertyDescriptor(cx, argsobj, id, &desc))
|
||||
|
@ -499,22 +506,22 @@ StrictArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue
|
|||
|
||||
/*
|
||||
* For simplicity we use delete/define to replace the property with a
|
||||
* simple data property. Note that we rely on args_delProperty to clear the
|
||||
* corresponding reserved slot so the GC can collect its value.
|
||||
* simple data property. Note that we rely on ArgumentsObject::obj_delProperty
|
||||
* to clear the corresponding reserved slot so the GC can collect its value.
|
||||
*/
|
||||
ObjectOpResult ignored;
|
||||
return NativeDeleteProperty(cx, argsobj, id, ignored) &&
|
||||
NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs, result);
|
||||
}
|
||||
|
||||
static bool
|
||||
strictargs_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
||||
/* static */ bool
|
||||
UnmappedArgumentsObject::obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
||||
{
|
||||
Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>());
|
||||
Rooted<UnmappedArgumentsObject*> argsobj(cx, &obj->as<UnmappedArgumentsObject>());
|
||||
|
||||
unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
|
||||
GetterOp getter = StrictArgGetter;
|
||||
SetterOp setter = StrictArgSetter;
|
||||
GetterOp getter = UnmappedArgGetter;
|
||||
SetterOp setter = UnmappedArgSetter;
|
||||
|
||||
if (JSID_IS_INT(id)) {
|
||||
uint32_t arg = uint32_t(JSID_TO_INT(id));
|
||||
|
@ -542,10 +549,10 @@ strictargs_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
strictargs_enumerate(JSContext* cx, HandleObject obj)
|
||||
/* static */ bool
|
||||
UnmappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>());
|
||||
Rooted<UnmappedArgumentsObject*> argsobj(cx, &obj->as<UnmappedArgumentsObject>());
|
||||
|
||||
RootedId id(cx);
|
||||
bool found;
|
||||
|
@ -624,19 +631,19 @@ ArgumentsObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject
|
|||
* stack frame with their corresponding property values in the frame's
|
||||
* arguments object.
|
||||
*/
|
||||
const Class NormalArgumentsObject::class_ = {
|
||||
const Class MappedArgumentsObject::class_ = {
|
||||
"Arguments",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_DELAY_METADATA_CALLBACK |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
|
||||
JSCLASS_DELAY_METADATA_CALLBACK |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(MappedArgumentsObject::RESERVED_SLOTS) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
|
||||
JSCLASS_SKIP_NURSERY_FINALIZE |
|
||||
JSCLASS_BACKGROUND_FINALIZE,
|
||||
nullptr, /* addProperty */
|
||||
args_delProperty,
|
||||
ArgumentsObject::obj_delProperty,
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
args_enumerate,
|
||||
args_resolve,
|
||||
MappedArgumentsObject::obj_enumerate,
|
||||
MappedArgumentsObject::obj_resolve,
|
||||
nullptr, /* mayResolve */
|
||||
nullptr, /* convert */
|
||||
ArgumentsObject::finalize,
|
||||
|
@ -647,23 +654,22 @@ const Class NormalArgumentsObject::class_ = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Strict mode arguments is significantly less magical than non-strict mode
|
||||
* arguments, so it is represented by a different class while sharing some
|
||||
* functionality.
|
||||
* Unmapped arguments is significantly less magical than mapped arguments, so
|
||||
* it is represented by a different class while sharing some functionality.
|
||||
*/
|
||||
const Class StrictArgumentsObject::class_ = {
|
||||
const Class UnmappedArgumentsObject::class_ = {
|
||||
"Arguments",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_DELAY_METADATA_CALLBACK |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
|
||||
JSCLASS_DELAY_METADATA_CALLBACK |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(UnmappedArgumentsObject::RESERVED_SLOTS) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
|
||||
JSCLASS_SKIP_NURSERY_FINALIZE |
|
||||
JSCLASS_BACKGROUND_FINALIZE,
|
||||
nullptr, /* addProperty */
|
||||
args_delProperty,
|
||||
ArgumentsObject::obj_delProperty,
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
strictargs_enumerate,
|
||||
strictargs_resolve,
|
||||
UnmappedArgumentsObject::obj_enumerate,
|
||||
UnmappedArgumentsObject::obj_resolve,
|
||||
nullptr, /* mayResolve */
|
||||
nullptr, /* convert */
|
||||
ArgumentsObject::finalize,
|
||||
|
|
|
@ -136,6 +136,9 @@ class ArgumentsObject : public NativeObject
|
|||
return reinterpret_cast<ArgumentsData*>(getFixedSlot(DATA_SLOT).toPrivate());
|
||||
}
|
||||
|
||||
static bool obj_delProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
ObjectOpResult& result);
|
||||
|
||||
public:
|
||||
static const uint32_t RESERVED_SLOTS = 3;
|
||||
static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND;
|
||||
|
@ -154,7 +157,7 @@ class ArgumentsObject : public NativeObject
|
|||
static ArgumentsObject* createForIon(JSContext* cx, jit::JitFrameLayout* frame,
|
||||
HandleObject scopeChain);
|
||||
|
||||
static ArgumentsObject* createTemplateObject(JSContext* cx, bool strict);
|
||||
static ArgumentsObject* createTemplateObject(JSContext* cx, bool mapped);
|
||||
|
||||
/*
|
||||
* Return the initial length of the arguments. This may differ from the
|
||||
|
@ -310,7 +313,7 @@ class ArgumentsObject : public NativeObject
|
|||
ArgumentsObject* obj, ArgumentsData* data);
|
||||
};
|
||||
|
||||
class NormalArgumentsObject : public ArgumentsObject
|
||||
class MappedArgumentsObject : public ArgumentsObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
@ -327,12 +330,20 @@ class NormalArgumentsObject : public ArgumentsObject
|
|||
void clearCallee() {
|
||||
data()->callee = MagicValue(JS_OVERWRITTEN_CALLEE);
|
||||
}
|
||||
|
||||
private:
|
||||
static bool obj_enumerate(JSContext* cx, HandleObject obj);
|
||||
static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
|
||||
};
|
||||
|
||||
class StrictArgumentsObject : public ArgumentsObject
|
||||
class UnmappedArgumentsObject : public ArgumentsObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
private:
|
||||
static bool obj_enumerate(JSContext* cx, HandleObject obj);
|
||||
static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
@ -341,7 +352,7 @@ template<>
|
|||
inline bool
|
||||
JSObject::is<js::ArgumentsObject>() const
|
||||
{
|
||||
return is<js::NormalArgumentsObject>() || is<js::StrictArgumentsObject>();
|
||||
return is<js::MappedArgumentsObject>() || is<js::UnmappedArgumentsObject>();
|
||||
}
|
||||
|
||||
#endif /* vm_ArgumentsObject_h */
|
||||
|
|
|
@ -99,7 +99,6 @@ const Class ArrayBufferObject::protoClass = {
|
|||
|
||||
const Class ArrayBufferObject::class_ = {
|
||||
"ArrayBuffer",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_DELAY_METADATA_CALLBACK |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
|
||||
|
|
|
@ -2594,7 +2594,7 @@ Debugger::finalize(FreeOp* fop, JSObject* obj)
|
|||
|
||||
const Class Debugger::jsclass = {
|
||||
"Debugger",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, Debugger::finalize,
|
||||
|
@ -4483,7 +4483,7 @@ DebuggerScript_trace(JSTracer* trc, JSObject* obj)
|
|||
|
||||
const Class DebuggerScript_class = {
|
||||
"Script",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
|
@ -5491,7 +5491,7 @@ DebuggerSource_trace(JSTracer* trc, JSObject* obj)
|
|||
|
||||
const Class DebuggerSource_class = {
|
||||
"Source",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
|
@ -6617,7 +6617,7 @@ DebuggerObject_trace(JSTracer* trc, JSObject* obj)
|
|||
|
||||
const Class DebuggerObject_class = {
|
||||
"Object",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
|
@ -7563,7 +7563,7 @@ DebuggerEnv_trace(JSTracer* trc, JSObject* obj)
|
|||
|
||||
const Class DebuggerEnv_class = {
|
||||
"Environment",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
|
|
|
@ -75,7 +75,7 @@ DebuggerMemory::construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
/* static */ const Class DebuggerMemory::class_ = {
|
||||
"Memory",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_COUNT)
|
||||
};
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче