This commit is contained in:
Ryan VanderMeulen 2015-09-02 14:53:48 -04:00
Родитель 95ed3be802 6abd4c751d
Коммит 35369d17c7
125 изменённых файлов: 2910 добавлений и 2101 удалений

Просмотреть файл

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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше