зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central and inbound
This commit is contained in:
Коммит
e41a3e1c8a
|
@ -31,6 +31,7 @@
|
||||||
gestures.forEach(AccessFuTest.addSequence);
|
gestures.forEach(AccessFuTest.addSequence);
|
||||||
AccessFuTest.addFunc(stopGestureTracker);
|
AccessFuTest.addFunc(stopGestureTracker);
|
||||||
AccessFuTest.waitForExplicitFinish();
|
AccessFuTest.waitForExplicitFinish();
|
||||||
|
Logger.logLevel = Logger.DEBUG;
|
||||||
AccessFuTest.runTests();
|
AccessFuTest.runTests();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,11 +156,6 @@ pref("browser.search.suggest.enabled", true);
|
||||||
// tell the search service that we don't really expose the "current engine"
|
// tell the search service that we don't really expose the "current engine"
|
||||||
pref("browser.search.noCurrentEngine", true);
|
pref("browser.search.noCurrentEngine", true);
|
||||||
|
|
||||||
// Enable sparse localization by setting a few package locale overrides
|
|
||||||
pref("chrome.override_package.global", "b2g-l10n");
|
|
||||||
pref("chrome.override_package.mozapps", "b2g-l10n");
|
|
||||||
pref("chrome.override_package.passwordmgr", "b2g-l10n");
|
|
||||||
|
|
||||||
// enable xul error pages
|
// enable xul error pages
|
||||||
pref("browser.xul.error_pages.enabled", true);
|
pref("browser.xul.error_pages.enabled", true);
|
||||||
|
|
||||||
|
@ -899,9 +894,9 @@ pref("osfile.reset_worker_delay", 5000);
|
||||||
pref("apz.asyncscroll.throttle", 40);
|
pref("apz.asyncscroll.throttle", 40);
|
||||||
pref("apz.pan_repaint_interval", 16);
|
pref("apz.pan_repaint_interval", 16);
|
||||||
|
|
||||||
// Maximum fling velocity in inches/ms. Slower devices may need to reduce this
|
// APZ physics settings, tuned by UX designers
|
||||||
// to avoid checkerboarding. Note, float value must be set as a string.
|
pref("apz.max_velocity_inches_per_ms", "0.07");
|
||||||
pref("apz.max_velocity_inches_per_ms", "0.0375");
|
pref("apz.fling_friction", "0.003");
|
||||||
|
|
||||||
// Tweak default displayport values to reduce the risk of running out of
|
// Tweak default displayport values to reduce the risk of running out of
|
||||||
// memory when zooming in
|
// memory when zooming in
|
||||||
|
|
|
@ -5,3 +5,8 @@
|
||||||
#filter substitution
|
#filter substitution
|
||||||
|
|
||||||
pref("general.useragent.locale", "@AB_CD@");
|
pref("general.useragent.locale", "@AB_CD@");
|
||||||
|
|
||||||
|
// Enable sparse localization by setting a few package locale overrides
|
||||||
|
pref("chrome.override_package.global", "b2g-l10n");
|
||||||
|
pref("chrome.override_package.mozapps", "b2g-l10n");
|
||||||
|
pref("chrome.override_package.passwordmgr", "b2g-l10n");
|
||||||
|
|
Двоичные данные
build/pgo/certs/cert8.db
Двоичные данные
build/pgo/certs/cert8.db
Двоичный файл не отображается.
Двоичные данные
build/pgo/certs/key3.db
Двоичные данные
build/pgo/certs/key3.db
Двоичный файл не отображается.
|
@ -212,3 +212,5 @@ http://example.cn:80 privileged
|
||||||
http://example.co.jp:80 privileged
|
http://example.co.jp:80 privileged
|
||||||
http://example.fi:80 privileged
|
http://example.fi:80 privileged
|
||||||
|
|
||||||
|
# Hosts for testing marketplace apps installations
|
||||||
|
https://marketplace.firefox.com:443 privileged
|
||||||
|
|
12
configure.in
12
configure.in
|
@ -5964,18 +5964,6 @@ if test "$MOZ_GAMEPAD"; then
|
||||||
MOZ_GAMEPAD_BACKEND=cocoa
|
MOZ_GAMEPAD_BACKEND=cocoa
|
||||||
;;
|
;;
|
||||||
WINNT)
|
WINNT)
|
||||||
if test -z "$MOZ_HAS_WINSDK_WITH_D3D"; then
|
|
||||||
if test -n "$MOZ_DIRECTX_SDK_PATH" ; then
|
|
||||||
if ! test -f "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_DIRECTX_SDK_CPU_SUFFIX/dxguid.lib ; then
|
|
||||||
MOZ_GAMEPAD=
|
|
||||||
fi
|
|
||||||
elif test "$GCC" != "yes"; then
|
|
||||||
MOZ_GAMEPAD=
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if test -z "$MOZ_GAMEPAD"; then
|
|
||||||
AC_MSG_ERROR([Couldn't find the DirectX SDK, needed for gamepad support. Please install it or, reconfigure with --disable-gamepad to disable gamepad support.])
|
|
||||||
fi
|
|
||||||
MOZ_GAMEPAD_BACKEND=windows
|
MOZ_GAMEPAD_BACKEND=windows
|
||||||
;;
|
;;
|
||||||
Linux)
|
Linux)
|
||||||
|
|
|
@ -3209,7 +3209,7 @@ nsObjectLoadingContent::LegacyCall(JSContext* aCx,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < args.length(); i++) {
|
for (size_t i = 0; i < args.length(); i++) {
|
||||||
if (!JS_WrapValue(aCx, args.handleAt(i))) {
|
if (!JS_WrapValue(aCx, args[i])) {
|
||||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||||
return JS::UndefinedValue();
|
return JS::UndefinedValue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public:
|
||||||
|
|
||||||
virtual ReentrantMonitor& GetReentrantMonitor() MOZ_OVERRIDE;
|
virtual ReentrantMonitor& GetReentrantMonitor() MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual bool IsShutdown() const MOZ_FINAL MOZ_OVERRIDE;
|
virtual bool IsShutdown() const MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual bool OnStateMachineThread() const MOZ_OVERRIDE;
|
virtual bool OnStateMachineThread() const MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
@ -40,11 +40,11 @@ public:
|
||||||
|
|
||||||
virtual MediaResource* GetResource() const MOZ_OVERRIDE;
|
virtual MediaResource* GetResource() const MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE;
|
virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_FINAL MOZ_OVERRIDE;
|
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual int64_t GetEndMediaTime() const MOZ_FINAL MOZ_OVERRIDE;
|
virtual int64_t GetEndMediaTime() const MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual int64_t GetMediaDuration() MOZ_OVERRIDE;
|
virtual int64_t GetMediaDuration() MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
@ -56,25 +56,25 @@ public:
|
||||||
|
|
||||||
virtual void SetTransportSeekable(bool aTransportSeekable) MOZ_OVERRIDE;
|
virtual void SetTransportSeekable(bool aTransportSeekable) MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
|
virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_OVERRIDE;
|
||||||
virtual layers::ImageContainer* GetImageContainer() MOZ_OVERRIDE;
|
virtual layers::ImageContainer* GetImageContainer() MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual bool IsTransportSeekable() MOZ_FINAL MOZ_OVERRIDE;
|
virtual bool IsTransportSeekable() MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual bool IsMediaSeekable() MOZ_FINAL MOZ_OVERRIDE;
|
virtual bool IsMediaSeekable() MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual void MetadataLoaded(int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
|
virtual void MetadataLoaded(int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags) MOZ_OVERRIDE;
|
||||||
virtual void QueueMetadata(int64_t aTime, int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
|
virtual void QueueMetadata(int64_t aTime, int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags) MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual void SetMediaEndTime(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
|
virtual void SetMediaEndTime(int64_t aTime) MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual void UpdatePlaybackPosition(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
|
virtual void UpdatePlaybackPosition(int64_t aTime) MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual void OnReadMetadataCompleted() MOZ_FINAL MOZ_OVERRIDE;
|
virtual void OnReadMetadataCompleted() MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual MediaDecoderOwner* GetOwner() MOZ_OVERRIDE;
|
virtual MediaDecoderOwner* GetOwner() MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual void NotifyWaitingForResourcesStatusChanged() MOZ_FINAL MOZ_OVERRIDE;
|
virtual void NotifyWaitingForResourcesStatusChanged() MOZ_OVERRIDE;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// This monitor object is not really used to synchronize access to anything.
|
// This monitor object is not really used to synchronize access to anything.
|
||||||
|
|
|
@ -164,6 +164,14 @@ VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||||
if (!chunk.IsNull()) {
|
if (!chunk.IsNull()) {
|
||||||
gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize();
|
gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize();
|
||||||
gfxIntSize intrinsicSize = chunk.mFrame.GetIntrinsicSize();
|
gfxIntSize intrinsicSize = chunk.mFrame.GetIntrinsicSize();
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
// Block the video frames come from video source.
|
||||||
|
if (chunk.mFrame.GetImage()->GetFormat() != ImageFormat::PLANAR_YCBCR) {
|
||||||
|
LOG("Can't encode this ImageFormat %x", chunk.mFrame.GetImage()->GetFormat());
|
||||||
|
NotifyCancel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
nsresult rv = Init(imgsize.width, imgsize.height,
|
nsresult rv = Init(imgsize.width, imgsize.height,
|
||||||
intrinsicSize.width, intrinsicSize.height,
|
intrinsicSize.width, intrinsicSize.height,
|
||||||
aTrackRate);
|
aTrackRate);
|
||||||
|
|
|
@ -89,7 +89,9 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeSwitchVideoReaders(aTimeThreshold);
|
if (MaybeSwitchVideoReaders(aTimeThreshold)) {
|
||||||
|
GetVideoReader()->DecodeToTarget(aTimeThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
bool rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
|
bool rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
|
||||||
|
|
||||||
|
@ -129,7 +131,7 @@ public:
|
||||||
void CallDecoderInitialization();
|
void CallDecoderInitialization();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void MaybeSwitchVideoReaders(int64_t aTimeThreshold) {
|
bool MaybeSwitchVideoReaders(int64_t aTimeThreshold) {
|
||||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||||
MOZ_ASSERT(mActiveVideoDecoder != -1);
|
MOZ_ASSERT(mActiveVideoDecoder != -1);
|
||||||
|
|
||||||
|
@ -146,10 +148,11 @@ private:
|
||||||
MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
|
MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
|
||||||
|
|
||||||
GetVideoReader()->SetActive();
|
GetVideoReader()->SetActive();
|
||||||
GetVideoReader()->DecodeToTarget(aTimeThreshold);
|
return true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaDecoderReader* GetAudioReader() {
|
MediaDecoderReader* GetAudioReader() {
|
||||||
|
|
|
@ -66,6 +66,12 @@ SubBufferDecoder::GetResource() const
|
||||||
return static_cast<SourceBufferResource*>(mResource.get());
|
return static_cast<SourceBufferResource*>(mResource.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SubBufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded)
|
||||||
|
{
|
||||||
|
return mParentDecoder->NotifyDecodedFrames(aParsed, aDecoded);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SubBufferDecoder::SetMediaDuration(int64_t aDuration)
|
SubBufferDecoder::SetMediaDuration(int64_t aDuration)
|
||||||
{
|
{
|
||||||
|
@ -192,12 +198,8 @@ SourceBuffer::GetBuffered(ErrorResult& aRv)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
nsRefPtr<TimeRanges> ranges = new TimeRanges();
|
nsRefPtr<TimeRanges> ranges = new TimeRanges();
|
||||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
if (mDecoder) {
|
||||||
nsRefPtr<TimeRanges> r = new TimeRanges();
|
mDecoder->GetBuffered(ranges);
|
||||||
mDecoders[i]->GetBuffered(r);
|
|
||||||
if (r->Length() > 0) {
|
|
||||||
ranges->Add(r->GetStartTime(), r->GetEndTime());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ranges->Normalize();
|
ranges->Normalize();
|
||||||
return ranges.forget();
|
return ranges.forget();
|
||||||
|
@ -264,10 +266,10 @@ SourceBuffer::Abort(ErrorResult& aRv)
|
||||||
mAppendWindowStart = 0;
|
mAppendWindowStart = 0;
|
||||||
mAppendWindowEnd = PositiveInfinity<double>();
|
mAppendWindowEnd = PositiveInfinity<double>();
|
||||||
|
|
||||||
MSE_DEBUG("%p Abort: Discarding decoders.", this);
|
MSE_DEBUG("%p Abort: Discarding decoder.", this);
|
||||||
if (mCurrentDecoder) {
|
if (mDecoder) {
|
||||||
mCurrentDecoder->GetResource()->Ended();
|
mDecoder->GetResource()->Ended();
|
||||||
mCurrentDecoder = nullptr;
|
mDecoder = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,16 +296,15 @@ void
|
||||||
SourceBuffer::Detach()
|
SourceBuffer::Detach()
|
||||||
{
|
{
|
||||||
Ended();
|
Ended();
|
||||||
mDecoders.Clear();
|
mDecoder = nullptr;
|
||||||
mCurrentDecoder = nullptr;
|
|
||||||
mMediaSource = nullptr;
|
mMediaSource = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SourceBuffer::Ended()
|
SourceBuffer::Ended()
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
if (mDecoder) {
|
||||||
mDecoders[i]->GetResource()->Ended();
|
mDecoder->GetResource()->Ended();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,6 +317,7 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||||
, mTimestampOffset(0)
|
, mTimestampOffset(0)
|
||||||
, mAppendMode(SourceBufferAppendMode::Segments)
|
, mAppendMode(SourceBufferAppendMode::Segments)
|
||||||
, mUpdating(false)
|
, mUpdating(false)
|
||||||
|
, mDecoderInit(false)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aMediaSource);
|
MOZ_ASSERT(aMediaSource);
|
||||||
if (mType.EqualsIgnoreCase("video/webm") || mType.EqualsIgnoreCase("audio/webm")) {
|
if (mType.EqualsIgnoreCase("video/webm") || mType.EqualsIgnoreCase("audio/webm")) {
|
||||||
|
@ -324,6 +326,8 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||||
// XXX: Plug in parsers for MPEG4, etc. here.
|
// XXX: Plug in parsers for MPEG4, etc. here.
|
||||||
mParser = new ContainerParser();
|
mParser = new ContainerParser();
|
||||||
}
|
}
|
||||||
|
MSE_DEBUG("%p SourceBuffer: Creating initial decoder.", this);
|
||||||
|
InitNewDecoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<SourceBuffer>
|
already_AddRefed<SourceBuffer>
|
||||||
|
@ -335,8 +339,8 @@ SourceBuffer::Create(MediaSource* aMediaSource, const nsACString& aType)
|
||||||
|
|
||||||
SourceBuffer::~SourceBuffer()
|
SourceBuffer::~SourceBuffer()
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
if (mDecoder) {
|
||||||
mDecoders[i]->GetResource()->Ended();
|
mDecoder->GetResource()->Ended();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,11 +379,7 @@ SourceBuffer::InitNewDecoder()
|
||||||
if (!decoder) {
|
if (!decoder) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mDecoders.AppendElement(decoder);
|
mDecoder = decoder;
|
||||||
// XXX: At this point, we really want to push through any remaining
|
|
||||||
// processing for the old decoder and discard it, rather than hanging on
|
|
||||||
// to all of them in mDecoders.
|
|
||||||
mCurrentDecoder = decoder;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,21 +424,24 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||||
// TODO: Test buffer full flag.
|
// TODO: Test buffer full flag.
|
||||||
StartUpdating();
|
StartUpdating();
|
||||||
// TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
|
// TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
|
||||||
if (mParser->IsInitSegmentPresent(aData, aLength) || !mCurrentDecoder) {
|
if (!mDecoder || mParser->IsInitSegmentPresent(aData, aLength)) {
|
||||||
MSE_DEBUG("%p AppendBuffer: New initialization segment, switching decoders.", this);
|
if (!mDecoder || mDecoderInit) {
|
||||||
if (mCurrentDecoder) {
|
MSE_DEBUG("%p AppendBuffer: New initialization segment, creating decoder.", this);
|
||||||
mCurrentDecoder->GetResource()->Ended();
|
mDecoder->GetResource()->Ended();
|
||||||
}
|
|
||||||
if (!InitNewDecoder()) {
|
if (!InitNewDecoder()) {
|
||||||
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
|
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MSE_DEBUG("%p AppendBuffer: Decoder marked as initialized.", this);
|
||||||
|
mDecoderInit = true;
|
||||||
|
}
|
||||||
// XXX: For future reference: NDA call must run on the main thread.
|
// XXX: For future reference: NDA call must run on the main thread.
|
||||||
mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
|
mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
|
||||||
aLength,
|
aLength,
|
||||||
mCurrentDecoder->GetResource()->GetLength());
|
mDecoder->GetResource()->GetLength());
|
||||||
mCurrentDecoder->GetResource()->AppendData(aData, aLength);
|
mDecoder->GetResource()->AppendData(aData, aLength);
|
||||||
|
|
||||||
// Eviction uses a byte threshold. If the buffer is greater than the
|
// Eviction uses a byte threshold. If the buffer is greater than the
|
||||||
// number of bytes then data is evicted. The time range for this
|
// number of bytes then data is evicted. The time range for this
|
||||||
|
@ -446,7 +449,7 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||||
// evict data before that range across all SourceBuffer's it knows
|
// evict data before that range across all SourceBuffer's it knows
|
||||||
// about.
|
// about.
|
||||||
const int evict_threshold = 1000000;
|
const int evict_threshold = 1000000;
|
||||||
bool evicted = mCurrentDecoder->GetResource()->EvictData(evict_threshold);
|
bool evicted = mDecoder->GetResource()->EvictData(evict_threshold);
|
||||||
if (evicted) {
|
if (evicted) {
|
||||||
double start = 0.0;
|
double start = 0.0;
|
||||||
double end = 0.0;
|
double end = 0.0;
|
||||||
|
@ -479,14 +482,15 @@ SourceBuffer::GetBufferedStartEndTime(double* aStart, double* aEnd)
|
||||||
void
|
void
|
||||||
SourceBuffer::Evict(double aStart, double aEnd)
|
SourceBuffer::Evict(double aStart, double aEnd)
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
if (!mDecoder) {
|
||||||
// Need to map time to byte offset then evict
|
return;
|
||||||
int64_t end = mDecoders[i]->ConvertToByteOffset(aEnd);
|
|
||||||
if (end <= 0) {
|
|
||||||
NS_WARNING("SourceBuffer::Evict failed");
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
mDecoders[i]->GetResource()->EvictBefore(end);
|
// Need to map time to byte offset then evict
|
||||||
|
int64_t end = mDecoder->ConvertToByteOffset(aEnd);
|
||||||
|
if (end > 0) {
|
||||||
|
mDecoder->GetResource()->EvictBefore(end);
|
||||||
|
} else {
|
||||||
|
NS_WARNING("SourceBuffer::Evict failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,10 +140,7 @@ private:
|
||||||
|
|
||||||
nsAutoPtr<ContainerParser> mParser;
|
nsAutoPtr<ContainerParser> mParser;
|
||||||
|
|
||||||
// XXX: We only want to keep the current decoder alive, but need a way to
|
nsRefPtr<SubBufferDecoder> mDecoder;
|
||||||
// query @buffered for everything this SourceBuffer is responsible for.
|
|
||||||
nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
|
|
||||||
nsRefPtr<SubBufferDecoder> mCurrentDecoder;
|
|
||||||
|
|
||||||
double mAppendWindowStart;
|
double mAppendWindowStart;
|
||||||
double mAppendWindowEnd;
|
double mAppendWindowEnd;
|
||||||
|
@ -152,6 +149,8 @@ private:
|
||||||
|
|
||||||
SourceBufferAppendMode mAppendMode;
|
SourceBufferAppendMode mAppendMode;
|
||||||
bool mUpdating;
|
bool mUpdating;
|
||||||
|
|
||||||
|
bool mDecoderInit;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
|
@ -40,6 +40,7 @@ public:
|
||||||
virtual bool OnStateMachineThread() const MOZ_OVERRIDE;
|
virtual bool OnStateMachineThread() const MOZ_OVERRIDE;
|
||||||
virtual bool OnDecodeThread() const MOZ_OVERRIDE;
|
virtual bool OnDecodeThread() const MOZ_OVERRIDE;
|
||||||
virtual SourceBufferResource* GetResource() const MOZ_OVERRIDE;
|
virtual SourceBufferResource* GetResource() const MOZ_OVERRIDE;
|
||||||
|
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_OVERRIDE;
|
||||||
virtual void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
virtual void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
||||||
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
||||||
virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;
|
virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;
|
||||||
|
|
|
@ -678,6 +678,7 @@ MediaEngineWebRTCVideoSource::Shutdown()
|
||||||
void
|
void
|
||||||
MediaEngineWebRTCVideoSource::AllocImpl() {
|
MediaEngineWebRTCVideoSource::AllocImpl() {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
|
||||||
|
|
||||||
mCameraControl = ICameraControl::Create(mCaptureIndex);
|
mCameraControl = ICameraControl::Create(mCaptureIndex);
|
||||||
if (mCameraControl) {
|
if (mCameraControl) {
|
||||||
|
@ -818,6 +819,7 @@ MediaEngineWebRTCVideoSource::OnError(CameraErrorContext aContext, CameraError a
|
||||||
void
|
void
|
||||||
MediaEngineWebRTCVideoSource::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
|
MediaEngineWebRTCVideoSource::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
|
||||||
{
|
{
|
||||||
|
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
|
||||||
mLastCapture =
|
mLastCapture =
|
||||||
static_cast<nsIDOMFile*>(new nsDOMMemoryFile(static_cast<void*>(aData),
|
static_cast<nsIDOMFile*>(new nsDOMMemoryFile(static_cast<void*>(aData),
|
||||||
static_cast<uint64_t>(aLength),
|
static_cast<uint64_t>(aLength),
|
||||||
|
|
|
@ -2244,8 +2244,7 @@ this.DOMApplicationRegistry = {
|
||||||
queuedDownload: {},
|
queuedDownload: {},
|
||||||
queuedPackageDownload: {},
|
queuedPackageDownload: {},
|
||||||
|
|
||||||
onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
|
onInstallSuccessAck: function(aManifestURL, aDontNeedNetwork) {
|
||||||
aDontNeedNetwork) {
|
|
||||||
// If we are offline, register to run when we'll be online.
|
// If we are offline, register to run when we'll be online.
|
||||||
if ((Services.io.offline) && !aDontNeedNetwork) {
|
if ((Services.io.offline) && !aDontNeedNetwork) {
|
||||||
let onlineWrapper = {
|
let onlineWrapper = {
|
||||||
|
@ -2345,7 +2344,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
|
||||||
|
|
||||||
let dir = this._getAppDir(aId).path;
|
let dir = this._getAppDir(aId).path;
|
||||||
let manFile = OS.Path.join(dir, manifestName);
|
let manFile = OS.Path.join(dir, manifestName);
|
||||||
this._writeFile(manFile, JSON.stringify(aJsonManifest));
|
return this._writeFile(manFile, JSON.stringify(aJsonManifest));
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add an app that is already installed to the registry.
|
// Add an app that is already installed to the registry.
|
||||||
|
@ -2418,7 +2417,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
confirmInstall: function(aData, aProfileDir, aInstallSuccessCallback) {
|
confirmInstall: Task.async(function*(aData, aProfileDir, aInstallSuccessCallback) {
|
||||||
debug("confirmInstall");
|
debug("confirmInstall");
|
||||||
|
|
||||||
let origin = Services.io.newURI(aData.app.origin, null, null);
|
let origin = Services.io.newURI(aData.app.origin, null, null);
|
||||||
|
@ -2443,7 +2442,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
|
||||||
let app = this._setupApp(aData, id);
|
let app = this._setupApp(aData, id);
|
||||||
|
|
||||||
let jsonManifest = aData.isPackage ? app.updateManifest : app.manifest;
|
let jsonManifest = aData.isPackage ? app.updateManifest : app.manifest;
|
||||||
this._writeManifestFile(id, aData.isPackage, jsonManifest);
|
yield this._writeManifestFile(id, aData.isPackage, jsonManifest);
|
||||||
|
|
||||||
debug("app.origin: " + app.origin);
|
debug("app.origin: " + app.origin);
|
||||||
let manifest = new ManifestHelper(jsonManifest, app.origin);
|
let manifest = new ManifestHelper(jsonManifest, app.origin);
|
||||||
|
@ -2477,42 +2476,15 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
|
||||||
aData.app[prop] = appObject[prop];
|
aData.app[prop] = appObject[prop];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dontNeedNetwork = false;
|
||||||
|
|
||||||
if (manifest.appcache_path) {
|
if (manifest.appcache_path) {
|
||||||
this.queuedDownload[app.manifestURL] = {
|
this.queuedDownload[app.manifestURL] = {
|
||||||
manifest: manifest,
|
manifest: manifest,
|
||||||
app: appObject,
|
app: appObject,
|
||||||
profileDir: aProfileDir
|
profileDir: aProfileDir
|
||||||
}
|
}
|
||||||
}
|
} else if (manifest.package_path) {
|
||||||
|
|
||||||
// We notify about the successful installation via mgmt.oninstall and the
|
|
||||||
// corresponging DOMRequest.onsuccess event as soon as the app is properly
|
|
||||||
// saved in the registry.
|
|
||||||
this._saveApps().then(() => {
|
|
||||||
this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
|
|
||||||
if (aData.isPackage && aData.autoInstall) {
|
|
||||||
// Skip directly to onInstallSuccessAck, since there isn't
|
|
||||||
// a WebappsRegistry to receive Webapps:Install:Return:OK and respond
|
|
||||||
// Webapps:Install:Return:Ack when an app is being auto-installed.
|
|
||||||
this.onInstallSuccessAck(app.manifestURL);
|
|
||||||
} else {
|
|
||||||
// Broadcast Webapps:Install:Return:OK so the WebappsRegistry can notify
|
|
||||||
// the installing page about the successful install, after which it'll
|
|
||||||
// respond Webapps:Install:Return:Ack, which calls onInstallSuccessAck.
|
|
||||||
this.broadcastMessage("Webapps:Install:Return:OK", aData);
|
|
||||||
}
|
|
||||||
if (!aData.isPackage) {
|
|
||||||
this.updateAppHandlers(null, app.manifest, app);
|
|
||||||
if (aInstallSuccessCallback) {
|
|
||||||
aInstallSuccessCallback(app.manifest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Services.obs.notifyObservers(null, "webapps-installed",
|
|
||||||
JSON.stringify({ manifestURL: app.manifestURL }));
|
|
||||||
});
|
|
||||||
|
|
||||||
let dontNeedNetwork = false;
|
|
||||||
if (manifest.package_path) {
|
|
||||||
// If it is a local app then it must been installed from a local file
|
// If it is a local app then it must been installed from a local file
|
||||||
// instead of web.
|
// instead of web.
|
||||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||||
|
@ -2537,12 +2509,40 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We notify about the successful installation via mgmt.oninstall and the
|
||||||
|
// corresponding DOMRequest.onsuccess event as soon as the app is properly
|
||||||
|
// saved in the registry.
|
||||||
|
yield this._saveApps();
|
||||||
|
|
||||||
|
this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
|
||||||
|
if (aData.isPackage && aData.autoInstall) {
|
||||||
|
// Skip directly to onInstallSuccessAck, since there isn't
|
||||||
|
// a WebappsRegistry to receive Webapps:Install:Return:OK and respond
|
||||||
|
// Webapps:Install:Return:Ack when an app is being auto-installed.
|
||||||
|
this.onInstallSuccessAck(app.manifestURL);
|
||||||
|
} else {
|
||||||
|
// Broadcast Webapps:Install:Return:OK so the WebappsRegistry can notify
|
||||||
|
// the installing page about the successful install, after which it'll
|
||||||
|
// respond Webapps:Install:Return:Ack, which calls onInstallSuccessAck.
|
||||||
|
this.broadcastMessage("Webapps:Install:Return:OK", aData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aData.isPackage) {
|
||||||
|
this.updateAppHandlers(null, app.manifest, app);
|
||||||
|
if (aInstallSuccessCallback) {
|
||||||
|
aInstallSuccessCallback(app.manifest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.obs.notifyObservers(null, "webapps-installed",
|
||||||
|
JSON.stringify({ manifestURL: app.manifestURL }));
|
||||||
|
|
||||||
if (aData.forceSuccessAck) {
|
if (aData.forceSuccessAck) {
|
||||||
// If it's a local install, there's no content process so just
|
// If it's a local install, there's no content process so just
|
||||||
// ack the install.
|
// ack the install.
|
||||||
this.onInstallSuccessAck(app.manifestURL, dontNeedNetwork);
|
this.onInstallSuccessAck(app.manifestURL, dontNeedNetwork);
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install the package after successfully downloading it
|
* Install the package after successfully downloading it
|
||||||
|
@ -3104,7 +3104,10 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
|
||||||
throw "CERTDB_ERROR";
|
throw "CERTDB_ERROR";
|
||||||
}
|
}
|
||||||
|
|
||||||
let [result, zipReader] = yield this._openSignedPackage(aZipFile, certDb);
|
let [result, zipReader] = yield this._openSignedPackage(aApp.installOrigin,
|
||||||
|
aApp.manifestURL,
|
||||||
|
aZipFile,
|
||||||
|
certDb);
|
||||||
|
|
||||||
// We cannot really know if the system date is correct or
|
// We cannot really know if the system date is correct or
|
||||||
// not. What we can know is if it's after the build date or not,
|
// not. What we can know is if it's after the build date or not,
|
||||||
|
@ -3147,11 +3150,39 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
|
||||||
}).bind(this));
|
}).bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
_openSignedPackage: function(aZipFile, aCertDb) {
|
_openSignedPackage: function(aInstallOrigin, aManifestURL, aZipFile, aCertDb) {
|
||||||
let deferred = Promise.defer();
|
let deferred = Promise.defer();
|
||||||
|
|
||||||
|
let root = TrustedRootCertificate.index;
|
||||||
|
|
||||||
|
let useReviewerCerts = false;
|
||||||
|
try {
|
||||||
|
useReviewerCerts = Services.prefs.
|
||||||
|
getBoolPref("dom.mozApps.use_reviewer_certs");
|
||||||
|
} catch (ex) { }
|
||||||
|
|
||||||
|
// We'll use the reviewer and dev certificates only if the pref is set to
|
||||||
|
// true.
|
||||||
|
if (useReviewerCerts) {
|
||||||
|
let manifestPath = Services.io.newURI(aManifestURL, null, null).path;
|
||||||
|
|
||||||
|
switch (aInstallOrigin) {
|
||||||
|
case "https://marketplace.firefox.com":
|
||||||
|
root = manifestPath.startsWith("/reviewers/")
|
||||||
|
? Ci.nsIX509CertDB.AppMarketplaceProdReviewersRoot
|
||||||
|
: Ci.nsIX509CertDB.AppMarketplaceProdPublicRoot;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "https://marketplace-dev.allizom.org":
|
||||||
|
root = manifestPath.startsWith("/reviewers/")
|
||||||
|
? Ci.nsIX509CertDB.AppMarketplaceDevReviewersRoot
|
||||||
|
: Ci.nsIX509CertDB.AppMarketplaceDevPublicRoot;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
aCertDb.openSignedAppFileAsync(
|
aCertDb.openSignedAppFileAsync(
|
||||||
TrustedRootCertificate.index, aZipFile,
|
root, aZipFile,
|
||||||
function(aRv, aZipReader) {
|
function(aRv, aZipReader) {
|
||||||
deferred.resolve([aRv, aZipReader]);
|
deferred.resolve([aRv, aZipReader]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"version" : "2.0",
|
||||||
|
"name" : "Flashlight (Linterna)",
|
||||||
|
"description" : "Simple Flashlight that you can use everywhere without internet connection and also with cool modes: * Flashlight mode - * Disco mode - * Colors mode",
|
||||||
|
"launch_path" : "/index.html",
|
||||||
|
"icons": {
|
||||||
|
"16": "/img/icons/mortar-16.png",
|
||||||
|
"48": "/img/icons/mortar-48.png",
|
||||||
|
"60": "/img/icons/mortar-60.png",
|
||||||
|
"128": "/img/icons/mortar-128.png"
|
||||||
|
},
|
||||||
|
"developer": {
|
||||||
|
"name": "William Vargas",
|
||||||
|
"url" : "https://twitter.com/tecnowilliam"
|
||||||
|
},
|
||||||
|
"installs_allowed_from": ["*"],
|
||||||
|
"locales": {
|
||||||
|
"es": {
|
||||||
|
"description": "Una simple linterna que puedes utilizar en cualquier lugar sin conexión a internet y con opciones geniales: * Modo Linterna - * Modo Disco - * Modo Colores",
|
||||||
|
"developer": {
|
||||||
|
"name": "William Vargas",
|
||||||
|
"url" : "https://twitter.com/tecnowilliam"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default_locale": "en",
|
||||||
|
"package_path": "marketplace_app.zip"
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Content-Type: application/x-web-app-manifest+json
|
Двоичный файл не отображается.
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.2",
|
||||||
|
"name": "KitchenSink",
|
||||||
|
"description": "Tests and report APIs available on the device",
|
||||||
|
"launch_path": "/index.html",
|
||||||
|
"developer": {
|
||||||
|
"name": "Piotr Zalewa",
|
||||||
|
"url": "http://www.mozillalabs.com"
|
||||||
|
},
|
||||||
|
"icons": {
|
||||||
|
"16": "/img/icons/logo-16.png",
|
||||||
|
"32": "/img/icons/logo-32.png",
|
||||||
|
"64": "/img/icons/logo-64.png",
|
||||||
|
"128": "/img/icons/logo-128.png",
|
||||||
|
"256": "/img/icons/logo-256.png"
|
||||||
|
},
|
||||||
|
"type": "privileged",
|
||||||
|
"permissions": {
|
||||||
|
"alarms": {
|
||||||
|
"description": "Testing"
|
||||||
|
},
|
||||||
|
"browser": {
|
||||||
|
"description": "Testing"
|
||||||
|
},
|
||||||
|
"geolocation": {
|
||||||
|
"description": "Testing"
|
||||||
|
},
|
||||||
|
"contacts": {
|
||||||
|
"access": "readwrite",
|
||||||
|
"description": "Testing"
|
||||||
|
},
|
||||||
|
"device-storage:sdcard": {
|
||||||
|
"access": "readwrite",
|
||||||
|
"description": "Testing"
|
||||||
|
},
|
||||||
|
"fmradio": {
|
||||||
|
"description": "Testing"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"description": "Testing"
|
||||||
|
},
|
||||||
|
"systemXHR": {
|
||||||
|
"description": "Testing"
|
||||||
|
},
|
||||||
|
"tcp-socket": {
|
||||||
|
"description": "Testing"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"package_path": "marketplace_privileged_app.zip"
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Content-Type: application/x-web-app-manifest+json
|
Двоичный файл не отображается.
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "Stopwatch",
|
||||||
|
"description": "Simple stopwatch",
|
||||||
|
"launch_path": "/index.html",
|
||||||
|
"icons": {
|
||||||
|
"128": "/static/img/icon.png"
|
||||||
|
},
|
||||||
|
"developer": {
|
||||||
|
"name": "Andy McKay",
|
||||||
|
"url": "http://www.agmweb.ca/blog/andy/"
|
||||||
|
},
|
||||||
|
"locales": {
|
||||||
|
"fr": {
|
||||||
|
"description": "Simple chronomètre"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installs_allowed_from": ["*"],
|
||||||
|
"default_locale": "en",
|
||||||
|
"package_path": "marketplace_reviewers_app.zip"
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Content-Type: application/x-web-app-manifest+json
|
Двоичный файл не отображается.
|
@ -14,10 +14,14 @@ support-files =
|
||||||
signed_app_template.webapp
|
signed_app_template.webapp
|
||||||
signed/*
|
signed/*
|
||||||
test_packaged_app_common.js
|
test_packaged_app_common.js
|
||||||
|
marketplace/*
|
||||||
|
pkg_install_iframe.html
|
||||||
|
|
||||||
[test_app_update.html]
|
[test_app_update.html]
|
||||||
[test_bug_795164.html]
|
[test_bug_795164.html]
|
||||||
[test_install_receipts.html]
|
[test_install_receipts.html]
|
||||||
|
[test_marketplace_pkg_install.html]
|
||||||
|
skip-if = buildapp == "b2g" || toolkit == "android" # see bug 989806
|
||||||
[test_packaged_app_install.html]
|
[test_packaged_app_install.html]
|
||||||
[test_packaged_app_update.html]
|
[test_packaged_app_update.html]
|
||||||
[test_receipt_operations.html]
|
[test_receipt_operations.html]
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Cross Origin Helper</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="application/javascript">
|
||||||
|
|
||||||
|
window.addEventListener("message", function onMessage(event) {
|
||||||
|
window.removeEventListener("message", onMessage, false);
|
||||||
|
|
||||||
|
var request = navigator.mozApps.installPackage(event.data);
|
||||||
|
|
||||||
|
request.onerror = function() {
|
||||||
|
parent.postMessage("Error: " + this.error.name, "*");
|
||||||
|
};
|
||||||
|
|
||||||
|
request.onsuccess = function() {
|
||||||
|
parent.postMessage("Application installed", "*");
|
||||||
|
};
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -102,7 +102,7 @@ function readFile(path) {
|
||||||
|
|
||||||
function makeResource(templatePath, version, packagePath, packageSize,
|
function makeResource(templatePath, version, packagePath, packageSize,
|
||||||
appName, developerName, developerUrl) {
|
appName, developerName, developerUrl) {
|
||||||
var res = readFile(templatePath, false).
|
var res = readFile(templatePath).
|
||||||
replace(/VERSIONTOKEN/g, version).
|
replace(/VERSIONTOKEN/g, version).
|
||||||
replace(/PACKAGEPATHTOKEN/g, packagePath).
|
replace(/PACKAGEPATHTOKEN/g, packagePath).
|
||||||
replace(/PACKAGESIZETOKEN/g, packageSize).
|
replace(/PACKAGESIZETOKEN/g, packageSize).
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=989806
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 989806</title>
|
||||||
|
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<script type="text/javascript" src="test_packaged_app_common.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=989806">Mozilla Bug 989806</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content" style="display: none">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
<script class="testbody" type="application/javascript;version=1.7">
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
let gApp = null;
|
||||||
|
|
||||||
|
let gExternalInstallOrigin = "http://mochi.test:8888/";
|
||||||
|
let gExternalAppsPath = gExternalInstallOrigin + "tests/dom/apps/tests/marketplace/";
|
||||||
|
|
||||||
|
let gMarketplaceInstallOrigin = "https://marketplace.firefox.com/";
|
||||||
|
let gMarketplaceAppsPath = gMarketplaceInstallOrigin + "tests/dom/apps/tests/marketplace/";
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
|
function checkAppOnInstallSuccess(aExpected) {
|
||||||
|
navigator.mozApps.mgmt.oninstall = function(evt) {
|
||||||
|
info("Got oninstall event");
|
||||||
|
gApp = evt.application;
|
||||||
|
gApp.ondownloaderror = function() {
|
||||||
|
ok(false, "Download should succeed (got error: " +
|
||||||
|
gApp.downloadError.name + ")");
|
||||||
|
PackagedTestHelper.finish();
|
||||||
|
};
|
||||||
|
gApp.ondownloadsuccess = function() {
|
||||||
|
info("App downloaded");
|
||||||
|
PackagedTestHelper.checkAppState(gApp, aExpected.version, aExpected,
|
||||||
|
true, true, PackagedTestHelper.next);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAppOnInstallError(aExpectedError) {
|
||||||
|
navigator.mozApps.mgmt.oninstall = function(evt) {
|
||||||
|
info("Got oninstall event");
|
||||||
|
gApp = evt.application;
|
||||||
|
gApp.ondownloaderror = function() {
|
||||||
|
is(gApp.downloadError.name, aExpectedError,
|
||||||
|
"Download fails with expected error: " + aExpectedError);
|
||||||
|
if (gApp.downloadError.name != aExpectedError) {
|
||||||
|
PackagedTestHelper.finish();
|
||||||
|
} else {
|
||||||
|
PackagedTestHelper.next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
gApp.ondownloadsuccess = function() {
|
||||||
|
ok(false, "App download should fail");
|
||||||
|
PackagedTestHelper.finish();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkUninstallApp(aApp) {
|
||||||
|
let req = navigator.mozApps.mgmt.uninstall(aApp);
|
||||||
|
|
||||||
|
req.onsuccess = function() {
|
||||||
|
info("App uninstalled");
|
||||||
|
aApp.ondownloadsuccess = null;
|
||||||
|
aApp.ondownloaderror = null;
|
||||||
|
aApp.onprogress = null;
|
||||||
|
PackagedTestHelper.next();
|
||||||
|
};
|
||||||
|
req.onerror = function(evt) {
|
||||||
|
ok(false, "App uninstallation should succeed (got unexpected " +
|
||||||
|
evt.target.error.name + ")");
|
||||||
|
PackagedTestHelper.finish();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function installApp(installOrigin, manifestURL) {
|
||||||
|
let domParent = document.getElementById('container');
|
||||||
|
|
||||||
|
let ifr = document.createElement('iframe');
|
||||||
|
ifr.setAttribute('mozbrowser', 'true');
|
||||||
|
ifr.setAttribute("src", installOrigin + "tests/dom/apps/tests/pkg_install_iframe.html");
|
||||||
|
|
||||||
|
ifr.addEventListener("load", function onIFrameLoad() {
|
||||||
|
ifr.removeEventListener("load", onIFrameLoad, false);
|
||||||
|
|
||||||
|
ifr.contentWindow.postMessage(manifestURL, "*");
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
ifr.addEventListener("mozbrowsererror", function onCertError(e) {
|
||||||
|
ifr.removeEventListener("mozbrowsererror", onCertError);
|
||||||
|
|
||||||
|
ok(false, "mozbrowsererror: " + e.detail.type);
|
||||||
|
domParent.removeChild(ifr);
|
||||||
|
PackagedTestHelper.finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("message", function onMessage(event) {
|
||||||
|
window.removeEventListener("message", onMessage);
|
||||||
|
|
||||||
|
is(event.data, "Application installed", "Application installed");
|
||||||
|
|
||||||
|
domParent.removeChild(ifr);
|
||||||
|
});
|
||||||
|
|
||||||
|
domParent.appendChild(ifr);
|
||||||
|
}
|
||||||
|
|
||||||
|
PackagedTestHelper.setSteps([
|
||||||
|
function() {
|
||||||
|
SpecialPowers.setAllAppsLaunchable(true);
|
||||||
|
SpecialPowers.addPermission("webapps-manage", true, document);
|
||||||
|
SpecialPowers.addPermission("browser", true, document);
|
||||||
|
SpecialPowers.autoConfirmAppInstall(() =>
|
||||||
|
SpecialPowers.pushPrefEnv({set: [["dom.mozBrowserFramesEnabled", true]]},
|
||||||
|
PackagedTestHelper.next));
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
info("== TEST == Marketplace packaged app from https://marketplace.firefox.com/");
|
||||||
|
let miniManifestURL = gMarketplaceAppsPath + "marketplace_app.webapp"
|
||||||
|
let expected = {
|
||||||
|
name: "Flashlight (Linterna)",
|
||||||
|
manifestURL: miniManifestURL,
|
||||||
|
installOrigin: gMarketplaceInstallOrigin.slice(0, -1),
|
||||||
|
progress: 0,
|
||||||
|
installState: "installed",
|
||||||
|
downloadAvailable: false,
|
||||||
|
downloading: false,
|
||||||
|
readyToApplyDownload: false,
|
||||||
|
launch_path: "/index.html",
|
||||||
|
version: "2.0",
|
||||||
|
};
|
||||||
|
checkAppOnInstallSuccess(expected);
|
||||||
|
installApp(gMarketplaceInstallOrigin, miniManifestURL);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
info("== TEST == Marketplace privileged app from https://marketplace.firefox.com/");
|
||||||
|
let miniManifestURL = gMarketplaceAppsPath + "marketplace_privileged_app.webapp"
|
||||||
|
let expected = {
|
||||||
|
name: "KitchenSink",
|
||||||
|
manifestURL: miniManifestURL,
|
||||||
|
installOrigin: gMarketplaceInstallOrigin.slice(0, -1),
|
||||||
|
progress: 0,
|
||||||
|
installState: "installed",
|
||||||
|
downloadAvailable: false,
|
||||||
|
downloading: false,
|
||||||
|
readyToApplyDownload: false,
|
||||||
|
launch_path: "/index.html",
|
||||||
|
version: "0.2.2",
|
||||||
|
};
|
||||||
|
checkAppOnInstallSuccess(expected);
|
||||||
|
installApp(gMarketplaceInstallOrigin, miniManifestURL);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
info("== TEST == Marketplace reviewers packaged app from https://marketplace.firefox.com/");
|
||||||
|
checkAppOnInstallError("INVALID_SIGNATURE");
|
||||||
|
installApp(gMarketplaceInstallOrigin, gMarketplaceAppsPath + "marketplace_reviewers_app.webapp");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
info("== TEST == Marketplace packaged app not from https://marketplace.firefox.com/");
|
||||||
|
checkAppOnInstallError("INSTALL_FROM_DENIED");
|
||||||
|
installApp(gExternalInstallOrigin, gExternalAppsPath + "marketplace_app.webapp");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
info("== TEST == Marketplace privileged app not from https://marketplace.firefox.com/");
|
||||||
|
checkAppOnInstallError("INSTALL_FROM_DENIED");
|
||||||
|
installApp(gExternalInstallOrigin, gExternalAppsPath + "marketplace_privileged_app.webapp");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
info("== TEST == Marketplace reviewers packaged app not from https://marketplace.firefox.com/");
|
||||||
|
checkAppOnInstallError("INVALID_SIGNATURE");
|
||||||
|
installApp(gExternalInstallOrigin, gExternalAppsPath + "marketplace_reviewers_app.webapp");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
PackagedTestHelper.finish();
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
addLoadEvent(PackagedTestHelper.start);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
<div id="container"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -12,31 +12,18 @@ var PackagedTestHelper = (function PackagedTestHelper() {
|
||||||
var gAppName = "appname";
|
var gAppName = "appname";
|
||||||
var gApp = null;
|
var gApp = null;
|
||||||
var gInstallOrigin = "http://mochi.test:8888";
|
var gInstallOrigin = "http://mochi.test:8888";
|
||||||
var timeoutID;
|
|
||||||
|
|
||||||
function timeoutError() {
|
|
||||||
ok(false, "Timeout! Probably waiting on a app installation event");
|
|
||||||
info("Finishing this test suite!");
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
function debug(aMsg) {
|
function debug(aMsg) {
|
||||||
//dump("== PackageTestHelper debug == " + aMsg + "\n");
|
//dump("== PackageTestHelper debug == " + aMsg + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function next() {
|
function next() {
|
||||||
if (timeoutID) {
|
|
||||||
clearTimeout(timeoutID);
|
|
||||||
}
|
|
||||||
index += 1;
|
index += 1;
|
||||||
if (index >= steps.length) {
|
if (index >= steps.length) {
|
||||||
ok(false, "Shouldn't get here!");
|
ok(false, "Shouldn't get here!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// There's nothing here that should take more than 30 seconds, even on
|
|
||||||
// heavy loads. So there's no need to stop further tests for five minutes.
|
|
||||||
timeoutID = setTimeout(timeoutError, 30000);
|
|
||||||
steps[index]();
|
steps[index]();
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
ok(false, "Caught exception", ex);
|
ok(false, "Caught exception", ex);
|
||||||
|
@ -48,10 +35,8 @@ var PackagedTestHelper = (function PackagedTestHelper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function finish() {
|
function finish() {
|
||||||
if (timeoutID) {
|
|
||||||
clearTimeout(timeoutID);
|
|
||||||
}
|
|
||||||
SpecialPowers.removePermission("webapps-manage", document);
|
SpecialPowers.removePermission("webapps-manage", document);
|
||||||
|
SpecialPowers.removePermission("browser", document);
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +150,7 @@ var PackagedTestHelper = (function PackagedTestHelper() {
|
||||||
is(aApp.manifest.size, aExpectedApp.size, "Check size");
|
is(aApp.manifest.size, aExpectedApp.size, "Check size");
|
||||||
}
|
}
|
||||||
if (aApp.manifest) {
|
if (aApp.manifest) {
|
||||||
is(aApp.manifest.launch_path, gSJSPath, "Check launch path");
|
is(aApp.manifest.launch_path, aExpectedApp.launch_path || gSJSPath, "Check launch path");
|
||||||
}
|
}
|
||||||
if (aExpectedApp.manifestURL) {
|
if (aExpectedApp.manifestURL) {
|
||||||
is(aApp.manifestURL, aExpectedApp.manifestURL, "Check manifestURL");
|
is(aApp.manifestURL, aExpectedApp.manifestURL, "Check manifestURL");
|
||||||
|
|
|
@ -476,10 +476,7 @@ private:
|
||||||
arguments.AppendElement(value);
|
arguments.AppendElement(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
console->ProfileMethod(cx, mAction, arguments, error);
|
console->ProfileMethod(cx, mAction, arguments);
|
||||||
if (error.Failed()) {
|
|
||||||
NS_WARNING("Failed to call call profile() method to the ConsoleAPI.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -662,23 +659,20 @@ Console::TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Console::Profile(JSContext* aCx, const Sequence<JS::Value>& aData,
|
Console::Profile(JSContext* aCx, const Sequence<JS::Value>& aData)
|
||||||
ErrorResult& aRv)
|
|
||||||
{
|
{
|
||||||
ProfileMethod(aCx, NS_LITERAL_STRING("profile"), aData, aRv);
|
ProfileMethod(aCx, NS_LITERAL_STRING("profile"), aData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Console::ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData,
|
Console::ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData)
|
||||||
ErrorResult& aRv)
|
|
||||||
{
|
{
|
||||||
ProfileMethod(aCx, NS_LITERAL_STRING("profileEnd"), aData, aRv);
|
ProfileMethod(aCx, NS_LITERAL_STRING("profileEnd"), aData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
|
Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
|
||||||
const Sequence<JS::Value>& aData,
|
const Sequence<JS::Value>& aData)
|
||||||
ErrorResult& aRv)
|
|
||||||
{
|
{
|
||||||
if (!NS_IsMainThread()) {
|
if (!NS_IsMainThread()) {
|
||||||
// Here we are in a worker thread.
|
// Here we are in a worker thread.
|
||||||
|
@ -688,6 +682,8 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClearException ce(aCx);
|
||||||
|
|
||||||
RootedDictionary<ConsoleProfileEvent> event(aCx);
|
RootedDictionary<ConsoleProfileEvent> event(aCx);
|
||||||
event.mAction = aAction;
|
event.mAction = aAction;
|
||||||
|
|
||||||
|
@ -700,15 +696,14 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
|
||||||
|
|
||||||
JS::Rooted<JS::Value> eventValue(aCx);
|
JS::Rooted<JS::Value> eventValue(aCx);
|
||||||
if (!event.ToObject(aCx, &eventValue)) {
|
if (!event.ToObject(aCx, &eventValue)) {
|
||||||
aRv.Throw(NS_ERROR_FAILURE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS::Rooted<JSObject*> eventObj(aCx, &eventValue.toObject());
|
JS::Rooted<JSObject*> eventObj(aCx, &eventValue.toObject());
|
||||||
MOZ_ASSERT(eventObj);
|
MOZ_ASSERT(eventObj);
|
||||||
|
|
||||||
if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue, JSPROP_ENUMERATE)) {
|
if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue,
|
||||||
aRv.Throw(NS_ERROR_FAILURE);
|
JSPROP_ENUMERATE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,7 +712,6 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
|
||||||
const nsIID& iid = NS_GET_IID(nsISupports);
|
const nsIID& iid = NS_GET_IID(nsISupports);
|
||||||
|
|
||||||
if (NS_FAILED(xpc->WrapJS(aCx, eventObj, iid, getter_AddRefs(wrapper)))) {
|
if (NS_FAILED(xpc->WrapJS(aCx, eventObj, iid, getter_AddRefs(wrapper)))) {
|
||||||
aRv.Throw(NS_ERROR_FAILURE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,13 +830,14 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||||
ConsoleCallData* callData = new ConsoleCallData();
|
ConsoleCallData* callData = new ConsoleCallData();
|
||||||
mQueuedCalls.insertBack(callData);
|
mQueuedCalls.insertBack(callData);
|
||||||
|
|
||||||
|
ClearException ce(aCx);
|
||||||
|
|
||||||
callData->Initialize(aCx, aMethodName, aMethodString, aData);
|
callData->Initialize(aCx, aMethodName, aMethodString, aData);
|
||||||
RAII raii(mQueuedCalls);
|
RAII raii(mQueuedCalls);
|
||||||
|
|
||||||
if (mWindow) {
|
if (mWindow) {
|
||||||
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
|
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
|
||||||
if (!webNav) {
|
if (!webNav) {
|
||||||
Throw(aCx, NS_ERROR_FAILURE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -857,7 +852,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||||
nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, maxDepth);
|
nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, maxDepth);
|
||||||
|
|
||||||
if (!stack) {
|
if (!stack) {
|
||||||
Throw(aCx, NS_ERROR_FAILURE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -866,7 +860,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||||
uint32_t language;
|
uint32_t language;
|
||||||
nsresult rv = stack->GetLanguage(&language);
|
nsresult rv = stack->GetLanguage(&language);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
Throw(aCx, rv);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -877,7 +870,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||||
callData->mTopStackFrame.ref(),
|
callData->mTopStackFrame.ref(),
|
||||||
language);
|
language);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
Throw(aCx, rv);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,7 +879,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||||
nsCOMPtr<nsIStackFrame> caller;
|
nsCOMPtr<nsIStackFrame> caller;
|
||||||
rv = stack->GetCaller(getter_AddRefs(caller));
|
rv = stack->GetCaller(getter_AddRefs(caller));
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
Throw(aCx, rv);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -902,7 +893,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||||
callData->mReifiedStack.construct();
|
callData->mReifiedStack.construct();
|
||||||
nsresult rv = ReifyStack(stack, callData->mReifiedStack.ref());
|
nsresult rv = ReifyStack(stack, callData->mReifiedStack.ref());
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
Throw(aCx, rv);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -915,7 +905,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||||
ErrorResult rv;
|
ErrorResult rv;
|
||||||
nsRefPtr<nsPerformance> performance = win->GetPerformance(rv);
|
nsRefPtr<nsPerformance> performance = win->GetPerformance(rv);
|
||||||
if (rv.Failed()) {
|
if (rv.Failed()) {
|
||||||
Throw(aCx, rv.ErrorCode());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1111,7 +1100,6 @@ Console::ProcessCallData(ConsoleCallData* aData)
|
||||||
|
|
||||||
JS::Rooted<JS::Value> eventValue(cx);
|
JS::Rooted<JS::Value> eventValue(cx);
|
||||||
if (!event.ToObject(cx, &eventValue)) {
|
if (!event.ToObject(cx, &eventValue)) {
|
||||||
Throw(cx, NS_ERROR_FAILURE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,12 +87,10 @@ public:
|
||||||
TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime);
|
TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime);
|
||||||
|
|
||||||
void
|
void
|
||||||
Profile(JSContext* aCx, const Sequence<JS::Value>& aData,
|
Profile(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||||
ErrorResult& aRv);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData,
|
ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||||
ErrorResult& aRv);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Assert(JSContext* aCx, bool aCondition, const Sequence<JS::Value>& aData);
|
Assert(JSContext* aCx, bool aCondition, const Sequence<JS::Value>& aData);
|
||||||
|
@ -181,8 +179,7 @@ private:
|
||||||
|
|
||||||
void
|
void
|
||||||
ProfileMethod(JSContext* aCx, const nsAString& aAction,
|
ProfileMethod(JSContext* aCx, const nsAString& aAction,
|
||||||
const Sequence<JS::Value>& aData,
|
const Sequence<JS::Value>& aData);
|
||||||
ErrorResult& aRv);
|
|
||||||
|
|
||||||
JS::Value
|
JS::Value
|
||||||
IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
|
IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
|
||||||
|
|
|
@ -1975,11 +1975,11 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMWindow> currentWin(do_GetInterface(currentInner));
|
nsCOMPtr<nsIDOMWindow> currentWin(do_GetInterface(currentInner));
|
||||||
rv = WrapNative(cx, currentWin, &NS_GET_IID(nsIDOMWindow),
|
rv = WrapNative(cx, currentWin, &NS_GET_IID(nsIDOMWindow),
|
||||||
true, argv.handleAt(0));
|
true, argv[0]);
|
||||||
|
|
||||||
for (size_t i = 1; i < argc; ++i) {
|
for (size_t i = 1; i < argc; ++i) {
|
||||||
argv[i] = args[i - 1];
|
argv[i].set(args[i - 1]);
|
||||||
if (!JS_WrapValue(cx, argv.handleAt(i)))
|
if (!JS_WrapValue(cx, argv[i]))
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1002,7 +1002,7 @@ nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, n
|
||||||
// got the arguments, now attach them.
|
// got the arguments, now attach them.
|
||||||
|
|
||||||
for (uint32_t i = 0; i < args.length(); ++i) {
|
for (uint32_t i = 0; i < args.length(); ++i) {
|
||||||
if (!JS_WrapValue(mContext, args.handleAt(i))) {
|
if (!JS_WrapValue(mContext, args[i])) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1066,7 +1066,7 @@ nsJSContext::ConvertSupportsTojsvals(nsISupports* aArgs,
|
||||||
if (argsArray) {
|
if (argsArray) {
|
||||||
for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
|
for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
|
||||||
nsCOMPtr<nsISupports> arg;
|
nsCOMPtr<nsISupports> arg;
|
||||||
JS::MutableHandle<JS::Value> thisVal = aArgsOut.handleAt(argCtr);
|
JS::MutableHandle<JS::Value> thisVal = aArgsOut[argCtr];
|
||||||
argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
|
argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
|
||||||
getter_AddRefs(arg));
|
getter_AddRefs(arg));
|
||||||
if (!arg) {
|
if (!arg) {
|
||||||
|
@ -1099,7 +1099,7 @@ nsJSContext::ConvertSupportsTojsvals(nsISupports* aArgs,
|
||||||
} else {
|
} else {
|
||||||
nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
|
nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
|
||||||
if (variant) {
|
if (variant) {
|
||||||
rv = xpc->VariantToJS(cx, aScope, variant, aArgsOut.handleAt(0));
|
rv = xpc->VariantToJS(cx, aScope, variant, aArgsOut[0]);
|
||||||
} else {
|
} else {
|
||||||
NS_ERROR("Not an array, not an interface?");
|
NS_ERROR("Not an array, not an interface?");
|
||||||
rv = NS_ERROR_UNEXPECTED;
|
rv = NS_ERROR_UNEXPECTED;
|
||||||
|
|
|
@ -2054,7 +2054,7 @@ inline bool
|
||||||
AddStringToIDVector(JSContext* cx, JS::AutoIdVector& vector, const char* name)
|
AddStringToIDVector(JSContext* cx, JS::AutoIdVector& vector, const char* name)
|
||||||
{
|
{
|
||||||
return vector.growBy(1) &&
|
return vector.growBy(1) &&
|
||||||
InternJSString(cx, vector[vector.length() - 1], name);
|
InternJSString(cx, *(vector[vector.length() - 1]).address(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of the bits that XrayWrapper needs
|
// Implementation of the bits that XrayWrapper needs
|
||||||
|
|
|
@ -12525,8 +12525,8 @@ class CallbackMember(CGNativeMember):
|
||||||
{
|
{
|
||||||
'result': result,
|
'result': result,
|
||||||
'successCode': "continue;\n" if arg.variadic else "break;\n",
|
'successCode': "continue;\n" if arg.variadic else "break;\n",
|
||||||
'jsvalRef': "argv.handleAt(%s)" % jsvalIndex,
|
'jsvalRef': "argv[%s]" % jsvalIndex,
|
||||||
'jsvalHandle': "argv.handleAt(%s)" % jsvalIndex,
|
'jsvalHandle': "argv[%s]" % jsvalIndex,
|
||||||
# XXXbz we don't have anything better to use for 'obj',
|
# XXXbz we don't have anything better to use for 'obj',
|
||||||
# really... It's OK to use CallbackPreserveColor because
|
# really... It's OK to use CallbackPreserveColor because
|
||||||
# CallSetup already handled the unmark-gray bits for us.
|
# CallSetup already handled the unmark-gray bits for us.
|
||||||
|
@ -12558,7 +12558,7 @@ class CallbackMember(CGNativeMember):
|
||||||
// This is our current trailing argument; reduce argc
|
// This is our current trailing argument; reduce argc
|
||||||
--argc;
|
--argc;
|
||||||
} else {
|
} else {
|
||||||
argv[${i}] = JS::UndefinedValue();
|
argv[${i}].setUndefined();
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
argName=arg.identifier.name,
|
argName=arg.identifier.name,
|
||||||
|
@ -12816,7 +12816,7 @@ class CallbackSetter(CallbackAccessor):
|
||||||
return fill(
|
return fill(
|
||||||
"""
|
"""
|
||||||
MOZ_ASSERT(argv.length() == 1);
|
MOZ_ASSERT(argv.length() == 1);
|
||||||
if (!JS_SetProperty(cx, CallbackPreserveColor(), "${attrName}", argv.handleAt(0))) {
|
if (!JS_SetProperty(cx, CallbackPreserveColor(), "${attrName}", argv[0])) {
|
||||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||||
return${errorReturn};
|
return${errorReturn};
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,7 +244,7 @@ ToJSValue(JSContext* aCx,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < aLength; ++i) {
|
for (size_t i = 0; i < aLength; ++i) {
|
||||||
if (!ToJSValue(aCx, aArguments[i], v.handleAt(i))) {
|
if (!ToJSValue(aCx, aArguments[i], v[i])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,19 @@ enum GamepadMappingType
|
||||||
StandardMapping = 1
|
StandardMapping = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Per spec:
|
||||||
|
// https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#remapping
|
||||||
|
const int kStandardGamepadButtons = 17;
|
||||||
|
const int kStandardGamepadAxes = 4;
|
||||||
|
|
||||||
|
const int kButtonLeftTrigger = 6;
|
||||||
|
const int kButtonRightTrigger = 7;
|
||||||
|
|
||||||
|
const int kLeftStickXAxis = 0;
|
||||||
|
const int kLeftStickYAxis = 1;
|
||||||
|
const int kRightStickXAxis = 2;
|
||||||
|
const int kRightStickYAxis = 3;
|
||||||
|
|
||||||
class Gamepad : public nsISupports,
|
class Gamepad : public nsISupports,
|
||||||
public nsWrapperCache
|
public nsWrapperCache
|
||||||
{
|
{
|
||||||
|
|
|
@ -97,7 +97,9 @@ function createMediaElement(type, label) {
|
||||||
* The error callback if the stream fails to be retrieved
|
* The error callback if the stream fails to be retrieved
|
||||||
*/
|
*/
|
||||||
function getUserMedia(constraints, onSuccess, onError) {
|
function getUserMedia(constraints, onSuccess, onError) {
|
||||||
|
if (!("fake" in constraints)) {
|
||||||
constraints["fake"] = FAKE_ENABLED;
|
constraints["fake"] = FAKE_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
info("Call getUserMedia for " + JSON.stringify(constraints));
|
info("Call getUserMedia for " + JSON.stringify(constraints));
|
||||||
navigator.mozGetUserMedia(constraints, onSuccess, onError);
|
navigator.mozGetUserMedia(constraints, onSuccess, onError);
|
||||||
|
|
|
@ -131,7 +131,7 @@ MobileMessageManager::Send(JSContext* aCx, JS::Handle<JSObject*> aGlobal,
|
||||||
uint32_t aServiceId,
|
uint32_t aServiceId,
|
||||||
JS::Handle<JSString*> aNumber,
|
JS::Handle<JSString*> aNumber,
|
||||||
const nsAString& aMessage,
|
const nsAString& aMessage,
|
||||||
JS::Value* aRequest)
|
JS::MutableHandle<JS::Value> aRequest)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
|
nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
|
||||||
NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE);
|
||||||
|
@ -149,16 +149,14 @@ MobileMessageManager::Send(JSContext* aCx, JS::Handle<JSObject*> aGlobal,
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
js::AssertSameCompartment(aCx, aGlobal);
|
js::AssertSameCompartment(aCx, aGlobal);
|
||||||
JS::Rooted<JS::Value> rval(aCx);
|
|
||||||
rv = nsContentUtils::WrapNative(aCx,
|
rv = nsContentUtils::WrapNative(aCx,
|
||||||
static_cast<nsIDOMDOMRequest*>(request.get()),
|
static_cast<nsIDOMDOMRequest*>(request.get()),
|
||||||
&rval);
|
aRequest);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
NS_ERROR("Failed to create the js value!");
|
NS_ERROR("Failed to create the js value!");
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
*aRequest = rval;
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +206,7 @@ MobileMessageManager::Send(JS::Handle<JS::Value> aNumber,
|
||||||
|
|
||||||
if (aNumber.isString()) {
|
if (aNumber.isString()) {
|
||||||
JS::Rooted<JSString*> str(aCx, aNumber.toString());
|
JS::Rooted<JSString*> str(aCx, aNumber.toString());
|
||||||
return Send(aCx, global, serviceId, str, aMessage, aReturn.address());
|
return Send(aCx, global, serviceId, str, aMessage, aReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be an array then.
|
// Must be an array then.
|
||||||
|
@ -236,7 +234,7 @@ MobileMessageManager::Send(JS::Handle<JS::Value> aNumber,
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = Send(aCx, global, serviceId, str, aMessage, &requests[i]);
|
nsresult rv = Send(aCx, global, serviceId, str, aMessage, requests[i]);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ private:
|
||||||
uint32_t aServiceId,
|
uint32_t aServiceId,
|
||||||
JS::Handle<JSString*> aNumber,
|
JS::Handle<JSString*> aNumber,
|
||||||
const nsAString& aMessage,
|
const nsAString& aMessage,
|
||||||
JS::Value* aRequest);
|
JS::MutableHandle<JS::Value> aRequest);
|
||||||
|
|
||||||
nsresult DispatchTrustedSmsEventToSelf(const char* aTopic,
|
nsresult DispatchTrustedSmsEventToSelf(const char* aTopic,
|
||||||
const nsAString& aEventName,
|
const nsAString& aEventName,
|
||||||
|
|
|
@ -272,6 +272,7 @@ let NotificationDB = {
|
||||||
var id = data.id;
|
var id = data.id;
|
||||||
if (!this.notifications[origin]) {
|
if (!this.notifications[origin]) {
|
||||||
if (DEBUG) { debug("No notifications found for origin: " + origin); }
|
if (DEBUG) { debug("No notifications found for origin: " + origin); }
|
||||||
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +280,7 @@ let NotificationDB = {
|
||||||
var oldNotification = this.notifications[origin][id];
|
var oldNotification = this.notifications[origin][id];
|
||||||
if (!oldNotification) {
|
if (!oldNotification) {
|
||||||
if (DEBUG) { debug("No notification found with id: " + id); }
|
if (DEBUG) { debug("No notification found with id: " + id); }
|
||||||
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,3 +34,4 @@ LOCAL_INCLUDES += [
|
||||||
'/dom/ipc',
|
'/dom/ipc',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
||||||
|
|
|
@ -0,0 +1,338 @@
|
||||||
|
const Cu = Components.utils;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||||
|
"@mozilla.org/childprocessmessagemanager;1",
|
||||||
|
"nsIMessageSender");
|
||||||
|
|
||||||
|
let systemNotification = {
|
||||||
|
origin: "app://system.gaiamobile.org/manifest.webapp",
|
||||||
|
id: "{2bc883bf-2809-4432-b0f4-f54e10372764}",
|
||||||
|
title: "SystemNotification:" + Date.now(),
|
||||||
|
dir: "auto",
|
||||||
|
lang: "",
|
||||||
|
body: "System notification body",
|
||||||
|
tag: "",
|
||||||
|
icon: "icon.png"
|
||||||
|
};
|
||||||
|
|
||||||
|
let calendarNotification = {
|
||||||
|
origin: "app://calendar.gaiamobile.org/manifest.webapp",
|
||||||
|
id: "{d8d11299-a58e-429b-9a9a-57c562982fbf}",
|
||||||
|
title: "CalendarNotification:" + Date.now(),
|
||||||
|
dir: "auto",
|
||||||
|
lang: "",
|
||||||
|
body: "Calendar notification body",
|
||||||
|
tag: "",
|
||||||
|
icon: "icon.png"
|
||||||
|
};
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_get_profile();
|
||||||
|
Cu.import("resource://gre/modules/NotificationDB.jsm");
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to add a listener, send message and treat the reply
|
||||||
|
function addAndSend(msg, reply, callback, payload, runNext = true) {
|
||||||
|
let handler = {
|
||||||
|
receiveMessage: function(message) {
|
||||||
|
if (message.name === reply) {
|
||||||
|
cpmm.removeMessageListener(reply, handler);
|
||||||
|
callback(message);
|
||||||
|
if (runNext) {
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cpmm.addMessageListener(reply, handler);
|
||||||
|
cpmm.sendAsyncMessage(msg, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper fonction, comparing two notifications
|
||||||
|
function compareNotification(notif1, notif2) {
|
||||||
|
// retrieved notification should be the second one sent
|
||||||
|
for (let prop in notif1) {
|
||||||
|
// compare each property
|
||||||
|
do_check_eq(notif1[prop], notif2[prop]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get one notification, none exists
|
||||||
|
add_test(function test_get_none() {
|
||||||
|
let requestID = 0;
|
||||||
|
let msgReply = "Notification:GetAll:Return:OK";
|
||||||
|
let msgHandler = function(message) {
|
||||||
|
do_check_eq(requestID, message.data.requestID);
|
||||||
|
do_check_eq(0, message.data.notifications.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndSend("Notification:GetAll", msgReply, msgHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
requestID: requestID
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store one notification
|
||||||
|
add_test(function test_send_one() {
|
||||||
|
let requestID = 1;
|
||||||
|
let msgReply = "Notification:Save:Return:OK";
|
||||||
|
let msgHandler = function(message) {
|
||||||
|
do_check_eq(requestID, message.data.requestID);
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndSend("Notification:Save", msgReply, msgHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
notification: systemNotification,
|
||||||
|
requestID: requestID
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get one notification, one exists
|
||||||
|
add_test(function test_get_one() {
|
||||||
|
let requestID = 2;
|
||||||
|
let msgReply = "Notification:GetAll:Return:OK";
|
||||||
|
let msgHandler = function(message) {
|
||||||
|
do_check_eq(requestID, message.data.requestID);
|
||||||
|
do_check_eq(1, message.data.notifications.length);
|
||||||
|
// compare the content
|
||||||
|
compareNotification(systemNotification, message.data.notifications[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndSend("Notification:GetAll", msgReply, msgHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
requestID: requestID
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete one notification
|
||||||
|
add_test(function test_delete_one() {
|
||||||
|
let requestID = 3;
|
||||||
|
let msgReply = "Notification:Delete:Return:OK";
|
||||||
|
let msgHandler = function(message) {
|
||||||
|
do_check_eq(requestID, message.data.requestID);
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndSend("Notification:Delete", msgReply, msgHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
id: systemNotification.id,
|
||||||
|
requestID: requestID
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get one notification, none exists
|
||||||
|
add_test(function test_get_none_again() {
|
||||||
|
let requestID = 4;
|
||||||
|
let msgReply = "Notification:GetAll:Return:OK";
|
||||||
|
let msgHandler = function(message) {
|
||||||
|
do_check_eq(requestID, message.data.requestID);
|
||||||
|
do_check_eq(0, message.data.notifications.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndSend("Notification:GetAll", msgReply, msgHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
requestID: requestID
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete one notification that do not exists anymore
|
||||||
|
add_test(function test_delete_one_nonexistent() {
|
||||||
|
let requestID = 5;
|
||||||
|
let msgReply = "Notification:Delete:Return:OK";
|
||||||
|
let msgHandler = function(message) {
|
||||||
|
do_check_eq(requestID, message.data.requestID);
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndSend("Notification:Delete", msgReply, msgHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
id: systemNotification.id,
|
||||||
|
requestID: requestID
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store two notifications with the same id
|
||||||
|
add_test(function test_send_two_get_one() {
|
||||||
|
let requestID = 6;
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
let msgGetReply = "Notification:GetAll:Return:OK";
|
||||||
|
let msgGetHandler = function(message) {
|
||||||
|
do_check_eq(requestID + 2, message.data.requestID);
|
||||||
|
do_check_eq(1, message.data.notifications.length);
|
||||||
|
// compare the content
|
||||||
|
compareNotification(systemNotification, message.data.notifications[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let msgSaveReply = "Notification:Save:Return:OK";
|
||||||
|
let msgSaveHandler = function(message) {
|
||||||
|
calls += 1;
|
||||||
|
if (calls === 2) {
|
||||||
|
addAndSend("Notification:GetAll", msgGetReply, msgGetHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
requestID: (requestID + 2)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndSend("Notification:Save", msgSaveReply, msgSaveHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
notification: systemNotification,
|
||||||
|
requestID: requestID
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
addAndSend("Notification:Save", msgSaveReply, msgSaveHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
notification: systemNotification,
|
||||||
|
requestID: (requestID + 1)
|
||||||
|
}, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete previous notification
|
||||||
|
add_test(function test_delete_previous() {
|
||||||
|
let requestID = 8;
|
||||||
|
let msgReply = "Notification:Delete:Return:OK";
|
||||||
|
let msgHandler = function(message) {
|
||||||
|
do_check_eq(requestID, message.data.requestID);
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndSend("Notification:Delete", msgReply, msgHandler, {
|
||||||
|
origin: systemNotification.origin,
|
||||||
|
id: systemNotification.id,
|
||||||
|
requestID: requestID
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store two notifications from same origin with the same tag
|
||||||
|
add_test(function test_send_two_get_one() {
|
||||||
|
let requestID = 10;
|
||||||
|
let tag = "voicemail";
|
||||||
|
|
||||||
|
let systemNotification1 = systemNotification;
|
||||||
|
systemNotification1.id = "{f271f9ee-3955-4c10-b1f2-af552fb270ee}";
|
||||||
|
systemNotification1.tag = tag;
|
||||||
|
|
||||||
|
let systemNotification2 = systemNotification;
|
||||||
|
systemNotification2.id = "{8ef9a628-f0f4-44b4-820d-c117573c33e3}";
|
||||||
|
systemNotification2.tag = tag;
|
||||||
|
|
||||||
|
let msgGetReply = "Notification:GetAll:Return:OK";
|
||||||
|
let msgGetNotifHandler = {
|
||||||
|
receiveMessage: function(message) {
|
||||||
|
if (message.name === msgGetReply) {
|
||||||
|
cpmm.removeMessageListener(msgGetReply, msgGetNotifHandler);
|
||||||
|
let notifications = message.data.notifications;
|
||||||
|
// same tag, so replaced
|
||||||
|
do_check_eq(1, notifications.length);
|
||||||
|
// compare the content
|
||||||
|
compareNotification(systemNotification2, notifications[0]);
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cpmm.addMessageListener(msgGetReply, msgGetNotifHandler);
|
||||||
|
|
||||||
|
let msgSaveReply = "Notification:Save:Return:OK";
|
||||||
|
let msgSaveCalls = 0;
|
||||||
|
let msgSaveHandler = function(message) {
|
||||||
|
msgSaveCalls++;
|
||||||
|
// Once both request have been sent, trigger getall
|
||||||
|
if (msgSaveCalls === 2) {
|
||||||
|
cpmm.sendAsyncMessage("Notification:GetAll", {
|
||||||
|
origin: systemNotification1.origin,
|
||||||
|
requestID: message.data.requestID + 2 // 12, 13
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndSend("Notification:Save", msgSaveReply, msgSaveHandler, {
|
||||||
|
origin: systemNotification1.origin,
|
||||||
|
notification: systemNotification1,
|
||||||
|
requestID: requestID // 10
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
addAndSend("Notification:Save", msgSaveReply, msgSaveHandler, {
|
||||||
|
origin: systemNotification2.origin,
|
||||||
|
notification: systemNotification2,
|
||||||
|
requestID: (requestID + 1) // 11
|
||||||
|
}, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store two notifications from two origins with the same tag
|
||||||
|
add_test(function test_send_two_get_two() {
|
||||||
|
let requestID = 20;
|
||||||
|
let tag = "voicemail";
|
||||||
|
|
||||||
|
let systemNotification1 = systemNotification;
|
||||||
|
systemNotification1.tag = tag;
|
||||||
|
|
||||||
|
let calendarNotification2 = calendarNotification;
|
||||||
|
calendarNotification2.tag = tag;
|
||||||
|
|
||||||
|
let msgGetReply = "Notification:GetAll:Return:OK";
|
||||||
|
let msgGetCalls = 0;
|
||||||
|
let msgGetHandler = {
|
||||||
|
receiveMessage: function(message) {
|
||||||
|
if (message.name === msgGetReply) {
|
||||||
|
msgGetCalls++;
|
||||||
|
let notifications = message.data.notifications;
|
||||||
|
|
||||||
|
// one notification per origin
|
||||||
|
do_check_eq(1, notifications.length);
|
||||||
|
|
||||||
|
// first call should be system notification
|
||||||
|
if (msgGetCalls === 1) {
|
||||||
|
compareNotification(systemNotification1, notifications[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// second and last call should be calendar notification
|
||||||
|
if (msgGetCalls === 2) {
|
||||||
|
cpmm.removeMessageListener(msgGetReply, msgGetHandler);
|
||||||
|
compareNotification(calendarNotification2, notifications[0]);
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cpmm.addMessageListener(msgGetReply, msgGetHandler);
|
||||||
|
|
||||||
|
let msgSaveReply = "Notification:Save:Return:OK";
|
||||||
|
let msgSaveCalls = 0;
|
||||||
|
let msgSaveHandler = {
|
||||||
|
receiveMessage: function(message) {
|
||||||
|
if (message.name === msgSaveReply) {
|
||||||
|
msgSaveCalls++;
|
||||||
|
if (msgSaveCalls === 2) {
|
||||||
|
cpmm.removeMessageListener(msgSaveReply, msgSaveHandler);
|
||||||
|
|
||||||
|
// Trigger getall for each origin
|
||||||
|
cpmm.sendAsyncMessage("Notification:GetAll", {
|
||||||
|
origin: systemNotification1.origin,
|
||||||
|
requestID: message.data.requestID + 1 // 22
|
||||||
|
});
|
||||||
|
|
||||||
|
cpmm.sendAsyncMessage("Notification:GetAll", {
|
||||||
|
origin: calendarNotification2.origin,
|
||||||
|
requestID: message.data.requestID + 2 // 23
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cpmm.addMessageListener(msgSaveReply, msgSaveHandler);
|
||||||
|
|
||||||
|
cpmm.sendAsyncMessage("Notification:Save", {
|
||||||
|
origin: systemNotification1.origin,
|
||||||
|
notification: systemNotification1,
|
||||||
|
requestID: requestID // 20
|
||||||
|
});
|
||||||
|
|
||||||
|
cpmm.sendAsyncMessage("Notification:Save", {
|
||||||
|
origin: calendarNotification2.origin,
|
||||||
|
notification: calendarNotification2,
|
||||||
|
requestID: (requestID + 1) // 21
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
[DEFAULT]
|
||||||
|
head =
|
||||||
|
tail =
|
||||||
|
|
||||||
|
[test_notificationdb.js]
|
|
@ -20,10 +20,7 @@ interface Console {
|
||||||
void time(optional any time);
|
void time(optional any time);
|
||||||
void timeEnd(optional any time);
|
void timeEnd(optional any time);
|
||||||
|
|
||||||
[Throws]
|
|
||||||
void profile(any... data);
|
void profile(any... data);
|
||||||
|
|
||||||
[Throws]
|
|
||||||
void profileEnd(any... data);
|
void profileEnd(any... data);
|
||||||
|
|
||||||
void assert(boolean condition, any... data);
|
void assert(boolean condition, any... data);
|
||||||
|
|
|
@ -728,7 +728,7 @@ APZCTreeManager::HandOffFling(AsyncPanZoomController* aPrev, ScreenPoint aVeloci
|
||||||
// otherwise built on touch-start and cleared on touch-end, and a fling
|
// otherwise built on touch-start and cleared on touch-end, and a fling
|
||||||
// happens after touch-end. Note that, unlike DispatchScroll() which is
|
// happens after touch-end. Note that, unlike DispatchScroll() which is
|
||||||
// called on every touch-move during overscroll panning,
|
// called on every touch-move during overscroll panning,
|
||||||
// HandleFlingOverscroll() is only called once during a fling handoff,
|
// HandOffFling() is only called once during a fling handoff,
|
||||||
// so it's not worth trying to avoid building the handoff chain here.
|
// so it's not worth trying to avoid building the handoff chain here.
|
||||||
BuildOverscrollHandoffChain(aPrev);
|
BuildOverscrollHandoffChain(aPrev);
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
#define APZC_LOG_FM(fm, prefix, ...) \
|
#define APZC_LOG_FM(fm, prefix, ...) \
|
||||||
APZC_LOG(prefix ":" \
|
APZC_LOG(prefix ":" \
|
||||||
" i=(%ld %lld) cb=(%d %d %d %d) rcs=(%.3f %.3f) dp=(%.3f %.3f %.3f %.3f) dpm=(%.3f %.3f %.3f %.3f) um=%d " \
|
" i=(%ld %lld) cb=(%d %d %d %d) rcs=(%.3f %.3f) dp=(%.3f %.3f %.3f %.3f) dpm=(%.3f %.3f %.3f %.3f) um=%d " \
|
||||||
"v=(%.3f %.3f %.3f %.3f) s=(%.3f %.3f) sr=(%.3f %.3f %.3f %.3f) z=(%.3f %.3f %.3f %.3f) u=(%d %lu)\n", \
|
"v=(%.3f %.3f %.3f %.3f) s=(%.3f %.3f) sr=(%.3f %.3f %.3f %.3f) z(ld=%.3f r=%.3f cr=%.3f z=%.3f ts=%.3f) u=(%d %lu)\n", \
|
||||||
__VA_ARGS__, \
|
__VA_ARGS__, \
|
||||||
fm.mPresShellId, fm.GetScrollId(), \
|
fm.mPresShellId, fm.GetScrollId(), \
|
||||||
fm.mCompositionBounds.x, fm.mCompositionBounds.y, fm.mCompositionBounds.width, fm.mCompositionBounds.height, \
|
fm.mCompositionBounds.x, fm.mCompositionBounds.y, fm.mCompositionBounds.width, fm.mCompositionBounds.height, \
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
fm.mViewport.x, fm.mViewport.y, fm.mViewport.width, fm.mViewport.height, \
|
fm.mViewport.x, fm.mViewport.y, fm.mViewport.width, fm.mViewport.height, \
|
||||||
fm.GetScrollOffset().x, fm.GetScrollOffset().y, \
|
fm.GetScrollOffset().x, fm.GetScrollOffset().y, \
|
||||||
fm.mScrollableRect.x, fm.mScrollableRect.y, fm.mScrollableRect.width, fm.mScrollableRect.height, \
|
fm.mScrollableRect.x, fm.mScrollableRect.y, fm.mScrollableRect.width, fm.mScrollableRect.height, \
|
||||||
fm.mDevPixelsPerCSSPixel.scale, fm.mResolution.scale, fm.mCumulativeResolution.scale, fm.GetZoom().scale, \
|
fm.mDevPixelsPerCSSPixel.scale, fm.mResolution.scale, fm.mCumulativeResolution.scale, fm.GetZoom().scale, fm.mTransformScale.scale, \
|
||||||
fm.GetScrollOffsetUpdated(), fm.GetScrollGeneration()); \
|
fm.GetScrollOffsetUpdated(), fm.GetScrollGeneration()); \
|
||||||
|
|
||||||
// Static helper functions
|
// Static helper functions
|
||||||
|
@ -163,6 +163,20 @@ typedef GeckoContentController::APZStateChange APZStateChange;
|
||||||
* generated displayport's size is beyond that of the scrollable rect on the
|
* generated displayport's size is beyond that of the scrollable rect on the
|
||||||
* opposite axis.
|
* opposite axis.
|
||||||
*
|
*
|
||||||
|
* "apz.fling_accel_interval_ms"
|
||||||
|
* The time in milliseconds that determines whether a second fling will be
|
||||||
|
* treated as accelerated. If two flings are started within this interval,
|
||||||
|
* the second one will be accelerated. Setting an interval of 0 means that
|
||||||
|
* acceleration will be disabled.
|
||||||
|
*
|
||||||
|
* "apz.fling_accel_base_mult"
|
||||||
|
* "apz.fling_accel_supplemental_mult"
|
||||||
|
* When applying an acceleration on a fling, the new computed velocity is
|
||||||
|
* (new_fling_velocity * base_mult) + (old_velocity * supplemental_mult).
|
||||||
|
* The base_mult and supplemental_mult multiplier values are controlled by
|
||||||
|
* these prefs. Note that "old_velocity" here is the initial velocity of the
|
||||||
|
* previous fling _after_ acceleration was applied to it (if applicable).
|
||||||
|
*
|
||||||
* "apz.fling_friction"
|
* "apz.fling_friction"
|
||||||
* Amount of friction applied during flings.
|
* Amount of friction applied during flings.
|
||||||
*
|
*
|
||||||
|
@ -330,10 +344,40 @@ GetFrameTime() {
|
||||||
|
|
||||||
class FlingAnimation: public AsyncPanZoomAnimation {
|
class FlingAnimation: public AsyncPanZoomAnimation {
|
||||||
public:
|
public:
|
||||||
FlingAnimation(AsyncPanZoomController& aApzc)
|
FlingAnimation(AsyncPanZoomController& aApzc, bool aApplyAcceleration)
|
||||||
: AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
|
: AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
|
||||||
, mApzc(aApzc)
|
, mApzc(aApzc)
|
||||||
{}
|
{
|
||||||
|
TimeStamp now = GetFrameTime();
|
||||||
|
ScreenPoint velocity(mApzc.mX.GetVelocity(), mApzc.mY.GetVelocity());
|
||||||
|
|
||||||
|
// If the last fling was very recent and in the same direction as this one,
|
||||||
|
// boost the velocity to be the sum of the two. Check separate axes separately
|
||||||
|
// because we could have two vertical flings with small horizontal components
|
||||||
|
// on the opposite side of zero, and we still want the y-fling to get accelerated.
|
||||||
|
// Note that the acceleration code is only applied on the APZC that receives the
|
||||||
|
// actual touch event; the accelerated velocities are then handed off using the
|
||||||
|
// normal HandOffFling codepath.
|
||||||
|
if (aApplyAcceleration && !mApzc.mLastFlingTime.IsNull()
|
||||||
|
&& (now - mApzc.mLastFlingTime).ToMilliseconds() < gfxPrefs::APZFlingAccelInterval()) {
|
||||||
|
if (SameDirection(velocity.x, mApzc.mLastFlingVelocity.x)) {
|
||||||
|
velocity.x = Accelerate(velocity.x, mApzc.mLastFlingVelocity.x);
|
||||||
|
APZC_LOG("%p Applying fling x-acceleration from %f to %f (delta %f)\n",
|
||||||
|
&mApzc, mApzc.mX.GetVelocity(), velocity.x, mApzc.mLastFlingVelocity.x);
|
||||||
|
mApzc.mX.SetVelocity(velocity.x);
|
||||||
|
}
|
||||||
|
if (SameDirection(velocity.y, mApzc.mLastFlingVelocity.y)) {
|
||||||
|
velocity.y = Accelerate(velocity.y, mApzc.mLastFlingVelocity.y);
|
||||||
|
APZC_LOG("%p Applying fling y-acceleration from %f to %f (delta %f)\n",
|
||||||
|
&mApzc, mApzc.mY.GetVelocity(), velocity.y, mApzc.mLastFlingVelocity.y);
|
||||||
|
mApzc.mY.SetVelocity(velocity.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mApzc.mLastFlingTime = now;
|
||||||
|
mApzc.mLastFlingVelocity = velocity;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advances a fling by an interpolated amount based on the passed in |aDelta|.
|
* Advances a fling by an interpolated amount based on the passed in |aDelta|.
|
||||||
* This should be called whenever sampling the content transform for this
|
* This should be called whenever sampling the content transform for this
|
||||||
|
@ -344,6 +388,19 @@ public:
|
||||||
const TimeDuration& aDelta);
|
const TimeDuration& aDelta);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static bool SameDirection(float aVelocity1, float aVelocity2)
|
||||||
|
{
|
||||||
|
return (aVelocity1 == 0.0f)
|
||||||
|
|| (aVelocity2 == 0.0f)
|
||||||
|
|| (IsNegative(aVelocity1) == IsNegative(aVelocity2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static float Accelerate(float aBase, float aSupplemental)
|
||||||
|
{
|
||||||
|
return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier())
|
||||||
|
+ (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
|
||||||
|
}
|
||||||
|
|
||||||
AsyncPanZoomController& mApzc;
|
AsyncPanZoomController& mApzc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -755,7 +812,7 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
|
||||||
mX.EndTouch();
|
mX.EndTouch();
|
||||||
mY.EndTouch();
|
mY.EndTouch();
|
||||||
SetState(FLING);
|
SetState(FLING);
|
||||||
StartAnimation(new FlingAnimation(*this));
|
StartAnimation(new FlingAnimation(*this, true));
|
||||||
return nsEventStatus_eConsumeNoDefault;
|
return nsEventStatus_eConsumeNoDefault;
|
||||||
|
|
||||||
case PINCHING:
|
case PINCHING:
|
||||||
|
@ -1183,7 +1240,7 @@ void AsyncPanZoomController::TakeOverFling(ScreenPoint aVelocity) {
|
||||||
mX.SetVelocity(mX.GetVelocity() + aVelocity.x);
|
mX.SetVelocity(mX.GetVelocity() + aVelocity.x);
|
||||||
mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
|
mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
|
||||||
SetState(FLING);
|
SetState(FLING);
|
||||||
StartAnimation(new FlingAnimation(*this));
|
StartAnimation(new FlingAnimation(*this, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
|
void AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
|
||||||
|
@ -1294,20 +1351,20 @@ bool FlingAnimation::Sample(FrameMetrics& aFrameMetrics,
|
||||||
velocity.y = 0;
|
velocity.y = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// To hand off the fling, we call APZCTreeManager::HandleFlingOverscroll()
|
// To hand off the fling, we call APZCTreeManager::HandOffFling()
|
||||||
// which starts a new fling in the next APZC in the handoff chain with
|
// which starts a new fling in the next APZC in the handoff chain with
|
||||||
// the same velocity. For simplicity, the actual overscroll of the current
|
// the same velocity. For simplicity, the actual overscroll of the current
|
||||||
// sample is discarded rather than being handed off. The compositor should
|
// sample is discarded rather than being handed off. The compositor should
|
||||||
// sample animations sufficiently frequently that this is not noticeable.
|
// sample animations sufficiently frequently that this is not noticeable.
|
||||||
|
|
||||||
// Make a local copy of the tree manager pointer and check if it's not
|
// Make a local copy of the tree manager pointer and check if it's not
|
||||||
// null before calling HandleFlingOverscroll(). This is necessary because
|
// null before calling HandOffFling(). This is necessary because
|
||||||
// Destroy(), which nulls out mTreeManager, could be called concurrently.
|
// Destroy(), which nulls out mTreeManager, could be called concurrently.
|
||||||
APZCTreeManager* treeManagerLocal = mApzc.mTreeManager;
|
APZCTreeManager* treeManagerLocal = mApzc.mTreeManager;
|
||||||
if (treeManagerLocal) {
|
if (treeManagerLocal) {
|
||||||
// APZC is holding mMonitor, so directly calling HandleFlingOverscroll()
|
// APZC is holding mMonitor, so directly calling HandOffFling()
|
||||||
// (which acquires the tree lock) would violate the lock ordering. Instead
|
// (which acquires the tree lock) would violate the lock ordering. Instead
|
||||||
// we schedule HandleFlingOverscroll() to be called after mMonitor is
|
// we schedule HandOffFling() to be called after mMonitor is
|
||||||
// released.
|
// released.
|
||||||
mDeferredTasks.append(NewRunnableMethod(treeManagerLocal,
|
mDeferredTasks.append(NewRunnableMethod(treeManagerLocal,
|
||||||
&APZCTreeManager::HandOffFling,
|
&APZCTreeManager::HandOffFling,
|
||||||
|
@ -1718,11 +1775,11 @@ gfx3DMatrix AsyncPanZoomController::GetTransformToLastDispatchedPaint() {
|
||||||
|
|
||||||
void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
|
void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
|
||||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||||
|
bool isDefault = mFrameMetrics.IsDefault();
|
||||||
|
|
||||||
mLastContentPaintMetrics = aLayerMetrics;
|
mLastContentPaintMetrics = aLayerMetrics;
|
||||||
UpdateTransformScale();
|
UpdateTransformScale();
|
||||||
|
|
||||||
bool isDefault = mFrameMetrics.IsDefault();
|
|
||||||
mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
|
mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
|
||||||
APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
|
APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
|
||||||
|
|
||||||
|
|
|
@ -294,12 +294,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void CancelAnimation();
|
void CancelAnimation();
|
||||||
|
|
||||||
/**
|
|
||||||
* Take over a fling with the given velocity from another APZC. Used for
|
|
||||||
* during overscroll handoff for a fling.
|
|
||||||
*/
|
|
||||||
void TakeOverFling(ScreenPoint aVelocity);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns allowed touch behavior for the given point on the scrollable layer.
|
* Returns allowed touch behavior for the given point on the scrollable layer.
|
||||||
* Internally performs a kind of hit testing based on the regions constructed
|
* Internally performs a kind of hit testing based on the regions constructed
|
||||||
|
@ -501,14 +495,6 @@ protected:
|
||||||
*/
|
*/
|
||||||
void DispatchRepaintRequest(const FrameMetrics& aFrameMetrics);
|
void DispatchRepaintRequest(const FrameMetrics& aFrameMetrics);
|
||||||
|
|
||||||
/**
|
|
||||||
* Advances a fling by an interpolated amount based on the passed in |aDelta|.
|
|
||||||
* This should be called whenever sampling the content transform for this
|
|
||||||
* frame. Returns true if the fling animation should be advanced by one frame,
|
|
||||||
* or false if there is no fling or the fling has ended.
|
|
||||||
*/
|
|
||||||
bool DoFling(const TimeDuration& aDelta);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current frame metrics. This is *not* the Gecko copy stored in the
|
* Gets the current frame metrics. This is *not* the Gecko copy stored in the
|
||||||
* layers code.
|
* layers code.
|
||||||
|
@ -777,7 +763,25 @@ private:
|
||||||
RefPtr<AsyncPanZoomAnimation> mAnimation;
|
RefPtr<AsyncPanZoomAnimation> mAnimation;
|
||||||
|
|
||||||
friend class Axis;
|
friend class Axis;
|
||||||
|
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* The functions and members in this section are used to manage
|
||||||
|
* fling animations.
|
||||||
|
*/
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Take over a fling with the given velocity from another APZC. Used for
|
||||||
|
* during overscroll handoff for a fling.
|
||||||
|
*/
|
||||||
|
void TakeOverFling(ScreenPoint aVelocity);
|
||||||
|
|
||||||
|
private:
|
||||||
friend class FlingAnimation;
|
friend class FlingAnimation;
|
||||||
|
// The initial velocity of the most recent fling.
|
||||||
|
ScreenPoint mLastFlingVelocity;
|
||||||
|
// The time at which the most recent fling started.
|
||||||
|
TimeStamp mLastFlingTime;
|
||||||
|
|
||||||
|
|
||||||
/* ===================================================================
|
/* ===================================================================
|
||||||
|
|
|
@ -111,6 +111,9 @@ private:
|
||||||
DECL_GFX_PREF(Live, "apz.content_response_timeout", APZContentResponseTimeout, int32_t, 300);
|
DECL_GFX_PREF(Live, "apz.content_response_timeout", APZContentResponseTimeout, int32_t, 300);
|
||||||
DECL_GFX_PREF(Live, "apz.cross_slide.enabled", APZCrossSlideEnabled, bool, false);
|
DECL_GFX_PREF(Live, "apz.cross_slide.enabled", APZCrossSlideEnabled, bool, false);
|
||||||
DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped", APZEnlargeDisplayPortWhenClipped, bool, false);
|
DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped", APZEnlargeDisplayPortWhenClipped, bool, false);
|
||||||
|
DECL_GFX_PREF(Live, "apz.fling_accel_interval_ms", APZFlingAccelInterval, int32_t, 500);
|
||||||
|
DECL_GFX_PREF(Live, "apz.fling_accel_base_mult", APZFlingAccelBaseMultiplier, float, 1.0f);
|
||||||
|
DECL_GFX_PREF(Live, "apz.fling_accel_supplemental_mult", APZFlingAccelSupplementalMultiplier, float, 1.0f);
|
||||||
DECL_GFX_PREF(Once, "apz.fling_friction", APZFlingFriction, float, 0.002f);
|
DECL_GFX_PREF(Once, "apz.fling_friction", APZFlingFriction, float, 0.002f);
|
||||||
DECL_GFX_PREF(Live, "apz.fling_repaint_interval", APZFlingRepaintInterval, int32_t, 75);
|
DECL_GFX_PREF(Live, "apz.fling_repaint_interval", APZFlingRepaintInterval, int32_t, 75);
|
||||||
DECL_GFX_PREF(Once, "apz.fling_stopped_threshold", APZFlingStoppedThreshold, float, 0.01f);
|
DECL_GFX_PREF(Once, "apz.fling_stopped_threshold", APZFlingStoppedThreshold, float, 0.01f);
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -501,7 +501,7 @@ JavaScriptChild::AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv
|
||||||
ContextOptionsRef(cx).setDontReportUncaught(true);
|
ContextOptionsRef(cx).setDontReportUncaught(true);
|
||||||
|
|
||||||
HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2);
|
HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2);
|
||||||
bool success = JS::Call(cx, vals.handleAt(1), vals.handleAt(0), args, &rval);
|
bool success = JS::Call(cx, vals[1], vals[0], args, &rval);
|
||||||
if (!success)
|
if (!success)
|
||||||
return fail(cx, rs);
|
return fail(cx, rs);
|
||||||
}
|
}
|
||||||
|
@ -536,7 +536,7 @@ JavaScriptChild::AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv
|
||||||
// treat this as the outparam never having been set.
|
// treat this as the outparam never having been set.
|
||||||
for (size_t i = 0; i < vals.length(); i++) {
|
for (size_t i = 0; i < vals.length(); i++) {
|
||||||
JSVariant variant;
|
JSVariant variant;
|
||||||
if (!toVariant(cx, vals.handleAt(i), &variant))
|
if (!toVariant(cx, vals[i], &variant))
|
||||||
return fail(cx, rs);
|
return fail(cx, rs);
|
||||||
outparams->ReplaceElementAt(i, JSParam(variant));
|
outparams->ReplaceElementAt(i, JSParam(variant));
|
||||||
}
|
}
|
||||||
|
@ -596,7 +596,7 @@ JavaScriptChild::AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &f
|
||||||
|
|
||||||
for (size_t i = 0; i < props.length(); i++) {
|
for (size_t i = 0; i < props.length(); i++) {
|
||||||
nsString name;
|
nsString name;
|
||||||
if (!convertIdToGeckoString(cx, props.handleAt(i), &name))
|
if (!convertIdToGeckoString(cx, props[i], &name))
|
||||||
return fail(cx, rs);
|
return fail(cx, rs);
|
||||||
|
|
||||||
names->AppendElement(name);
|
names->AppendElement(name);
|
||||||
|
|
|
@ -236,7 +236,7 @@ typedef enum JSWhyMagic
|
||||||
JS_BLOCK_NEEDS_CLONE, /* value of static block object slot */
|
JS_BLOCK_NEEDS_CLONE, /* value of static block object slot */
|
||||||
JS_HASH_KEY_EMPTY, /* see class js::HashableValue */
|
JS_HASH_KEY_EMPTY, /* see class js::HashableValue */
|
||||||
JS_ION_ERROR, /* error while running Ion code */
|
JS_ION_ERROR, /* error while running Ion code */
|
||||||
JS_ION_BAILOUT, /* status code to signal EnterIon will OSR into Interpret */
|
JS_ION_BAILOUT, /* missing recover instruction result */
|
||||||
JS_OPTIMIZED_OUT, /* optimized out slot */
|
JS_OPTIMIZED_OUT, /* optimized out slot */
|
||||||
JS_GENERIC_MAGIC /* for local use */
|
JS_GENERIC_MAGIC /* for local use */
|
||||||
} JSWhyMagic;
|
} JSWhyMagic;
|
||||||
|
|
|
@ -930,7 +930,7 @@ StructMetaTypeDescr::create(JSContext *cx,
|
||||||
|
|
||||||
// userFieldTypes[id] = typeObj
|
// userFieldTypes[id] = typeObj
|
||||||
if (!JSObject::defineGeneric(cx, userFieldTypes, id,
|
if (!JSObject::defineGeneric(cx, userFieldTypes, id,
|
||||||
fieldTypeObjs.handleAt(i), nullptr, nullptr,
|
fieldTypeObjs[i], nullptr, nullptr,
|
||||||
JSPROP_READONLY | JSPROP_PERMANENT))
|
JSPROP_READONLY | JSPROP_PERMANENT))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|
|
@ -1163,27 +1163,27 @@ InitTypeClasses(JSContext* cx, HandleObject parent)
|
||||||
if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
|
if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
|
||||||
sPointerFunction, nullptr, sPointerProps,
|
sPointerFunction, nullptr, sPointerProps,
|
||||||
sPointerInstanceFunctions, sPointerInstanceProps,
|
sPointerInstanceFunctions, sPointerInstanceProps,
|
||||||
protos.handleAt(SLOT_POINTERPROTO), protos.handleAt(SLOT_POINTERDATAPROTO)))
|
protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
|
if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
|
||||||
sArrayFunction, nullptr, sArrayProps,
|
sArrayFunction, nullptr, sArrayProps,
|
||||||
sArrayInstanceFunctions, sArrayInstanceProps,
|
sArrayInstanceFunctions, sArrayInstanceProps,
|
||||||
protos.handleAt(SLOT_ARRAYPROTO), protos.handleAt(SLOT_ARRAYDATAPROTO)))
|
protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
|
if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
|
||||||
sStructFunction, sStructFunctions, sStructProps,
|
sStructFunction, sStructFunctions, sStructProps,
|
||||||
sStructInstanceFunctions, nullptr,
|
sStructInstanceFunctions, nullptr,
|
||||||
protos.handleAt(SLOT_STRUCTPROTO), protos.handleAt(SLOT_STRUCTDATAPROTO)))
|
protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!InitTypeConstructor(cx, parent, CTypeProto, protos.handleAt(SLOT_POINTERDATAPROTO),
|
if (!InitTypeConstructor(cx, parent, CTypeProto, protos[SLOT_POINTERDATAPROTO],
|
||||||
sFunctionFunction, nullptr, sFunctionProps, sFunctionInstanceFunctions, nullptr,
|
sFunctionFunction, nullptr, sFunctionProps, sFunctionInstanceFunctions, nullptr,
|
||||||
protos.handleAt(SLOT_FUNCTIONPROTO), protos.handleAt(SLOT_FUNCTIONDATAPROTO)))
|
protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
protos[SLOT_CDATAPROTO] = CDataProto;
|
protos[SLOT_CDATAPROTO].set(CDataProto);
|
||||||
|
|
||||||
// Create and attach the ctypes.{Int64,UInt64} constructors.
|
// Create and attach the ctypes.{Int64,UInt64} constructors.
|
||||||
// Each of these has, respectively:
|
// Each of these has, respectively:
|
||||||
|
@ -1193,18 +1193,18 @@ InitTypeClasses(JSContext* cx, HandleObject parent)
|
||||||
// * 'prototype' property:
|
// * 'prototype' property:
|
||||||
// * [[Class]] {"Int64Proto","UInt64Proto"}
|
// * [[Class]] {"Int64Proto","UInt64Proto"}
|
||||||
// * 'constructor' property === ctypes.{Int64,UInt64}
|
// * 'constructor' property === ctypes.{Int64,UInt64}
|
||||||
protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass,
|
protos[SLOT_INT64PROTO].set(InitInt64Class(cx, parent, &sInt64ProtoClass,
|
||||||
Int64::Construct, sInt64Functions, sInt64StaticFunctions);
|
Int64::Construct, sInt64Functions, sInt64StaticFunctions));
|
||||||
if (!protos[SLOT_INT64PROTO])
|
if (!protos[SLOT_INT64PROTO])
|
||||||
return false;
|
return false;
|
||||||
protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass,
|
protos[SLOT_UINT64PROTO].set(InitInt64Class(cx, parent, &sUInt64ProtoClass,
|
||||||
UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions);
|
UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions));
|
||||||
if (!protos[SLOT_UINT64PROTO])
|
if (!protos[SLOT_UINT64PROTO])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Finally, store a pointer to the global ctypes object.
|
// Finally, store a pointer to the global ctypes object.
|
||||||
// Note that there is no other reliable manner of locating this object.
|
// Note that there is no other reliable manner of locating this object.
|
||||||
protos[SLOT_CTYPES] = parent;
|
protos[SLOT_CTYPES].set(parent);
|
||||||
|
|
||||||
// Attach the prototypes just created to each of ctypes.CType.prototype,
|
// Attach the prototypes just created to each of ctypes.CType.prototype,
|
||||||
// and the special type constructors, so we can access them when constructing
|
// and the special type constructors, so we can access them when constructing
|
||||||
|
@ -4827,7 +4827,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsOb
|
||||||
Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, fieldType.address()));
|
Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, fieldType.address()));
|
||||||
if (!name)
|
if (!name)
|
||||||
return false;
|
return false;
|
||||||
fieldRoots[i] = JS::ObjectValue(*fieldType);
|
fieldRoots[i].setObject(*fieldType);
|
||||||
|
|
||||||
// Make sure each field name is unique
|
// Make sure each field name is unique
|
||||||
FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
|
FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
|
||||||
|
@ -5135,7 +5135,7 @@ StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
|
||||||
for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
|
for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
|
||||||
const FieldInfoHash::Entry& entry = r.front();
|
const FieldInfoHash::Entry& entry = r.front();
|
||||||
// Add the field descriptor to the array.
|
// Add the field descriptor to the array.
|
||||||
if (!AddFieldToArray(cx, &fieldsVec[entry.value().mIndex],
|
if (!AddFieldToArray(cx, fieldsVec[entry.value().mIndex].address(),
|
||||||
entry.key(), entry.value().mType))
|
entry.key(), entry.value().mType))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -5620,7 +5620,7 @@ FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp)
|
||||||
// Pull out the argument types from the array, if any.
|
// Pull out the argument types from the array, if any.
|
||||||
JS_ASSERT_IF(argTypes.length(), arrayObj);
|
JS_ASSERT_IF(argTypes.length(), arrayObj);
|
||||||
for (uint32_t i = 0; i < argTypes.length(); ++i) {
|
for (uint32_t i = 0; i < argTypes.length(); ++i) {
|
||||||
if (!JS_GetElement(cx, arrayObj, i, argTypes.handleAt(i)))
|
if (!JS_GetElement(cx, arrayObj, i, argTypes[i]))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5946,7 +5946,7 @@ FunctionType::ArgTypesGetter(JSContext* cx, JS::CallArgs args)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (size_t i = 0; i < len; ++i)
|
for (size_t i = 0; i < len; ++i)
|
||||||
vec[i] = JS::ObjectValue(*fninfo->mArgTypes[i]);
|
vec[i].setObject(*fninfo->mArgTypes[i]);
|
||||||
|
|
||||||
argTypes = JS_NewArrayObject(cx, vec);
|
argTypes = JS_NewArrayObject(cx, vec);
|
||||||
if (!argTypes)
|
if (!argTypes)
|
||||||
|
@ -6182,7 +6182,7 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
|
||||||
// Convert each argument, and have any CData objects created depend on
|
// Convert each argument, and have any CData objects created depend on
|
||||||
// the existing buffers.
|
// the existing buffers.
|
||||||
RootedObject argType(cx, fninfo->mArgTypes[i]);
|
RootedObject argType(cx, fninfo->mArgTypes[i]);
|
||||||
if (!ConvertToJS(cx, argType, NullPtr(), args[i], false, false, &argv[i]))
|
if (!ConvertToJS(cx, argType, NullPtr(), args[i], false, false, argv[i].address()))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
setJitCompilerOption("baseline.usecount.trigger", 10);
|
||||||
|
setJitCompilerOption("ion.usecount.trigger", 20);
|
||||||
|
var i;
|
||||||
|
|
||||||
|
// Check that we are able to remove the addition inside "ra" functions, when we
|
||||||
|
// inline the first version of uceFault, and ensure that the bailout is correct
|
||||||
|
// when uceFault is replaced (which cause an invalidation bailout)
|
||||||
|
|
||||||
|
var uceFault = function (i) {
|
||||||
|
if (i > 98)
|
||||||
|
uceFault = function (i) { return true; };
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uceFault_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_number'));
|
||||||
|
function ra_number(i) {
|
||||||
|
var x = 1 + i;
|
||||||
|
if (uceFault_number(i) || uceFault_number(i))
|
||||||
|
assertEq(x, 100 /* = 1 + 99 */);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uceFault_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_float'));
|
||||||
|
function ra_float(i) {
|
||||||
|
var t = Math.fround(1/3);
|
||||||
|
var fi = Math.fround(i);
|
||||||
|
var x = Math.fround(Math.fround(Math.fround(Math.fround(t + fi) + t) + fi) + t);
|
||||||
|
if (uceFault_float(i) || uceFault_float(i))
|
||||||
|
assertEq(x, 199); /* != 199.00000002980232 (when computed with double additions) */
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uceFault_string = eval(uneval(uceFault).replace('uceFault', 'uceFault_string'));
|
||||||
|
function ra_string(i) {
|
||||||
|
var x = "s" + i;
|
||||||
|
if (uceFault_string(i) || uceFault_string(i))
|
||||||
|
assertEq(x, "s99");
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uceFault_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_object'));
|
||||||
|
function ra_object(i) {
|
||||||
|
var x = {} + i;
|
||||||
|
if (uceFault_object(i) || uceFault_object(i))
|
||||||
|
assertEq(x, "[object Object]99");
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 100; i++) {
|
||||||
|
ra_number(i);
|
||||||
|
ra_float(i);
|
||||||
|
ra_string(i);
|
||||||
|
ra_object(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that we can refer multiple time to the same recover instruction, as well
|
||||||
|
// as chaining recover instructions.
|
||||||
|
|
||||||
|
function alignedAlloc($size, $alignment) {
|
||||||
|
var $1 = $size + 4 | 0;
|
||||||
|
var $2 = $alignment - 1 | 0;
|
||||||
|
var $3 = $1 + $2 | 0;
|
||||||
|
var $4 = malloc($3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function malloc($bytes) {
|
||||||
|
var $189 = undefined;
|
||||||
|
var $198 = $189 + 8 | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 50; i++)
|
||||||
|
alignedAlloc(608, 16);
|
|
@ -239,7 +239,7 @@ ValidateFFI(JSContext *cx, AsmJSModule::Global &global, HandleValue importVal,
|
||||||
if (!v.isObject() || !v.toObject().is<JSFunction>())
|
if (!v.isObject() || !v.toObject().is<JSFunction>())
|
||||||
return LinkFail(cx, "FFI imports must be functions");
|
return LinkFail(cx, "FFI imports must be functions");
|
||||||
|
|
||||||
(*ffis)[global.ffiIndex()] = &v.toObject().as<JSFunction>();
|
(*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,8 @@ SnapshotIterator::SnapshotIterator(const IonBailoutIterator &iter)
|
||||||
iter.ionScript()->recoversSize()),
|
iter.ionScript()->recoversSize()),
|
||||||
fp_(iter.jsFrame()),
|
fp_(iter.jsFrame()),
|
||||||
machine_(iter.machineState()),
|
machine_(iter.machineState()),
|
||||||
ionScript_(iter.ionScript())
|
ionScript_(iter.ionScript()),
|
||||||
|
instructionResults_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -558,9 +558,6 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||||
if (callerPC == nullptr) {
|
if (callerPC == nullptr) {
|
||||||
IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on top frame!");
|
IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on top frame!");
|
||||||
flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
|
flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
|
||||||
} else if (js_JitOptions.profileInlineFrames) {
|
|
||||||
IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on inline frame!");
|
|
||||||
flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,7 +671,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||||
size_t argOffset = builder.framePushed() + IonJSFrameLayout::offsetOfActualArg(i);
|
size_t argOffset = builder.framePushed() + IonJSFrameLayout::offsetOfActualArg(i);
|
||||||
*builder.valuePointerAtStackOffset(argOffset) = arg;
|
*builder.valuePointerAtStackOffset(argOffset) = arg;
|
||||||
} else {
|
} else {
|
||||||
startFrameFormals[i] = arg;
|
startFrameFormals[i].set(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -758,7 +755,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||||
if (!savedCallerArgs.resize(inlined_args))
|
if (!savedCallerArgs.resize(inlined_args))
|
||||||
return false;
|
return false;
|
||||||
for (uint32_t i = 0; i < inlined_args; i++)
|
for (uint32_t i = 0; i < inlined_args; i++)
|
||||||
savedCallerArgs[i] = iter.read();
|
savedCallerArgs[i].set(iter.read());
|
||||||
|
|
||||||
if (IsSetPropPC(pc)) {
|
if (IsSetPropPC(pc)) {
|
||||||
// We would love to just save all the arguments and leave them
|
// We would love to just save all the arguments and leave them
|
||||||
|
@ -988,36 +985,6 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||||
blFrame->unsetPushedSPSFrame();
|
blFrame->unsetPushedSPSFrame();
|
||||||
|
|
||||||
if (cx->runtime()->spsProfiler.enabled()) {
|
if (cx->runtime()->spsProfiler.enabled()) {
|
||||||
if (js_JitOptions.profileInlineFrames) {
|
|
||||||
// If SPS is enabled, there are two corner cases to handle:
|
|
||||||
// 1. If resuming into the prologue, and innermost frame is an inlined
|
|
||||||
// frame, and bailout is because of argument check failure, then:
|
|
||||||
// Top SPS profiler entry would be for caller frame.
|
|
||||||
// Ion would not have set the PC index field on that frame
|
|
||||||
// (since this bailout happens before MFunctionBoundary).
|
|
||||||
// Make sure that's done now.
|
|
||||||
// 2. If resuming into the prologue, and the bailout is NOT because of an
|
|
||||||
// argument check, then:
|
|
||||||
// Top SPS profiler entry would be for callee frame.
|
|
||||||
// Ion would already have pushed an SPS entry for this frame.
|
|
||||||
// The pc for this entry would be set to nullptr.
|
|
||||||
// Make sure it's set to script->pc.
|
|
||||||
if (caller && bailoutKind == Bailout_ArgumentCheck) {
|
|
||||||
IonSpew(IonSpew_BaselineBailouts, " Setting PCidx on innermost "
|
|
||||||
"inlined frame's parent's SPS entry (%s:%d) (pcIdx=%d)!",
|
|
||||||
caller->filename(), caller->lineno(),
|
|
||||||
caller->pcToOffset(callerPC));
|
|
||||||
cx->runtime()->spsProfiler.updatePC(caller, callerPC);
|
|
||||||
|
|
||||||
} else if (bailoutKind != Bailout_ArgumentCheck) {
|
|
||||||
IonSpew(IonSpew_BaselineBailouts,
|
|
||||||
" Popping SPS entry for innermost inlined frame");
|
|
||||||
cx->runtime()->spsProfiler.exit(script, fun);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// If not profiling inline frames, then this is logically simpler.
|
|
||||||
//
|
|
||||||
// 1. If resuming into inline code, then the top SPS entry will be
|
// 1. If resuming into inline code, then the top SPS entry will be
|
||||||
// for the outermost caller, and will have an uninitialized PC.
|
// for the outermost caller, and will have an uninitialized PC.
|
||||||
// This will be fixed up later in BailoutIonToBaseline.
|
// This will be fixed up later in BailoutIonToBaseline.
|
||||||
|
@ -1038,7 +1005,6 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||||
cx->runtime()->spsProfiler.exit(script, fun);
|
cx->runtime()->spsProfiler.exit(script, fun);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
opReturnAddr = nativeCodeForPC;
|
opReturnAddr = nativeCodeForPC;
|
||||||
}
|
}
|
||||||
|
@ -1370,8 +1336,12 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||||
return BAILOUT_RETURN_FATAL_ERROR;
|
return BAILOUT_RETURN_FATAL_ERROR;
|
||||||
IonSpew(IonSpew_BaselineBailouts, " Incoming frame ptr = %p", builder.startFrame());
|
IonSpew(IonSpew_BaselineBailouts, " Incoming frame ptr = %p", builder.startFrame());
|
||||||
|
|
||||||
|
AutoValueVector instructionResults(cx);
|
||||||
SnapshotIterator snapIter(iter);
|
SnapshotIterator snapIter(iter);
|
||||||
|
|
||||||
|
if (!snapIter.initIntructionResults(instructionResults))
|
||||||
|
return BAILOUT_RETURN_FATAL_ERROR;
|
||||||
|
|
||||||
RootedFunction callee(cx, iter.maybeCallee());
|
RootedFunction callee(cx, iter.maybeCallee());
|
||||||
RootedScript scr(cx, iter.script());
|
RootedScript scr(cx, iter.script());
|
||||||
if (callee) {
|
if (callee) {
|
||||||
|
@ -1399,7 +1369,12 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||||
jsbytecode *topCallerPC = nullptr;
|
jsbytecode *topCallerPC = nullptr;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
MOZ_ASSERT(snapIter.instruction()->isResumePoint());
|
if (!snapIter.instruction()->isResumePoint()) {
|
||||||
|
if (!snapIter.instruction()->recover(cx, snapIter))
|
||||||
|
return BAILOUT_RETURN_FATAL_ERROR;
|
||||||
|
snapIter.nextInstruction();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (frameNo > 0) {
|
if (frameNo > 0) {
|
||||||
TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
|
TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
|
||||||
|
@ -1453,10 +1428,9 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||||
}
|
}
|
||||||
IonSpew(IonSpew_BaselineBailouts, " Done restoring frames");
|
IonSpew(IonSpew_BaselineBailouts, " Done restoring frames");
|
||||||
|
|
||||||
// If there were multiple inline frames unpacked, and inline frame profiling
|
// If there were multiple inline frames unpacked, then the current top SPS frame
|
||||||
// is off, then the current top SPS frame is for the outermost caller, and
|
// is for the outermost caller, and has an uninitialized PC. Initialize it now.
|
||||||
// has an uninitialized PC. Initialize it now.
|
if (frameNo > 0)
|
||||||
if (frameNo > 0 && !js_JitOptions.profileInlineFrames)
|
|
||||||
cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC);
|
cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC);
|
||||||
|
|
||||||
BailoutKind bailoutKind = snapIter.bailoutKind();
|
BailoutKind bailoutKind = snapIter.bailoutKind();
|
||||||
|
|
|
@ -2342,6 +2342,58 @@ BaselineCompiler::emit_JSOP_INITELEM_SETTER()
|
||||||
return emitInitElemGetterSetter();
|
return emitInitElemGetterSetter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BaselineCompiler::emit_JSOP_INITELEM_INC()
|
||||||
|
{
|
||||||
|
// Keep the object and rhs on the stack.
|
||||||
|
frame.syncStack(0);
|
||||||
|
|
||||||
|
// Load object in R0, index in R1.
|
||||||
|
masm.loadValue(frame.addressOfStackValue(frame.peek(-3)), R0);
|
||||||
|
masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1);
|
||||||
|
|
||||||
|
// Call IC.
|
||||||
|
ICSetElem_Fallback::Compiler stubCompiler(cx);
|
||||||
|
if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Pop the rhs
|
||||||
|
frame.pop();
|
||||||
|
|
||||||
|
// Increment index
|
||||||
|
Address indexAddr = frame.addressOfStackValue(frame.peek(-1));
|
||||||
|
masm.incrementInt32Value(indexAddr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef bool (*SpreadFn)(JSContext *, HandleObject, HandleValue,
|
||||||
|
HandleValue, MutableHandleValue);
|
||||||
|
static const VMFunction SpreadInfo = FunctionInfo<SpreadFn>(js::SpreadOperation);
|
||||||
|
|
||||||
|
bool
|
||||||
|
BaselineCompiler::emit_JSOP_SPREAD()
|
||||||
|
{
|
||||||
|
// Load index and iterable in R0 and R1, but keep values on the stack for
|
||||||
|
// the decompiler.
|
||||||
|
frame.syncStack(0);
|
||||||
|
masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
|
||||||
|
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
|
||||||
|
|
||||||
|
prepareVMCall();
|
||||||
|
|
||||||
|
pushArg(R1);
|
||||||
|
pushArg(R0);
|
||||||
|
masm.extractObject(frame.addressOfStackValue(frame.peek(-3)), R0.scratchReg());
|
||||||
|
pushArg(R0.scratchReg());
|
||||||
|
|
||||||
|
if (!callVM(SpreadInfo))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
frame.popn(2);
|
||||||
|
frame.push(R0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BaselineCompiler::emit_JSOP_GETLOCAL()
|
BaselineCompiler::emit_JSOP_GETLOCAL()
|
||||||
{
|
{
|
||||||
|
|
|
@ -97,6 +97,8 @@ namespace jit {
|
||||||
_(JSOP_INITELEM) \
|
_(JSOP_INITELEM) \
|
||||||
_(JSOP_INITELEM_GETTER) \
|
_(JSOP_INITELEM_GETTER) \
|
||||||
_(JSOP_INITELEM_SETTER) \
|
_(JSOP_INITELEM_SETTER) \
|
||||||
|
_(JSOP_INITELEM_INC) \
|
||||||
|
_(JSOP_SPREAD) \
|
||||||
_(JSOP_MUTATEPROTO) \
|
_(JSOP_MUTATEPROTO) \
|
||||||
_(JSOP_INITPROP) \
|
_(JSOP_INITPROP) \
|
||||||
_(JSOP_INITPROP_GETTER) \
|
_(JSOP_INITPROP_GETTER) \
|
||||||
|
|
|
@ -111,7 +111,7 @@ BaselineFrame::copyRawFrameSlots(AutoValueVector *vec) const
|
||||||
|
|
||||||
mozilla::PodCopy(vec->begin(), argv(), nformals);
|
mozilla::PodCopy(vec->begin(), argv(), nformals);
|
||||||
for (unsigned i = 0; i < nfixed; i++)
|
for (unsigned i = 0; i < nfixed; i++)
|
||||||
(*vec)[nformals + i] = *valueSlot(i);
|
(*vec)[nformals + i].set(*valueSlot(i));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5002,7 +5002,8 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_
|
||||||
|
|
||||||
JS_ASSERT(op == JSOP_SETELEM ||
|
JS_ASSERT(op == JSOP_SETELEM ||
|
||||||
op == JSOP_INITELEM ||
|
op == JSOP_INITELEM ||
|
||||||
op == JSOP_INITELEM_ARRAY);
|
op == JSOP_INITELEM_ARRAY ||
|
||||||
|
op == JSOP_INITELEM_INC);
|
||||||
|
|
||||||
RootedObject obj(cx, ToObjectFromStack(cx, objv));
|
RootedObject obj(cx, ToObjectFromStack(cx, objv));
|
||||||
if (!obj)
|
if (!obj)
|
||||||
|
@ -5025,6 +5026,9 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_
|
||||||
JS_ASSERT(uint32_t(index.toInt32()) == GET_UINT24(pc));
|
JS_ASSERT(uint32_t(index.toInt32()) == GET_UINT24(pc));
|
||||||
if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
|
if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
|
||||||
return false;
|
return false;
|
||||||
|
} else if (op == JSOP_INITELEM_INC) {
|
||||||
|
if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!SetObjectElement(cx, obj, index, rhs, script->strict(), script, pc))
|
if (!SetObjectElement(cx, obj, index, rhs, script->strict(), script, pc))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1728,7 +1728,7 @@ CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
|
||||||
Register scratch = ToTempRegisterOrInvalid(lir->temp());
|
Register scratch = ToTempRegisterOrInvalid(lir->temp());
|
||||||
|
|
||||||
Label miss;
|
Label miss;
|
||||||
masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &miss);
|
masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), lir->mir()->barrierKind(), scratch, &miss);
|
||||||
if (!bailoutFrom(&miss, lir->snapshot()))
|
if (!bailoutFrom(&miss, lir->snapshot()))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
|
@ -1737,6 +1737,8 @@ CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
|
||||||
bool
|
bool
|
||||||
CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir)
|
CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(lir->mir()->barrierKind() != BarrierKind::TypeTagOnly);
|
||||||
|
|
||||||
Register obj = ToRegister(lir->object());
|
Register obj = ToRegister(lir->object());
|
||||||
Register scratch = ToTempRegisterOrInvalid(lir->temp());
|
Register scratch = ToTempRegisterOrInvalid(lir->temp());
|
||||||
|
|
||||||
|
@ -1754,7 +1756,7 @@ CodeGenerator::visitMonitorTypes(LMonitorTypes *lir)
|
||||||
Register scratch = ToTempUnboxRegister(lir->temp());
|
Register scratch = ToTempUnboxRegister(lir->temp());
|
||||||
|
|
||||||
Label matched, miss;
|
Label matched, miss;
|
||||||
masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &miss);
|
masm.guardTypeSet(operand, lir->mir()->typeSet(), lir->mir()->barrierKind(), scratch, &miss);
|
||||||
if (!bailoutFrom(&miss, lir->snapshot()))
|
if (!bailoutFrom(&miss, lir->snapshot()))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
|
@ -2710,7 +2712,7 @@ CodeGenerator::generateArgumentsChecks(bool bailout)
|
||||||
// ... * sizeof(Value) - Scale by value size.
|
// ... * sizeof(Value) - Scale by value size.
|
||||||
// ArgToStackOffset(...) - Compute displacement within arg vector.
|
// ArgToStackOffset(...) - Compute displacement within arg vector.
|
||||||
int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value));
|
int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value));
|
||||||
masm.guardTypeSet(Address(StackPointer, offset), types, temp, &miss);
|
masm.guardTypeSet(Address(StackPointer, offset), types, BarrierKind::TypeSet, temp, &miss);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (miss.used()) {
|
if (miss.used()) {
|
||||||
|
@ -3186,7 +3188,7 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
|
||||||
if (mir->resultTypeSet() && !mir->resultTypeSet()->unknown()) {
|
if (mir->resultTypeSet() && !mir->resultTypeSet()->unknown()) {
|
||||||
// We have a result TypeSet, assert this value is in it.
|
// We have a result TypeSet, assert this value is in it.
|
||||||
Label miss, ok;
|
Label miss, ok;
|
||||||
masm.guardTypeSet(output, mir->resultTypeSet(), temp1, &miss);
|
masm.guardTypeSet(output, mir->resultTypeSet(), BarrierKind::TypeSet, temp1, &miss);
|
||||||
masm.jump(&ok);
|
masm.jump(&ok);
|
||||||
|
|
||||||
masm.bind(&miss);
|
masm.bind(&miss);
|
||||||
|
@ -8051,7 +8053,7 @@ CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
|
||||||
|
|
||||||
case MProfilerStackOp::Enter:
|
case MProfilerStackOp::Enter:
|
||||||
if (gen->options.spsSlowAssertionsEnabled()) {
|
if (gen->options.spsSlowAssertionsEnabled()) {
|
||||||
if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
|
if (!inlinedFunction) {
|
||||||
saveLive(lir);
|
saveLive(lir);
|
||||||
pushArg(ImmGCPtr(lir->script()));
|
pushArg(ImmGCPtr(lir->script()));
|
||||||
if (!callVM(SPSEnterInfo, lir))
|
if (!callVM(SPSEnterInfo, lir))
|
||||||
|
@ -8074,7 +8076,7 @@ CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
|
||||||
|
|
||||||
case MProfilerStackOp::Exit:
|
case MProfilerStackOp::Exit:
|
||||||
if (gen->options.spsSlowAssertionsEnabled()) {
|
if (gen->options.spsSlowAssertionsEnabled()) {
|
||||||
if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
|
if (!inlinedFunction) {
|
||||||
saveLive(lir);
|
saveLive(lir);
|
||||||
pushArg(ImmGCPtr(lir->script()));
|
pushArg(ImmGCPtr(lir->script()));
|
||||||
// Once we've exited, then we shouldn't emit instrumentation for
|
// Once we've exited, then we shouldn't emit instrumentation for
|
||||||
|
|
|
@ -171,6 +171,9 @@ jit::EliminateDeadCode(MIRGenerator *mir, MIRGraph &graph)
|
||||||
!inst->hasUses() && !inst->isGuard() &&
|
!inst->hasUses() && !inst->isGuard() &&
|
||||||
!inst->isControlInstruction()) {
|
!inst->isControlInstruction()) {
|
||||||
inst = block->discardAt(inst);
|
inst = block->discardAt(inst);
|
||||||
|
} else if (!inst->hasLiveDefUses() && inst->canRecoverOnBailout()) {
|
||||||
|
inst->setRecoveredOnBailout();
|
||||||
|
inst++;
|
||||||
} else {
|
} else {
|
||||||
inst++;
|
inst++;
|
||||||
}
|
}
|
||||||
|
@ -1382,11 +1385,6 @@ jit::AssertBasicGraphCoherency(MIRGraph &graph)
|
||||||
for (size_t i = 0; i < block->numPredecessors(); i++)
|
for (size_t i = 0; i < block->numPredecessors(); i++)
|
||||||
JS_ASSERT(CheckPredecessorImpliesSuccessor(*block, block->getPredecessor(i)));
|
JS_ASSERT(CheckPredecessorImpliesSuccessor(*block, block->getPredecessor(i)));
|
||||||
|
|
||||||
// Assert that use chains are valid for this instruction.
|
|
||||||
for (MDefinitionIterator iter(*block); iter; iter++) {
|
|
||||||
for (uint32_t i = 0, e = iter->numOperands(); i < e; i++)
|
|
||||||
JS_ASSERT(CheckOperandImpliesUse(*iter, iter->getOperand(i)));
|
|
||||||
}
|
|
||||||
for (MResumePointIterator iter(block->resumePointsBegin()); iter != block->resumePointsEnd(); iter++) {
|
for (MResumePointIterator iter(block->resumePointsBegin()); iter != block->resumePointsEnd(); iter++) {
|
||||||
for (uint32_t i = 0, e = iter->numOperands(); i < e; i++) {
|
for (uint32_t i = 0, e = iter->numOperands(); i < e; i++) {
|
||||||
if (iter->getUseFor(i)->hasProducer())
|
if (iter->getUseFor(i)->hasProducer())
|
||||||
|
@ -1395,11 +1393,16 @@ jit::AssertBasicGraphCoherency(MIRGraph &graph)
|
||||||
}
|
}
|
||||||
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) {
|
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) {
|
||||||
JS_ASSERT(phi->numOperands() == block->numPredecessors());
|
JS_ASSERT(phi->numOperands() == block->numPredecessors());
|
||||||
|
MOZ_ASSERT(!phi->isRecoveredOnBailout());
|
||||||
}
|
}
|
||||||
for (MDefinitionIterator iter(*block); iter; iter++) {
|
for (MDefinitionIterator iter(*block); iter; iter++) {
|
||||||
JS_ASSERT(iter->block() == *block);
|
JS_ASSERT(iter->block() == *block);
|
||||||
for (MUseIterator i(iter->usesBegin()); i != iter->usesEnd(); i++)
|
|
||||||
JS_ASSERT(CheckUseImpliesOperand(*iter, *i));
|
// Assert that use chains are valid for this instruction.
|
||||||
|
for (uint32_t i = 0, end = iter->numOperands(); i < end; i++)
|
||||||
|
JS_ASSERT(CheckOperandImpliesUse(*iter, iter->getOperand(i)));
|
||||||
|
for (MUseIterator use(iter->usesBegin()); use != iter->usesEnd(); use++)
|
||||||
|
JS_ASSERT(CheckUseImpliesOperand(*iter, *use));
|
||||||
|
|
||||||
if (iter->isInstruction()) {
|
if (iter->isInstruction()) {
|
||||||
if (MResumePoint *resume = iter->toInstruction()->resumePoint()) {
|
if (MResumePoint *resume = iter->toInstruction()->resumePoint()) {
|
||||||
|
@ -1407,6 +1410,9 @@ jit::AssertBasicGraphCoherency(MIRGraph &graph)
|
||||||
JS_ASSERT(ins->block() == iter->block());
|
JS_ASSERT(ins->block() == iter->block());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iter->isRecoveredOnBailout())
|
||||||
|
MOZ_ASSERT(!iter->hasLiveDefUses());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5049,7 +5049,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
return pushTypeBarrier(apply, types, true);
|
return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When inlining we have the arguments the function gets called with
|
// When inlining we have the arguments the function gets called with
|
||||||
|
@ -5378,7 +5378,7 @@ IonBuilder::makeCall(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsit
|
||||||
if (call->isCallDOMNative())
|
if (call->isCallDOMNative())
|
||||||
return pushDOMTypeBarrier(call, types, call->getSingleTarget());
|
return pushDOMTypeBarrier(call, types, call->getSingleTarget());
|
||||||
|
|
||||||
return pushTypeBarrier(call, types, true);
|
return pushTypeBarrier(call, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -5426,7 +5426,7 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||||
if (!string->mightBeType(MIRType_String)) {
|
if (!string->mightBeType(MIRType_String)) {
|
||||||
current->push(string);
|
current->push(string);
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
return pushTypeBarrier(string, types, true);
|
return pushTypeBarrier(string, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
current->pushSlot(info().thisSlot());
|
current->pushSlot(info().thisSlot());
|
||||||
|
@ -5465,7 +5465,7 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||||
current->push(ins);
|
current->push(ins);
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
return resumeAfter(ins) && pushTypeBarrier(ins, types, true);
|
return resumeAfter(ins) && pushTypeBarrier(ins, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsop_call(argc, /* constructing = */ false);
|
return jsop_call(argc, /* constructing = */ false);
|
||||||
|
@ -6274,7 +6274,7 @@ IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, Pr
|
||||||
// instruction replaces the top of the stack.
|
// instruction replaces the top of the stack.
|
||||||
// (5) Lastly, a type barrier instruction replaces the top of the stack.
|
// (5) Lastly, a type barrier instruction replaces the top of the stack.
|
||||||
bool
|
bool
|
||||||
IonBuilder::pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed, bool needsBarrier)
|
IonBuilder::pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed, BarrierKind kind)
|
||||||
{
|
{
|
||||||
// Barriers are never needed for instructions whose result will not be used.
|
// Barriers are never needed for instructions whose result will not be used.
|
||||||
if (BytecodeIsPopped(pc))
|
if (BytecodeIsPopped(pc))
|
||||||
|
@ -6286,7 +6286,7 @@ IonBuilder::pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed,
|
||||||
// must be a resume point capturing the original def, and resuming
|
// must be a resume point capturing the original def, and resuming
|
||||||
// to that point will explicitly monitor the new type.
|
// to that point will explicitly monitor the new type.
|
||||||
|
|
||||||
if (!needsBarrier) {
|
if (kind == BarrierKind::NoBarrier) {
|
||||||
MDefinition *replace = ensureDefiniteType(def, observed->getKnownMIRType());
|
MDefinition *replace = ensureDefiniteType(def, observed->getKnownMIRType());
|
||||||
if (replace != def) {
|
if (replace != def) {
|
||||||
current->pop();
|
current->pop();
|
||||||
|
@ -6301,7 +6301,7 @@ IonBuilder::pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed,
|
||||||
|
|
||||||
current->pop();
|
current->pop();
|
||||||
|
|
||||||
MInstruction *barrier = MTypeBarrier::New(alloc(), def, observed);
|
MInstruction *barrier = MTypeBarrier::New(alloc(), def, observed, kind);
|
||||||
current->add(barrier);
|
current->add(barrier);
|
||||||
|
|
||||||
if (barrier->type() == MIRType_Undefined)
|
if (barrier->type() == MIRType_Undefined)
|
||||||
|
@ -6342,7 +6342,8 @@ IonBuilder::pushDOMTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *obser
|
||||||
JS_ASSERT(barrier);
|
JS_ASSERT(barrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pushTypeBarrier(replace, observed, barrier);
|
return pushTypeBarrier(replace, observed,
|
||||||
|
barrier ? BarrierKind::TypeSet : BarrierKind::NoBarrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
MDefinition *
|
MDefinition *
|
||||||
|
@ -6449,13 +6450,13 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
|
||||||
}
|
}
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticType,
|
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticType,
|
||||||
name, types, /* updateObserved = */ true);
|
name, types, /* updateObserved = */ true);
|
||||||
|
|
||||||
JSObject *singleton = types->getSingleton();
|
JSObject *singleton = types->getSingleton();
|
||||||
|
|
||||||
MIRType knownType = types->getKnownMIRType();
|
MIRType knownType = types->getKnownMIRType();
|
||||||
if (!barrier) {
|
if (barrier == BarrierKind::NoBarrier) {
|
||||||
if (singleton) {
|
if (singleton) {
|
||||||
// Try to inline a known constant value.
|
// Try to inline a known constant value.
|
||||||
if (testSingletonProperty(staticObject, name) == singleton)
|
if (testSingletonProperty(staticObject, name) == singleton)
|
||||||
|
@ -6470,7 +6471,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
|
||||||
MInstruction *obj = constant(ObjectValue(*staticObject));
|
MInstruction *obj = constant(ObjectValue(*staticObject));
|
||||||
|
|
||||||
MIRType rvalType = types->getKnownMIRType();
|
MIRType rvalType = types->getKnownMIRType();
|
||||||
if (barrier)
|
if (barrier != BarrierKind::NoBarrier)
|
||||||
rvalType = MIRType_Value;
|
rvalType = MIRType_Value;
|
||||||
|
|
||||||
return loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
|
return loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
|
||||||
|
@ -6612,7 +6613,7 @@ IonBuilder::jsop_getname(PropertyName *name)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
return pushTypeBarrier(ins, types, true);
|
return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -6631,7 +6632,7 @@ IonBuilder::jsop_intrinsic(PropertyName *name)
|
||||||
if (!resumeAfter(ins))
|
if (!resumeAfter(ins))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return pushTypeBarrier(ins, types, true);
|
return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bake in the intrinsic. Make sure that TI agrees with us on the type.
|
// Bake in the intrinsic. Make sure that TI agrees with us on the type.
|
||||||
|
@ -6696,7 +6697,7 @@ IonBuilder::jsop_getelem()
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
return pushTypeBarrier(ins, types, true);
|
return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool emitted = false;
|
bool emitted = false;
|
||||||
|
@ -6738,7 +6739,7 @@ IonBuilder::jsop_getelem()
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
return pushTypeBarrier(ins, types, true);
|
return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -7004,7 +7005,7 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
|
||||||
{
|
{
|
||||||
derivedTypedObj->setResultTypeSet(observedTypes);
|
derivedTypedObj->setResultTypeSet(observedTypes);
|
||||||
} else {
|
} else {
|
||||||
if (!pushTypeBarrier(derivedTypedObj, observedTypes, true))
|
if (!pushTypeBarrier(derivedTypedObj, observedTypes, BarrierKind::TypeSet))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7186,7 +7187,7 @@ IonBuilder::getElemTryArguments(bool *emitted, MDefinition *obj, MDefinition *in
|
||||||
current->push(load);
|
current->push(load);
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
if (!pushTypeBarrier(load, types, true))
|
if (!pushTypeBarrier(load, types, BarrierKind::TypeSet))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*emitted = true;
|
*emitted = true;
|
||||||
|
@ -7255,18 +7256,19 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
|
||||||
// Emit GetElementCache.
|
// Emit GetElementCache.
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj, nullptr, types);
|
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
|
||||||
|
nullptr, types);
|
||||||
|
|
||||||
// Always add a barrier if the index might be a string, so that the cache
|
// Always add a barrier if the index might be a string, so that the cache
|
||||||
// can attach stubs for particular properties.
|
// can attach stubs for particular properties.
|
||||||
if (index->mightBeType(MIRType_String))
|
if (index->mightBeType(MIRType_String))
|
||||||
barrier = true;
|
barrier = BarrierKind::TypeSet;
|
||||||
|
|
||||||
// See note about always needing a barrier in jsop_getprop.
|
// See note about always needing a barrier in jsop_getprop.
|
||||||
if (needsToMonitorMissingProperties(types))
|
if (needsToMonitorMissingProperties(types))
|
||||||
barrier = true;
|
barrier = BarrierKind::TypeSet;
|
||||||
|
|
||||||
MInstruction *ins = MGetElementCache::New(alloc(), obj, index, barrier);
|
MInstruction *ins = MGetElementCache::New(alloc(), obj, index, barrier != BarrierKind::NoBarrier);
|
||||||
|
|
||||||
current->add(ins);
|
current->add(ins);
|
||||||
current->push(ins);
|
current->push(ins);
|
||||||
|
@ -7275,7 +7277,7 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Spice up type information.
|
// Spice up type information.
|
||||||
if (index->type() == MIRType_Int32 && !barrier) {
|
if (index->type() == MIRType_Int32 && barrier == BarrierKind::NoBarrier) {
|
||||||
bool needHoleCheck = !ElementAccessIsPacked(constraints(), obj);
|
bool needHoleCheck = !ElementAccessIsPacked(constraints(), obj);
|
||||||
MIRType knownType = GetElemKnownType(needHoleCheck, types);
|
MIRType knownType = GetElemKnownType(needHoleCheck, types);
|
||||||
|
|
||||||
|
@ -7302,7 +7304,8 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
|
||||||
AddObjectsForPropertyRead(obj, nullptr, types);
|
AddObjectsForPropertyRead(obj, nullptr, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj, nullptr, types);
|
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
|
||||||
|
nullptr, types);
|
||||||
bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
|
bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
|
||||||
|
|
||||||
// Reads which are on holes in the object do not have to bail out if
|
// Reads which are on holes in the object do not have to bail out if
|
||||||
|
@ -7313,7 +7316,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
|
||||||
!ElementAccessHasExtraIndexedProperty(constraints(), obj);
|
!ElementAccessHasExtraIndexedProperty(constraints(), obj);
|
||||||
|
|
||||||
MIRType knownType = MIRType_Value;
|
MIRType knownType = MIRType_Value;
|
||||||
if (!barrier)
|
if (barrier == BarrierKind::NoBarrier)
|
||||||
knownType = GetElemKnownType(needsHoleCheck, types);
|
knownType = GetElemKnownType(needsHoleCheck, types);
|
||||||
|
|
||||||
// Ensure index is an integer.
|
// Ensure index is an integer.
|
||||||
|
@ -7341,7 +7344,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
|
||||||
ExecutionMode executionMode = info().executionMode();
|
ExecutionMode executionMode = info().executionMode();
|
||||||
bool loadDouble =
|
bool loadDouble =
|
||||||
executionMode == SequentialExecution &&
|
executionMode == SequentialExecution &&
|
||||||
!barrier &&
|
barrier == BarrierKind::NoBarrier &&
|
||||||
loopDepth_ &&
|
loopDepth_ &&
|
||||||
!readOutOfBounds &&
|
!readOutOfBounds &&
|
||||||
!needsHoleCheck &&
|
!needsHoleCheck &&
|
||||||
|
@ -7392,7 +7395,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
|
||||||
// NB: we have not added a MConvertElementsToDoubles MIR, so we
|
// NB: we have not added a MConvertElementsToDoubles MIR, so we
|
||||||
// cannot *assume* the result is a double.
|
// cannot *assume* the result is a double.
|
||||||
if (executionMode == ParallelExecution &&
|
if (executionMode == ParallelExecution &&
|
||||||
barrier &&
|
barrier != BarrierKind::NoBarrier &&
|
||||||
types->getKnownMIRType() == MIRType_Int32 &&
|
types->getKnownMIRType() == MIRType_Int32 &&
|
||||||
objTypes &&
|
objTypes &&
|
||||||
objTypes->convertDoubleElements(constraints()) == types::TemporaryTypeSet::AlwaysConvertToDoubles)
|
objTypes->convertDoubleElements(constraints()) == types::TemporaryTypeSet::AlwaysConvertToDoubles)
|
||||||
|
@ -7402,7 +7405,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
|
||||||
if (!types)
|
if (!types)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
barrier = false; // Don't need a barrier anymore
|
barrier = BarrierKind::NoBarrier; // Don't need a barrier anymore
|
||||||
}
|
}
|
||||||
|
|
||||||
if (knownType != MIRType_Value)
|
if (knownType != MIRType_Value)
|
||||||
|
@ -7561,7 +7564,7 @@ IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index,
|
||||||
// observed (we've only read out-of-bounds values). Note that for
|
// observed (we've only read out-of-bounds values). Note that for
|
||||||
// Uint32Array, we only check for int32: if allowDouble is false we
|
// Uint32Array, we only check for int32: if allowDouble is false we
|
||||||
// will bailout when we read a double.
|
// will bailout when we read a double.
|
||||||
bool needsBarrier = true;
|
BarrierKind barrier = BarrierKind::TypeSet;
|
||||||
switch (arrayType) {
|
switch (arrayType) {
|
||||||
case ScalarTypeDescr::TYPE_INT8:
|
case ScalarTypeDescr::TYPE_INT8:
|
||||||
case ScalarTypeDescr::TYPE_UINT8:
|
case ScalarTypeDescr::TYPE_UINT8:
|
||||||
|
@ -7571,12 +7574,12 @@ IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index,
|
||||||
case ScalarTypeDescr::TYPE_INT32:
|
case ScalarTypeDescr::TYPE_INT32:
|
||||||
case ScalarTypeDescr::TYPE_UINT32:
|
case ScalarTypeDescr::TYPE_UINT32:
|
||||||
if (types->hasType(types::Type::Int32Type()))
|
if (types->hasType(types::Type::Int32Type()))
|
||||||
needsBarrier = false;
|
barrier = BarrierKind::NoBarrier;
|
||||||
break;
|
break;
|
||||||
case ScalarTypeDescr::TYPE_FLOAT32:
|
case ScalarTypeDescr::TYPE_FLOAT32:
|
||||||
case ScalarTypeDescr::TYPE_FLOAT64:
|
case ScalarTypeDescr::TYPE_FLOAT64:
|
||||||
if (allowDouble)
|
if (allowDouble)
|
||||||
needsBarrier = false;
|
barrier = BarrierKind::NoBarrier;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
MOZ_ASSUME_UNREACHABLE("Unknown typed array type");
|
MOZ_ASSUME_UNREACHABLE("Unknown typed array type");
|
||||||
|
@ -7590,7 +7593,7 @@ IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index,
|
||||||
current->add(load);
|
current->add(load);
|
||||||
current->push(load);
|
current->push(load);
|
||||||
|
|
||||||
return pushTypeBarrier(load, types, needsBarrier);
|
return pushTypeBarrier(load, types, barrier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8494,7 +8497,7 @@ IonBuilder::invalidatedIdempotentCache()
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::loadSlot(MDefinition *obj, size_t slot, size_t nfixed, MIRType rvalType,
|
IonBuilder::loadSlot(MDefinition *obj, size_t slot, size_t nfixed, MIRType rvalType,
|
||||||
bool barrier, types::TemporaryTypeSet *types)
|
BarrierKind barrier, types::TemporaryTypeSet *types)
|
||||||
{
|
{
|
||||||
if (slot < nfixed) {
|
if (slot < nfixed) {
|
||||||
MLoadFixedSlot *load = MLoadFixedSlot::New(alloc(), obj, slot);
|
MLoadFixedSlot *load = MLoadFixedSlot::New(alloc(), obj, slot);
|
||||||
|
@ -8518,7 +8521,7 @@ IonBuilder::loadSlot(MDefinition *obj, size_t slot, size_t nfixed, MIRType rvalT
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
|
IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
|
||||||
bool barrier, types::TemporaryTypeSet *types)
|
BarrierKind barrier, types::TemporaryTypeSet *types)
|
||||||
{
|
{
|
||||||
return loadSlot(obj, shape->slot(), shape->numFixedSlots(), rvalType, barrier, types);
|
return loadSlot(obj, shape->slot(), shape->numFixedSlots(), rvalType, barrier, types);
|
||||||
}
|
}
|
||||||
|
@ -8568,7 +8571,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
|
||||||
return emitted;
|
return emitted;
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
|
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
|
||||||
current->peek(-1), name, types);
|
current->peek(-1), name, types);
|
||||||
|
|
||||||
// Always use a call if we are performing analysis and
|
// Always use a call if we are performing analysis and
|
||||||
|
@ -8591,7 +8594,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
|
||||||
|
|
||||||
current->pop();
|
current->pop();
|
||||||
current->push(call);
|
current->push(call);
|
||||||
return resumeAfter(call) && pushTypeBarrier(call, types, true);
|
return resumeAfter(call) && pushTypeBarrier(call, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to hardcode known constants.
|
// Try to hardcode known constants.
|
||||||
|
@ -8626,7 +8629,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
|
||||||
if (!resumeAfter(call))
|
if (!resumeAfter(call))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return pushTypeBarrier(call, types, true);
|
return pushTypeBarrier(call, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -8765,7 +8768,7 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
|
IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
|
||||||
bool barrier, types::TemporaryTypeSet *types)
|
BarrierKind barrier, types::TemporaryTypeSet *types)
|
||||||
{
|
{
|
||||||
JS_ASSERT(*emitted == false);
|
JS_ASSERT(*emitted == false);
|
||||||
types::HeapTypeSetKey property;
|
types::HeapTypeSetKey property;
|
||||||
|
@ -8781,7 +8784,7 @@ IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
|
||||||
}
|
}
|
||||||
|
|
||||||
MLoadFixedSlot *fixed = MLoadFixedSlot::New(alloc(), useObj, property.maybeTypes()->definiteSlot());
|
MLoadFixedSlot *fixed = MLoadFixedSlot::New(alloc(), useObj, property.maybeTypes()->definiteSlot());
|
||||||
if (!barrier)
|
if (barrier == BarrierKind::NoBarrier)
|
||||||
fixed->setResultType(types->getKnownMIRType());
|
fixed->setResultType(types->getKnownMIRType());
|
||||||
|
|
||||||
current->add(fixed);
|
current->add(fixed);
|
||||||
|
@ -8903,7 +8906,7 @@ CanInlinePropertyOpShapes(const BaselineInspector::ShapeVector &shapes)
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name,
|
IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name,
|
||||||
bool barrier, types::TemporaryTypeSet *types)
|
BarrierKind barrier, types::TemporaryTypeSet *types)
|
||||||
{
|
{
|
||||||
JS_ASSERT(*emitted == false);
|
JS_ASSERT(*emitted == false);
|
||||||
if (current->peek(-1)->type() != MIRType_Object)
|
if (current->peek(-1)->type() != MIRType_Object)
|
||||||
|
@ -8917,7 +8920,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name,
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
MIRType rvalType = types->getKnownMIRType();
|
MIRType rvalType = types->getKnownMIRType();
|
||||||
if (barrier || IsNullOrUndefined(rvalType))
|
if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
|
||||||
rvalType = MIRType_Value;
|
rvalType = MIRType_Value;
|
||||||
|
|
||||||
MDefinition *obj = current->pop();
|
MDefinition *obj = current->pop();
|
||||||
|
@ -8964,7 +8967,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name,
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::getPropTryCache(bool *emitted, PropertyName *name,
|
IonBuilder::getPropTryCache(bool *emitted, PropertyName *name,
|
||||||
bool barrier, types::TemporaryTypeSet *types)
|
BarrierKind barrier, types::TemporaryTypeSet *types)
|
||||||
{
|
{
|
||||||
JS_ASSERT(*emitted == false);
|
JS_ASSERT(*emitted == false);
|
||||||
|
|
||||||
|
@ -8981,18 +8984,19 @@ IonBuilder::getPropTryCache(bool *emitted, PropertyName *name,
|
||||||
// Since getters have no guaranteed return values, we must barrier in order to be
|
// Since getters have no guaranteed return values, we must barrier in order to be
|
||||||
// able to attach stubs for them.
|
// able to attach stubs for them.
|
||||||
if (inspector->hasSeenAccessedGetter(pc))
|
if (inspector->hasSeenAccessedGetter(pc))
|
||||||
barrier = true;
|
barrier = BarrierKind::TypeSet;
|
||||||
|
|
||||||
if (needsToMonitorMissingProperties(types))
|
if (needsToMonitorMissingProperties(types))
|
||||||
barrier = true;
|
barrier = BarrierKind::TypeSet;
|
||||||
|
|
||||||
// Caches can read values from prototypes, so update the barrier to
|
// Caches can read values from prototypes, so update the barrier to
|
||||||
// reflect such possible values.
|
// reflect such possible values.
|
||||||
if (!barrier)
|
if (barrier == BarrierKind::NoBarrier)
|
||||||
barrier = PropertyReadOnPrototypeNeedsTypeBarrier(constraints(), obj, name, types);
|
barrier = PropertyReadOnPrototypeNeedsTypeBarrier(constraints(), obj, name, types);
|
||||||
|
|
||||||
current->pop();
|
current->pop();
|
||||||
MGetPropertyCache *load = MGetPropertyCache::New(alloc(), obj, name, barrier);
|
MGetPropertyCache *load = MGetPropertyCache::New(alloc(), obj, name,
|
||||||
|
barrier != BarrierKind::NoBarrier);
|
||||||
|
|
||||||
// Try to mark the cache as idempotent.
|
// Try to mark the cache as idempotent.
|
||||||
//
|
//
|
||||||
|
@ -9019,7 +9023,7 @@ IonBuilder::getPropTryCache(bool *emitted, PropertyName *name,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MIRType rvalType = types->getKnownMIRType();
|
MIRType rvalType = types->getKnownMIRType();
|
||||||
if (barrier || IsNullOrUndefined(rvalType))
|
if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
|
||||||
rvalType = MIRType_Value;
|
rvalType = MIRType_Value;
|
||||||
load->setResultType(rvalType);
|
load->setResultType(rvalType);
|
||||||
|
|
||||||
|
@ -9863,7 +9867,7 @@ IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc)
|
||||||
current->push(load);
|
current->push(load);
|
||||||
|
|
||||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||||
return pushTypeBarrier(load, types, true);
|
return pushTypeBarrier(load, types, BarrierKind::TypeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -343,7 +343,7 @@ class IonBuilder : public MIRGenerator
|
||||||
|
|
||||||
// Add a guard which ensure that the set of type which goes through this
|
// Add a guard which ensure that the set of type which goes through this
|
||||||
// generated code correspond to the observed types for the bytecode.
|
// generated code correspond to the observed types for the bytecode.
|
||||||
bool pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed, bool needBarrier);
|
bool pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed, BarrierKind kind);
|
||||||
|
|
||||||
// As pushTypeBarrier, but will compute the needBarrier boolean itself based
|
// As pushTypeBarrier, but will compute the needBarrier boolean itself based
|
||||||
// on observed and the JSFunction that we're planning to call. The
|
// on observed and the JSFunction that we're planning to call. The
|
||||||
|
@ -380,9 +380,9 @@ class IonBuilder : public MIRGenerator
|
||||||
|
|
||||||
bool hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall);
|
bool hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall);
|
||||||
bool loadSlot(MDefinition *obj, size_t slot, size_t nfixed, MIRType rvalType,
|
bool loadSlot(MDefinition *obj, size_t slot, size_t nfixed, MIRType rvalType,
|
||||||
bool barrier, types::TemporaryTypeSet *types);
|
BarrierKind barrier, types::TemporaryTypeSet *types);
|
||||||
bool loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
|
bool loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
|
||||||
bool barrier, types::TemporaryTypeSet *types);
|
BarrierKind barrier, types::TemporaryTypeSet *types);
|
||||||
bool storeSlot(MDefinition *obj, size_t slot, size_t nfixed,
|
bool storeSlot(MDefinition *obj, size_t slot, size_t nfixed,
|
||||||
MDefinition *value, bool needsBarrier,
|
MDefinition *value, bool needsBarrier,
|
||||||
MIRType slotType = MIRType_None);
|
MIRType slotType = MIRType_None);
|
||||||
|
@ -394,11 +394,11 @@ class IonBuilder : public MIRGenerator
|
||||||
bool getPropTryConstant(bool *emitted, PropertyName *name,
|
bool getPropTryConstant(bool *emitted, PropertyName *name,
|
||||||
types::TemporaryTypeSet *types);
|
types::TemporaryTypeSet *types);
|
||||||
bool getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
|
bool getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
|
||||||
bool barrier, types::TemporaryTypeSet *types);
|
BarrierKind barrier, types::TemporaryTypeSet *types);
|
||||||
bool getPropTryCommonGetter(bool *emitted, PropertyName *name,
|
bool getPropTryCommonGetter(bool *emitted, PropertyName *name,
|
||||||
types::TemporaryTypeSet *types);
|
types::TemporaryTypeSet *types);
|
||||||
bool getPropTryInlineAccess(bool *emitted, PropertyName *name,
|
bool getPropTryInlineAccess(bool *emitted, PropertyName *name,
|
||||||
bool barrier, types::TemporaryTypeSet *types);
|
BarrierKind barrier, types::TemporaryTypeSet *types);
|
||||||
bool getPropTryTypedObject(bool *emitted, PropertyName *name,
|
bool getPropTryTypedObject(bool *emitted, PropertyName *name,
|
||||||
types::TemporaryTypeSet *resultTypes);
|
types::TemporaryTypeSet *resultTypes);
|
||||||
bool getPropTryScalarPropOfTypedObject(bool *emitted,
|
bool getPropTryScalarPropOfTypedObject(bool *emitted,
|
||||||
|
@ -411,7 +411,7 @@ class IonBuilder : public MIRGenerator
|
||||||
size_t fieldIndex,
|
size_t fieldIndex,
|
||||||
types::TemporaryTypeSet *resultTypes);
|
types::TemporaryTypeSet *resultTypes);
|
||||||
bool getPropTryCache(bool *emitted, PropertyName *name,
|
bool getPropTryCache(bool *emitted, PropertyName *name,
|
||||||
bool barrier, types::TemporaryTypeSet *types);
|
BarrierKind barrier, types::TemporaryTypeSet *types);
|
||||||
bool needsToMonitorMissingProperties(types::TemporaryTypeSet *types);
|
bool needsToMonitorMissingProperties(types::TemporaryTypeSet *types);
|
||||||
|
|
||||||
// jsop_setprop() helpers.
|
// jsop_setprop() helpers.
|
||||||
|
|
|
@ -1969,7 +1969,7 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
||||||
Register scratchReg = object;
|
Register scratchReg = object;
|
||||||
masm.push(scratchReg);
|
masm.push(scratchReg);
|
||||||
|
|
||||||
masm.guardTypeSet(valReg, propTypes, scratchReg, &barrierFailure);
|
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &barrierFailure);
|
||||||
masm.pop(object);
|
masm.pop(object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2521,7 +2521,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
||||||
JS_ASSERT(!propTypes->unknown());
|
JS_ASSERT(!propTypes->unknown());
|
||||||
|
|
||||||
Register scratchReg = object;
|
Register scratchReg = object;
|
||||||
masm.guardTypeSet(valReg, propTypes, scratchReg, &failuresPopObject);
|
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &failuresPopObject);
|
||||||
masm.loadPtr(Address(StackPointer, 0), object);
|
masm.loadPtr(Address(StackPointer, 0), object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -631,9 +631,8 @@ HandleException(ResumeFromException *rfe)
|
||||||
if (invalidated)
|
if (invalidated)
|
||||||
popSPSFrame = ionScript->hasSPSInstrumentation();
|
popSPSFrame = ionScript->hasSPSInstrumentation();
|
||||||
|
|
||||||
// If inline-frames are not profiled, then don't pop an SPS frame
|
// Don't pop an SPS frame for inlined frames, since they are not instrumented.
|
||||||
// for them.
|
if (frames.more())
|
||||||
if (frames.more() && !js_JitOptions.profileInlineFrames)
|
|
||||||
popSPSFrame = false;
|
popSPSFrame = false;
|
||||||
|
|
||||||
// When profiling, each frame popped needs a notification that
|
// When profiling, each frame popped needs a notification that
|
||||||
|
@ -1339,7 +1338,8 @@ SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshot
|
||||||
ionScript->recoversSize()),
|
ionScript->recoversSize()),
|
||||||
fp_(fp),
|
fp_(fp),
|
||||||
machine_(machine),
|
machine_(machine),
|
||||||
ionScript_(ionScript)
|
ionScript_(ionScript),
|
||||||
|
instructionResults_(nullptr)
|
||||||
{
|
{
|
||||||
JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize());
|
JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize());
|
||||||
}
|
}
|
||||||
|
@ -1354,7 +1354,8 @@ SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter)
|
||||||
iter.ionScript()->recoversSize()),
|
iter.ionScript()->recoversSize()),
|
||||||
fp_(iter.jsFrame()),
|
fp_(iter.jsFrame()),
|
||||||
machine_(iter.machineState()),
|
machine_(iter.machineState()),
|
||||||
ionScript_(iter.ionScript())
|
ionScript_(iter.ionScript()),
|
||||||
|
instructionResults_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1362,7 +1363,8 @@ SnapshotIterator::SnapshotIterator()
|
||||||
: snapshot_(nullptr, 0, 0, 0),
|
: snapshot_(nullptr, 0, 0, 0),
|
||||||
recover_(snapshot_, nullptr, 0),
|
recover_(snapshot_, nullptr, 0),
|
||||||
fp_(nullptr),
|
fp_(nullptr),
|
||||||
ionScript_(nullptr)
|
ionScript_(nullptr),
|
||||||
|
instructionResults_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1427,6 +1429,9 @@ SnapshotIterator::allocationReadable(const RValueAllocation &alloc)
|
||||||
return hasStack(alloc.stackOffset());
|
return hasStack(alloc.stackOffset());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case RValueAllocation::RECOVER_INSTRUCTION:
|
||||||
|
return hasInstructionResult(alloc.index());
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1532,6 +1537,9 @@ SnapshotIterator::allocationValue(const RValueAllocation &alloc)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case RValueAllocation::RECOVER_INSTRUCTION:
|
||||||
|
return fromInstructionResult(alloc.index());
|
||||||
|
|
||||||
default:
|
default:
|
||||||
MOZ_ASSUME_UNREACHABLE("huh?");
|
MOZ_ASSUME_UNREACHABLE("huh?");
|
||||||
}
|
}
|
||||||
|
@ -1546,7 +1554,7 @@ SnapshotIterator::resumePoint() const
|
||||||
uint32_t
|
uint32_t
|
||||||
SnapshotIterator::numAllocations() const
|
SnapshotIterator::numAllocations() const
|
||||||
{
|
{
|
||||||
return resumePoint()->numOperands();
|
return instruction()->numOperands();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
|
@ -1565,6 +1573,43 @@ SnapshotIterator::skipInstruction()
|
||||||
nextInstruction();
|
nextInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
SnapshotIterator::initIntructionResults(AutoValueVector &results)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(recover_.numInstructionsRead() == 1);
|
||||||
|
|
||||||
|
// The last instruction will always be a resume point, no need to allocate
|
||||||
|
// space for it.
|
||||||
|
if (recover_.numInstructions() == 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
MOZ_ASSERT(recover_.numInstructions() > 1);
|
||||||
|
size_t numResults = recover_.numInstructions() - 1;
|
||||||
|
if (!results.reserve(numResults))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numResults; i++)
|
||||||
|
results.infallibleAppend(MagicValue(JS_ION_BAILOUT));
|
||||||
|
|
||||||
|
instructionResults_ = &results;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SnapshotIterator::storeInstructionResult(Value v)
|
||||||
|
{
|
||||||
|
uint32_t currIns = recover_.numInstructionsRead() - 1;
|
||||||
|
MOZ_ASSERT((*instructionResults_)[currIns].isMagic(JS_ION_BAILOUT));
|
||||||
|
(*instructionResults_)[currIns].set(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value
|
||||||
|
SnapshotIterator::fromInstructionResult(uint32_t index) const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!(*instructionResults_)[index].isMagic(JS_ION_BAILOUT));
|
||||||
|
return (*instructionResults_)[index];
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SnapshotIterator::nextFrame()
|
SnapshotIterator::nextFrame()
|
||||||
{
|
{
|
||||||
|
|
|
@ -69,9 +69,10 @@ class TypeWrapper {
|
||||||
} /* anonymous namespace */
|
} /* anonymous namespace */
|
||||||
|
|
||||||
template <typename Source, typename TypeSet> void
|
template <typename Source, typename TypeSet> void
|
||||||
MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types,
|
MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types, BarrierKind kind,
|
||||||
Register scratch, Label *miss)
|
Register scratch, Label *miss)
|
||||||
{
|
{
|
||||||
|
JS_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
|
||||||
JS_ASSERT(!types->unknown());
|
JS_ASSERT(!types->unknown());
|
||||||
|
|
||||||
Label matched;
|
Label matched;
|
||||||
|
@ -126,8 +127,10 @@ MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types,
|
||||||
// Test specific objects.
|
// Test specific objects.
|
||||||
JS_ASSERT(scratch != InvalidReg);
|
JS_ASSERT(scratch != InvalidReg);
|
||||||
branchTestObject(NotEqual, tag, miss);
|
branchTestObject(NotEqual, tag, miss);
|
||||||
|
if (kind != BarrierKind::TypeTagOnly) {
|
||||||
Register obj = extractObject(address, scratch);
|
Register obj = extractObject(address, scratch);
|
||||||
guardObjectType(obj, types, scratch, miss);
|
guardObjectType(obj, types, scratch, miss);
|
||||||
|
}
|
||||||
|
|
||||||
bind(&matched);
|
bind(&matched);
|
||||||
}
|
}
|
||||||
|
@ -203,30 +206,30 @@ MacroAssembler::guardType(const Source &address, types::Type type,
|
||||||
Register scratch, Label *miss)
|
Register scratch, Label *miss)
|
||||||
{
|
{
|
||||||
TypeWrapper wrapper(type);
|
TypeWrapper wrapper(type);
|
||||||
guardTypeSet(address, &wrapper, scratch, miss);
|
guardTypeSet(address, &wrapper, BarrierKind::TypeSet, scratch, miss);
|
||||||
}
|
}
|
||||||
|
|
||||||
template void MacroAssembler::guardTypeSet(const Address &address, const types::TemporaryTypeSet *types,
|
template void MacroAssembler::guardTypeSet(const Address &address, const types::TemporaryTypeSet *types,
|
||||||
Register scratch, Label *miss);
|
BarrierKind kind, Register scratch, Label *miss);
|
||||||
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::TemporaryTypeSet *types,
|
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::TemporaryTypeSet *types,
|
||||||
Register scratch, Label *miss);
|
BarrierKind kind, Register scratch, Label *miss);
|
||||||
|
|
||||||
template void MacroAssembler::guardTypeSet(const Address &address, const types::HeapTypeSet *types,
|
template void MacroAssembler::guardTypeSet(const Address &address, const types::HeapTypeSet *types,
|
||||||
Register scratch, Label *miss);
|
BarrierKind kind, Register scratch, Label *miss);
|
||||||
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::HeapTypeSet *types,
|
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::HeapTypeSet *types,
|
||||||
Register scratch, Label *miss);
|
BarrierKind kind, Register scratch, Label *miss);
|
||||||
template void MacroAssembler::guardTypeSet(const TypedOrValueRegister ®, const types::HeapTypeSet *types,
|
template void MacroAssembler::guardTypeSet(const TypedOrValueRegister ®, const types::HeapTypeSet *types,
|
||||||
Register scratch, Label *miss);
|
BarrierKind kind, Register scratch, Label *miss);
|
||||||
|
|
||||||
template void MacroAssembler::guardTypeSet(const Address &address, const types::TypeSet *types,
|
template void MacroAssembler::guardTypeSet(const Address &address, const types::TypeSet *types,
|
||||||
Register scratch, Label *miss);
|
BarrierKind kind, Register scratch, Label *miss);
|
||||||
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::TypeSet *types,
|
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::TypeSet *types,
|
||||||
Register scratch, Label *miss);
|
BarrierKind kind, Register scratch, Label *miss);
|
||||||
|
|
||||||
template void MacroAssembler::guardTypeSet(const Address &address, const TypeWrapper *types,
|
template void MacroAssembler::guardTypeSet(const Address &address, const TypeWrapper *types,
|
||||||
Register scratch, Label *miss);
|
BarrierKind kind, Register scratch, Label *miss);
|
||||||
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const TypeWrapper *types,
|
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const TypeWrapper *types,
|
||||||
Register scratch, Label *miss);
|
BarrierKind kind, Register scratch, Label *miss);
|
||||||
|
|
||||||
template void MacroAssembler::guardObjectType(Register obj, const types::TemporaryTypeSet *types,
|
template void MacroAssembler::guardObjectType(Register obj, const types::TemporaryTypeSet *types,
|
||||||
Register scratch, Label *miss);
|
Register scratch, Label *miss);
|
||||||
|
|
|
@ -300,7 +300,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
// Emits a test of a value against all types in a TypeSet. A scratch
|
// Emits a test of a value against all types in a TypeSet. A scratch
|
||||||
// register is required.
|
// register is required.
|
||||||
template <typename Source, typename TypeSet>
|
template <typename Source, typename TypeSet>
|
||||||
void guardTypeSet(const Source &address, const TypeSet *types, Register scratch, Label *miss);
|
void guardTypeSet(const Source &address, const TypeSet *types, BarrierKind kind, Register scratch, Label *miss);
|
||||||
template <typename TypeSet>
|
template <typename TypeSet>
|
||||||
void guardObjectType(Register obj, const TypeSet *types, Register scratch, Label *miss);
|
void guardObjectType(Register obj, const TypeSet *types, Register scratch, Label *miss);
|
||||||
template <typename Source>
|
template <typename Source>
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#ifndef jit_IonTypes_h
|
#ifndef jit_IonTypes_h
|
||||||
#define jit_IonTypes_h
|
#define jit_IonTypes_h
|
||||||
|
|
||||||
|
#include "mozilla/TypedEnum.h"
|
||||||
|
|
||||||
#include "jstypes.h"
|
#include "jstypes.h"
|
||||||
|
|
||||||
#include "js/Value.h"
|
#include "js/Value.h"
|
||||||
|
@ -324,6 +326,19 @@ enum ABIFunctionType
|
||||||
(ArgType_General << (ArgType_Shift * 2))
|
(ArgType_General << (ArgType_Shift * 2))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MOZ_BEGIN_ENUM_CLASS(BarrierKind, uint32_t)
|
||||||
|
// No barrier is needed.
|
||||||
|
NoBarrier,
|
||||||
|
|
||||||
|
// The barrier only has to check the value's type tag is in the TypeSet.
|
||||||
|
// Specific object types don't have to be checked.
|
||||||
|
TypeTagOnly,
|
||||||
|
|
||||||
|
// Check if the value is in the TypeSet, including the object type if it's
|
||||||
|
// an object.
|
||||||
|
TypeSet
|
||||||
|
MOZ_END_ENUM_CLASS(BarrierKind)
|
||||||
|
|
||||||
} // namespace jit
|
} // namespace jit
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
|
|
@ -258,6 +258,7 @@ class SnapshotIterator
|
||||||
IonJSFrameLayout *fp_;
|
IonJSFrameLayout *fp_;
|
||||||
MachineState machine_;
|
MachineState machine_;
|
||||||
IonScript *ionScript_;
|
IonScript *ionScript_;
|
||||||
|
AutoValueVector *instructionResults_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Read a spilled register from the machine state.
|
// Read a spilled register from the machine state.
|
||||||
|
@ -281,6 +282,11 @@ class SnapshotIterator
|
||||||
}
|
}
|
||||||
uintptr_t fromStack(int32_t offset) const;
|
uintptr_t fromStack(int32_t offset) const;
|
||||||
|
|
||||||
|
bool hasInstructionResult(uint32_t index) const {
|
||||||
|
return instructionResults_;
|
||||||
|
}
|
||||||
|
Value fromInstructionResult(uint32_t index) const;
|
||||||
|
|
||||||
Value allocationValue(const RValueAllocation &a);
|
Value allocationValue(const RValueAllocation &a);
|
||||||
bool allocationReadable(const RValueAllocation &a);
|
bool allocationReadable(const RValueAllocation &a);
|
||||||
void warnUnreadableAllocation();
|
void warnUnreadableAllocation();
|
||||||
|
@ -338,6 +344,14 @@ class SnapshotIterator
|
||||||
return recover_.moreInstructions();
|
return recover_.moreInstructions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register a vector used for storing the results of the evaluation of
|
||||||
|
// recover instructions. This vector should be registered before the
|
||||||
|
// beginning of the iteration. This function is in charge of allocating
|
||||||
|
// enough space for all instructions results, and return false iff it fails.
|
||||||
|
bool initIntructionResults(AutoValueVector &results);
|
||||||
|
|
||||||
|
void storeInstructionResult(Value v);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Handle iterating over frames of the snapshots.
|
// Handle iterating over frames of the snapshots.
|
||||||
void nextFrame();
|
void nextFrame();
|
||||||
|
|
|
@ -110,9 +110,6 @@ JitOptions::JitOptions()
|
||||||
|
|
||||||
// How many uses of a parallel kernel before we attempt compilation.
|
// How many uses of a parallel kernel before we attempt compilation.
|
||||||
usesBeforeCompilePar = 1;
|
usesBeforeCompilePar = 1;
|
||||||
|
|
||||||
// Whether to profile inlined functions in Ion or not.
|
|
||||||
profileInlineFrames = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -69,7 +69,6 @@ struct JitOptions
|
||||||
uint32_t osrPcMismatchesBeforeRecompile;
|
uint32_t osrPcMismatchesBeforeRecompile;
|
||||||
uint32_t smallFunctionMaxBytecodeLength_;
|
uint32_t smallFunctionMaxBytecodeLength_;
|
||||||
uint32_t usesBeforeCompilePar;
|
uint32_t usesBeforeCompilePar;
|
||||||
bool profileInlineFrames;
|
|
||||||
|
|
||||||
JitOptions();
|
JitOptions();
|
||||||
bool isSmallFunction(JSScript *script) const;
|
bool isSmallFunction(JSScript *script) const;
|
||||||
|
|
|
@ -111,11 +111,16 @@ LBlock::getExitMoveGroup(TempAllocator &alloc)
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
TotalOperandCount(MResumePoint *mir)
|
TotalOperandCount(LRecoverInfo *recoverInfo)
|
||||||
{
|
{
|
||||||
size_t accum = mir->numOperands();
|
LRecoverInfo::OperandIter it(recoverInfo->begin());
|
||||||
while ((mir = mir->caller()))
|
LRecoverInfo::OperandIter end(recoverInfo->end());
|
||||||
accum += mir->numOperands();
|
size_t accum = 0;
|
||||||
|
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
if (!it->isRecoveredOnBailout())
|
||||||
|
accum++;
|
||||||
|
}
|
||||||
return accum;
|
return accum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,28 +142,71 @@ LRecoverInfo::New(MIRGenerator *gen, MResumePoint *mir)
|
||||||
return recoverInfo;
|
return recoverInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LRecoverInfo::appendOperands(MNode *ins)
|
||||||
|
{
|
||||||
|
for (size_t i = 0, end = ins->numOperands(); i < end; i++) {
|
||||||
|
MDefinition *def = ins->getOperand(i);
|
||||||
|
|
||||||
|
// As there is no cycle in the data-flow (without MPhi), checking for
|
||||||
|
// isInWorkList implies that the definition is already in the
|
||||||
|
// instruction vector, and not processed by a caller of the current
|
||||||
|
// function.
|
||||||
|
if (def->isRecoveredOnBailout() && !def->isInWorklist()) {
|
||||||
|
if (!appendDefinition(def))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LRecoverInfo::appendDefinition(MDefinition *def)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(def->isRecoveredOnBailout());
|
||||||
|
def->setInWorklist();
|
||||||
|
if (!appendOperands(def))
|
||||||
|
return false;
|
||||||
|
return instructions_.append(def);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LRecoverInfo::appendResumePoint(MResumePoint *rp)
|
||||||
|
{
|
||||||
|
if (rp->caller() && !appendResumePoint(rp->caller()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!appendOperands(rp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return instructions_.append(rp);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
LRecoverInfo::init(MResumePoint *rp)
|
LRecoverInfo::init(MResumePoint *rp)
|
||||||
{
|
{
|
||||||
MResumePoint *it = rp;
|
|
||||||
|
|
||||||
// Sort operations in the order in which we need to restore the stack. This
|
// Sort operations in the order in which we need to restore the stack. This
|
||||||
// implies that outer frames, as well as operations needed to recover the
|
// implies that outer frames, as well as operations needed to recover the
|
||||||
// current frame, are located before the current frame. The inner-most
|
// current frame, are located before the current frame. The inner-most
|
||||||
// resume point should be the last element in the list.
|
// resume point should be the last element in the list.
|
||||||
do {
|
if (!appendResumePoint(rp))
|
||||||
if (!instructions_.append(it))
|
|
||||||
return false;
|
return false;
|
||||||
it = it->caller();
|
|
||||||
} while (it);
|
|
||||||
|
|
||||||
Reverse(instructions_.begin(), instructions_.end());
|
// Remove temporary flags from all definitions.
|
||||||
|
for (MNode **it = begin(); it != end(); it++) {
|
||||||
|
if (!(*it)->isDefinition())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
(*it)->toDefinition()->setNotInWorklist();
|
||||||
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(mir() == rp);
|
MOZ_ASSERT(mir() == rp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
LSnapshot::LSnapshot(LRecoverInfo *recoverInfo, BailoutKind kind)
|
LSnapshot::LSnapshot(LRecoverInfo *recoverInfo, BailoutKind kind)
|
||||||
: numSlots_(TotalOperandCount(recoverInfo->mir()) * BOX_PIECES),
|
: numSlots_(TotalOperandCount(recoverInfo) * BOX_PIECES),
|
||||||
slots_(nullptr),
|
slots_(nullptr),
|
||||||
recoverInfo_(recoverInfo),
|
recoverInfo_(recoverInfo),
|
||||||
snapshotOffset_(INVALID_SNAPSHOT_OFFSET),
|
snapshotOffset_(INVALID_SNAPSHOT_OFFSET),
|
||||||
|
@ -368,8 +416,6 @@ LInstruction::dump(FILE *fp)
|
||||||
fprintf(fp, "} <- ");
|
fprintf(fp, "} <- ");
|
||||||
|
|
||||||
printName(fp);
|
printName(fp);
|
||||||
|
|
||||||
|
|
||||||
printInfo(fp);
|
printInfo(fp);
|
||||||
|
|
||||||
if (numTemps()) {
|
if (numTemps()) {
|
||||||
|
@ -381,13 +427,13 @@ LInstruction::dump(FILE *fp)
|
||||||
}
|
}
|
||||||
fprintf(fp, ")");
|
fprintf(fp, ")");
|
||||||
}
|
}
|
||||||
fprintf(fp, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LInstruction::dump()
|
LInstruction::dump()
|
||||||
{
|
{
|
||||||
return dump(stderr);
|
dump(stderr);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -879,7 +879,7 @@ class LCallInstructionHelper : public LInstructionHelper<Defs, Operands, Temps>
|
||||||
class LRecoverInfo : public TempObject
|
class LRecoverInfo : public TempObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef Vector<MResumePoint *, 2, IonAllocPolicy> Instructions;
|
typedef Vector<MNode *, 2, IonAllocPolicy> Instructions;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// List of instructions needed to recover the stack frames.
|
// List of instructions needed to recover the stack frames.
|
||||||
|
@ -892,12 +892,17 @@ class LRecoverInfo : public TempObject
|
||||||
LRecoverInfo(TempAllocator &alloc);
|
LRecoverInfo(TempAllocator &alloc);
|
||||||
bool init(MResumePoint *mir);
|
bool init(MResumePoint *mir);
|
||||||
|
|
||||||
|
// Fill the instruction vector such as all instructions needed for the
|
||||||
|
// recovery are pushed before the current instruction.
|
||||||
|
bool appendOperands(MNode *ins);
|
||||||
|
bool appendDefinition(MDefinition *def);
|
||||||
|
bool appendResumePoint(MResumePoint *rp);
|
||||||
public:
|
public:
|
||||||
static LRecoverInfo *New(MIRGenerator *gen, MResumePoint *mir);
|
static LRecoverInfo *New(MIRGenerator *gen, MResumePoint *mir);
|
||||||
|
|
||||||
// Resume point of the inner most function.
|
// Resume point of the inner most function.
|
||||||
MResumePoint *mir() const {
|
MResumePoint *mir() const {
|
||||||
return instructions_.back();
|
return instructions_.back()->toResumePoint();
|
||||||
}
|
}
|
||||||
RecoverOffset recoverOffset() const {
|
RecoverOffset recoverOffset() const {
|
||||||
return recoverOffset_;
|
return recoverOffset_;
|
||||||
|
@ -907,12 +912,47 @@ class LRecoverInfo : public TempObject
|
||||||
recoverOffset_ = offset;
|
recoverOffset_ = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
MResumePoint **begin() {
|
MNode **begin() {
|
||||||
return instructions_.begin();
|
return instructions_.begin();
|
||||||
}
|
}
|
||||||
MResumePoint **end() {
|
MNode **end() {
|
||||||
return instructions_.end();
|
return instructions_.end();
|
||||||
}
|
}
|
||||||
|
size_t numInstructions() const {
|
||||||
|
return instructions_.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
class OperandIter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
MNode **it_;
|
||||||
|
size_t op_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OperandIter(MNode **it)
|
||||||
|
: it_(it), op_(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
MDefinition *operator *() {
|
||||||
|
return (*it_)->getOperand(op_);
|
||||||
|
}
|
||||||
|
MDefinition *operator ->() {
|
||||||
|
return (*it_)->getOperand(op_);
|
||||||
|
}
|
||||||
|
|
||||||
|
OperandIter &operator ++() {
|
||||||
|
++op_;
|
||||||
|
if (op_ == (*it_)->numOperands()) {
|
||||||
|
op_ = 0;
|
||||||
|
++it_;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator !=(const OperandIter &where) const {
|
||||||
|
return it_ != where.it_ || op_ != where.op_;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// An LSnapshot is the reflection of an MResumePoint in LIR. Unlike MResumePoints,
|
// An LSnapshot is the reflection of an MResumePoint in LIR. Unlike MResumePoints,
|
||||||
|
|
|
@ -2303,7 +2303,8 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle typebarrier with specific TypeObject/SingleObjects.
|
// Handle typebarrier with specific TypeObject/SingleObjects.
|
||||||
if (inputType == MIRType_Object && !types->hasType(types::Type::AnyObjectType()))
|
if (inputType == MIRType_Object && !types->hasType(types::Type::AnyObjectType()) &&
|
||||||
|
ins->barrierKind() != BarrierKind::TypeTagOnly)
|
||||||
{
|
{
|
||||||
LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp();
|
LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp();
|
||||||
LTypeBarrierO *barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
|
LTypeBarrierO *barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
|
||||||
|
@ -3597,6 +3598,9 @@ SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint
|
||||||
bool
|
bool
|
||||||
LIRGenerator::visitInstruction(MInstruction *ins)
|
LIRGenerator::visitInstruction(MInstruction *ins)
|
||||||
{
|
{
|
||||||
|
if (ins->isRecoveredOnBailout())
|
||||||
|
return true;
|
||||||
|
|
||||||
if (!gen->ensureBallast())
|
if (!gen->ensureBallast())
|
||||||
return false;
|
return false;
|
||||||
if (!ins->accept(this))
|
if (!ins->accept(this))
|
||||||
|
|
|
@ -385,9 +385,9 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
|
||||||
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
|
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
|
||||||
bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
|
bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
|
||||||
|
|
||||||
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
|
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
|
||||||
callInfo.thisArg(), nullptr, returnTypes);
|
callInfo.thisArg(), nullptr, returnTypes);
|
||||||
if (barrier)
|
if (barrier != BarrierKind::NoBarrier)
|
||||||
returnType = MIRType_Value;
|
returnType = MIRType_Value;
|
||||||
|
|
||||||
MArrayPopShift *ins = MArrayPopShift::New(alloc(), callInfo.thisArg(), mode,
|
MArrayPopShift *ins = MArrayPopShift::New(alloc(), callInfo.thisArg(), mode,
|
||||||
|
@ -1231,7 +1231,7 @@ IonBuilder::inlineRegExpExec(CallInfo &callInfo)
|
||||||
if (!resumeAfter(exec))
|
if (!resumeAfter(exec))
|
||||||
return InliningStatus_Error;
|
return InliningStatus_Error;
|
||||||
|
|
||||||
if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), true))
|
if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), BarrierKind::TypeSet))
|
||||||
return InliningStatus_Error;
|
return InliningStatus_Error;
|
||||||
|
|
||||||
return InliningStatus_Inlined;
|
return InliningStatus_Inlined;
|
||||||
|
@ -1782,7 +1782,7 @@ IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo)
|
||||||
current->push(load);
|
current->push(load);
|
||||||
|
|
||||||
// We don't track reserved slot types, so always emit a barrier.
|
// We don't track reserved slot types, so always emit a barrier.
|
||||||
if (!pushTypeBarrier(load, getInlineReturnTypeSet(), true))
|
if (!pushTypeBarrier(load, getInlineReturnTypeSet(), BarrierKind::TypeSet))
|
||||||
return InliningStatus_Error;
|
return InliningStatus_Error;
|
||||||
|
|
||||||
return InliningStatus_Inlined;
|
return InliningStatus_Inlined;
|
||||||
|
|
|
@ -359,6 +359,20 @@ MDefinition::hasDefUses() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MDefinition::hasLiveDefUses() const
|
||||||
|
{
|
||||||
|
for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
|
||||||
|
MNode *ins = (*i)->consumer();
|
||||||
|
if (!ins->isDefinition())
|
||||||
|
continue;
|
||||||
|
if (!ins->toDefinition()->isRecoveredOnBailout())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
MUseIterator
|
MUseIterator
|
||||||
MDefinition::removeUse(MUseIterator use)
|
MDefinition::removeUse(MUseIterator use)
|
||||||
{
|
{
|
||||||
|
@ -3089,7 +3103,7 @@ jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinit
|
||||||
return elementType;
|
return elementType;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static BarrierKind
|
||||||
PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints,
|
PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints,
|
||||||
types::TypeObjectKey *object, PropertyName *name,
|
types::TypeObjectKey *object, PropertyName *name,
|
||||||
types::TypeSet *observed)
|
types::TypeSet *observed)
|
||||||
|
@ -3105,13 +3119,22 @@ PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints,
|
||||||
if (object->unknownProperties() || observed->empty() ||
|
if (object->unknownProperties() || observed->empty() ||
|
||||||
object->clasp()->isProxy())
|
object->clasp()->isProxy())
|
||||||
{
|
{
|
||||||
return true;
|
return BarrierKind::TypeSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
jsid id = name ? NameToId(name) : JSID_VOID;
|
jsid id = name ? NameToId(name) : JSID_VOID;
|
||||||
types::HeapTypeSetKey property = object->property(id);
|
types::HeapTypeSetKey property = object->property(id);
|
||||||
if (property.maybeTypes() && !TypeSetIncludes(observed, MIRType_Value, property.maybeTypes()))
|
if (property.maybeTypes()) {
|
||||||
return true;
|
if (!TypeSetIncludes(observed, MIRType_Value, property.maybeTypes())) {
|
||||||
|
// If all possible objects have been observed, we don't have to
|
||||||
|
// guard on the specific object types.
|
||||||
|
if (property.maybeTypes()->objectsAreSubset(observed)) {
|
||||||
|
property.freeze(constraints);
|
||||||
|
return BarrierKind::TypeTagOnly;
|
||||||
|
}
|
||||||
|
return BarrierKind::TypeSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Type information for global objects is not required to reflect the
|
// Type information for global objects is not required to reflect the
|
||||||
// initial 'undefined' value for properties, in particular global
|
// initial 'undefined' value for properties, in particular global
|
||||||
|
@ -3121,15 +3144,15 @@ PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints,
|
||||||
if (name && types::CanHaveEmptyPropertyTypesForOwnProperty(obj) &&
|
if (name && types::CanHaveEmptyPropertyTypesForOwnProperty(obj) &&
|
||||||
(!property.maybeTypes() || property.maybeTypes()->empty()))
|
(!property.maybeTypes() || property.maybeTypes()->empty()))
|
||||||
{
|
{
|
||||||
return true;
|
return BarrierKind::TypeSet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property.freeze(constraints);
|
property.freeze(constraints);
|
||||||
return false;
|
return BarrierKind::NoBarrier;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
BarrierKind
|
||||||
jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
||||||
types::CompilerConstraintList *constraints,
|
types::CompilerConstraintList *constraints,
|
||||||
types::TypeObjectKey *object, PropertyName *name,
|
types::TypeObjectKey *object, PropertyName *name,
|
||||||
|
@ -3177,45 +3200,55 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
||||||
return PropertyReadNeedsTypeBarrier(constraints, object, name, observed);
|
return PropertyReadNeedsTypeBarrier(constraints, object, name, observed);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
BarrierKind
|
||||||
jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
||||||
types::CompilerConstraintList *constraints,
|
types::CompilerConstraintList *constraints,
|
||||||
MDefinition *obj, PropertyName *name,
|
MDefinition *obj, PropertyName *name,
|
||||||
types::TemporaryTypeSet *observed)
|
types::TemporaryTypeSet *observed)
|
||||||
{
|
{
|
||||||
if (observed->unknown())
|
if (observed->unknown())
|
||||||
return false;
|
return BarrierKind::NoBarrier;
|
||||||
|
|
||||||
types::TypeSet *types = obj->resultTypeSet();
|
types::TypeSet *types = obj->resultTypeSet();
|
||||||
if (!types || types->unknownObject())
|
if (!types || types->unknownObject())
|
||||||
return true;
|
return BarrierKind::TypeSet;
|
||||||
|
|
||||||
|
BarrierKind res = BarrierKind::NoBarrier;
|
||||||
|
|
||||||
bool updateObserved = types->getObjectCount() == 1;
|
bool updateObserved = types->getObjectCount() == 1;
|
||||||
for (size_t i = 0; i < types->getObjectCount(); i++) {
|
for (size_t i = 0; i < types->getObjectCount(); i++) {
|
||||||
types::TypeObjectKey *object = types->getObject(i);
|
types::TypeObjectKey *object = types->getObject(i);
|
||||||
if (object) {
|
if (object) {
|
||||||
if (PropertyReadNeedsTypeBarrier(propertycx, constraints, object, name,
|
BarrierKind kind = PropertyReadNeedsTypeBarrier(propertycx, constraints, object, name,
|
||||||
observed, updateObserved))
|
observed, updateObserved);
|
||||||
{
|
if (kind == BarrierKind::TypeSet)
|
||||||
return true;
|
return BarrierKind::TypeSet;
|
||||||
|
|
||||||
|
if (kind == BarrierKind::TypeTagOnly) {
|
||||||
|
MOZ_ASSERT(res == BarrierKind::NoBarrier || res == BarrierKind::TypeTagOnly);
|
||||||
|
res = BarrierKind::TypeTagOnly;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(kind == BarrierKind::NoBarrier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
BarrierKind
|
||||||
jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
|
jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
|
||||||
MDefinition *obj, PropertyName *name,
|
MDefinition *obj, PropertyName *name,
|
||||||
types::TemporaryTypeSet *observed)
|
types::TemporaryTypeSet *observed)
|
||||||
{
|
{
|
||||||
if (observed->unknown())
|
if (observed->unknown())
|
||||||
return false;
|
return BarrierKind::NoBarrier;
|
||||||
|
|
||||||
types::TypeSet *types = obj->resultTypeSet();
|
types::TypeSet *types = obj->resultTypeSet();
|
||||||
if (!types || types->unknownObject())
|
if (!types || types->unknownObject())
|
||||||
return true;
|
return BarrierKind::TypeSet;
|
||||||
|
|
||||||
|
BarrierKind res = BarrierKind::NoBarrier;
|
||||||
|
|
||||||
for (size_t i = 0; i < types->getObjectCount(); i++) {
|
for (size_t i = 0; i < types->getObjectCount(); i++) {
|
||||||
types::TypeObjectKey *object = types->getObject(i);
|
types::TypeObjectKey *object = types->getObject(i);
|
||||||
|
@ -3223,16 +3256,24 @@ jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *cons
|
||||||
continue;
|
continue;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!object->hasTenuredProto())
|
if (!object->hasTenuredProto())
|
||||||
return true;
|
return BarrierKind::TypeSet;
|
||||||
if (!object->proto().isObject())
|
if (!object->proto().isObject())
|
||||||
break;
|
break;
|
||||||
object = types::TypeObjectKey::get(object->proto().toObject());
|
object = types::TypeObjectKey::get(object->proto().toObject());
|
||||||
if (PropertyReadNeedsTypeBarrier(constraints, object, name, observed))
|
BarrierKind kind = PropertyReadNeedsTypeBarrier(constraints, object, name, observed);
|
||||||
return true;
|
if (kind == BarrierKind::TypeSet)
|
||||||
|
return BarrierKind::TypeSet;
|
||||||
|
|
||||||
|
if (kind == BarrierKind::TypeTagOnly) {
|
||||||
|
MOZ_ASSERT(res == BarrierKind::NoBarrier || res == BarrierKind::TypeTagOnly);
|
||||||
|
res = BarrierKind::TypeTagOnly;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(kind == BarrierKind::NoBarrier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -3382,7 +3423,13 @@ TryAddTypeBarrierForWrite(TempAllocator &alloc, types::CompilerConstraintList *c
|
||||||
if (!types)
|
if (!types)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MInstruction *ins = MMonitorTypes::New(alloc, *pvalue, types);
|
// If all possible objects can be stored without a barrier, we don't have to
|
||||||
|
// guard on the specific object types.
|
||||||
|
BarrierKind kind = BarrierKind::TypeSet;
|
||||||
|
if ((*pvalue)->resultTypeSet() && (*pvalue)->resultTypeSet()->objectsAreSubset(types))
|
||||||
|
kind = BarrierKind::TypeTagOnly;
|
||||||
|
|
||||||
|
MInstruction *ins = MMonitorTypes::New(alloc, *pvalue, types, kind);
|
||||||
current->add(ins);
|
current->add(ins);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,14 @@ MIRType MIRTypeFromValue(const js::Value &vp)
|
||||||
* Truncate Doubles. So every time removeUse is called, UseRemoved needs
|
* Truncate Doubles. So every time removeUse is called, UseRemoved needs
|
||||||
* to get set.
|
* to get set.
|
||||||
*/ \
|
*/ \
|
||||||
_(UseRemoved)
|
_(UseRemoved) \
|
||||||
|
\
|
||||||
|
/* Marks if the current instruction should go to the bailout paths instead
|
||||||
|
* of producing code as part of the control flow. This flag can only be set
|
||||||
|
* on instructions which are only used by ResumePoint or by other flagged
|
||||||
|
* instructions.
|
||||||
|
*/ \
|
||||||
|
_(RecoveredOnBailout)
|
||||||
|
|
||||||
class MDefinition;
|
class MDefinition;
|
||||||
class MInstruction;
|
class MInstruction;
|
||||||
|
@ -208,6 +215,8 @@ class MNode : public TempObject
|
||||||
inline MDefinition *toDefinition();
|
inline MDefinition *toDefinition();
|
||||||
inline MResumePoint *toResumePoint();
|
inline MResumePoint *toResumePoint();
|
||||||
|
|
||||||
|
virtual bool writeRecoverData(CompactBufferWriter &writer) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Sets an unset operand, updating use information.
|
// Sets an unset operand, updating use information.
|
||||||
virtual void setOperand(size_t index, MDefinition *operand) = 0;
|
virtual void setOperand(size_t index, MDefinition *operand) = 0;
|
||||||
|
@ -531,6 +540,10 @@ class MDefinition : public MNode
|
||||||
// (only counting MDefinitions, ignoring MResumePoints)
|
// (only counting MDefinitions, ignoring MResumePoints)
|
||||||
bool hasDefUses() const;
|
bool hasDefUses() const;
|
||||||
|
|
||||||
|
// Test whether this MDefinition has at least one non-recovered use.
|
||||||
|
// (only counting MDefinitions, ignoring MResumePoints)
|
||||||
|
bool hasLiveDefUses() const;
|
||||||
|
|
||||||
bool hasUses() const {
|
bool hasUses() const {
|
||||||
return !uses_.empty();
|
return !uses_.empty();
|
||||||
}
|
}
|
||||||
|
@ -606,6 +619,10 @@ class MDefinition : public MNode
|
||||||
JS_ASSERT(getAliasSet().flags() & store->getAliasSet().flags());
|
JS_ASSERT(getAliasSet().flags() & store->getAliasSet().flags());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool canRecoverOnBailout() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// An MUseDefIterator walks over uses in a definition, skipping any use that is
|
// An MUseDefIterator walks over uses in a definition, skipping any use that is
|
||||||
|
@ -4000,6 +4017,11 @@ class MAdd : public MBinaryArithInstruction
|
||||||
void computeRange(TempAllocator &alloc);
|
void computeRange(TempAllocator &alloc);
|
||||||
bool truncate();
|
bool truncate();
|
||||||
bool isOperandTruncated(size_t index) const;
|
bool isOperandTruncated(size_t index) const;
|
||||||
|
|
||||||
|
bool writeRecoverData(CompactBufferWriter &writer) const;
|
||||||
|
bool canRecoverOnBailout() const {
|
||||||
|
return specialization_ < MIRType_Object;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class MSub : public MBinaryArithInstruction
|
class MSub : public MBinaryArithInstruction
|
||||||
|
@ -9070,10 +9092,15 @@ class MTypeBarrier
|
||||||
: public MUnaryInstruction,
|
: public MUnaryInstruction,
|
||||||
public TypeBarrierPolicy
|
public TypeBarrierPolicy
|
||||||
{
|
{
|
||||||
MTypeBarrier(MDefinition *def, types::TemporaryTypeSet *types)
|
BarrierKind barrierKind_;
|
||||||
: MUnaryInstruction(def)
|
|
||||||
|
MTypeBarrier(MDefinition *def, types::TemporaryTypeSet *types, BarrierKind kind)
|
||||||
|
: MUnaryInstruction(def),
|
||||||
|
barrierKind_(kind)
|
||||||
{
|
{
|
||||||
JS_ASSERT(!types->unknown());
|
MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
|
||||||
|
|
||||||
|
MOZ_ASSERT(!types->unknown());
|
||||||
setResultType(types->getKnownMIRType());
|
setResultType(types->getKnownMIRType());
|
||||||
setResultTypeSet(types);
|
setResultTypeSet(types);
|
||||||
|
|
||||||
|
@ -9084,8 +9111,9 @@ class MTypeBarrier
|
||||||
public:
|
public:
|
||||||
INSTRUCTION_HEADER(TypeBarrier)
|
INSTRUCTION_HEADER(TypeBarrier)
|
||||||
|
|
||||||
static MTypeBarrier *New(TempAllocator &alloc, MDefinition *def, types::TemporaryTypeSet *types) {
|
static MTypeBarrier *New(TempAllocator &alloc, MDefinition *def, types::TemporaryTypeSet *types,
|
||||||
return new(alloc) MTypeBarrier(def, types);
|
BarrierKind kind = BarrierKind::TypeSet) {
|
||||||
|
return new(alloc) MTypeBarrier(def, types, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
void printOpcode(FILE *fp) const;
|
void printOpcode(FILE *fp) const;
|
||||||
|
@ -9103,6 +9131,9 @@ class MTypeBarrier
|
||||||
virtual bool neverHoist() const {
|
virtual bool neverHoist() const {
|
||||||
return resultTypeSet()->empty();
|
return resultTypeSet()->empty();
|
||||||
}
|
}
|
||||||
|
BarrierKind barrierKind() const {
|
||||||
|
return barrierKind_;
|
||||||
|
}
|
||||||
|
|
||||||
bool alwaysBails() const {
|
bool alwaysBails() const {
|
||||||
// If mirtype of input doesn't agree with mirtype of barrier,
|
// If mirtype of input doesn't agree with mirtype of barrier,
|
||||||
|
@ -9122,20 +9153,25 @@ class MTypeBarrier
|
||||||
class MMonitorTypes : public MUnaryInstruction, public BoxInputsPolicy
|
class MMonitorTypes : public MUnaryInstruction, public BoxInputsPolicy
|
||||||
{
|
{
|
||||||
const types::TemporaryTypeSet *typeSet_;
|
const types::TemporaryTypeSet *typeSet_;
|
||||||
|
BarrierKind barrierKind_;
|
||||||
|
|
||||||
MMonitorTypes(MDefinition *def, const types::TemporaryTypeSet *types)
|
MMonitorTypes(MDefinition *def, const types::TemporaryTypeSet *types, BarrierKind kind)
|
||||||
: MUnaryInstruction(def),
|
: MUnaryInstruction(def),
|
||||||
typeSet_(types)
|
typeSet_(types),
|
||||||
|
barrierKind_(kind)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
|
||||||
|
|
||||||
setGuard();
|
setGuard();
|
||||||
JS_ASSERT(!types->unknown());
|
MOZ_ASSERT(!types->unknown());
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
INSTRUCTION_HEADER(MonitorTypes)
|
INSTRUCTION_HEADER(MonitorTypes)
|
||||||
|
|
||||||
static MMonitorTypes *New(TempAllocator &alloc, MDefinition *def, const types::TemporaryTypeSet *types) {
|
static MMonitorTypes *New(TempAllocator &alloc, MDefinition *def, const types::TemporaryTypeSet *types,
|
||||||
return new(alloc) MMonitorTypes(def, types);
|
BarrierKind kind) {
|
||||||
|
return new(alloc) MMonitorTypes(def, types, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePolicy *typePolicy() {
|
TypePolicy *typePolicy() {
|
||||||
|
@ -9145,6 +9181,10 @@ class MMonitorTypes : public MUnaryInstruction, public BoxInputsPolicy
|
||||||
const types::TemporaryTypeSet *typeSet() const {
|
const types::TemporaryTypeSet *typeSet() const {
|
||||||
return typeSet_;
|
return typeSet_;
|
||||||
}
|
}
|
||||||
|
BarrierKind barrierKind() const {
|
||||||
|
return barrierKind_;
|
||||||
|
}
|
||||||
|
|
||||||
AliasSet getAliasSet() const {
|
AliasSet getAliasSet() const {
|
||||||
return AliasSet::None();
|
return AliasSet::None();
|
||||||
}
|
}
|
||||||
|
@ -10094,15 +10134,15 @@ bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefiniti
|
||||||
bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
|
bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
|
||||||
MDefinition *obj);
|
MDefinition *obj);
|
||||||
MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj);
|
MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj);
|
||||||
bool PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
BarrierKind PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
||||||
types::CompilerConstraintList *constraints,
|
types::CompilerConstraintList *constraints,
|
||||||
types::TypeObjectKey *object, PropertyName *name,
|
types::TypeObjectKey *object, PropertyName *name,
|
||||||
types::TemporaryTypeSet *observed, bool updateObserved);
|
types::TemporaryTypeSet *observed, bool updateObserved);
|
||||||
bool PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
BarrierKind PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
||||||
types::CompilerConstraintList *constraints,
|
types::CompilerConstraintList *constraints,
|
||||||
MDefinition *obj, PropertyName *name,
|
MDefinition *obj, PropertyName *name,
|
||||||
types::TemporaryTypeSet *observed);
|
types::TemporaryTypeSet *observed);
|
||||||
bool PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
|
BarrierKind PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
|
||||||
MDefinition *obj, PropertyName *name,
|
MDefinition *obj, PropertyName *name,
|
||||||
types::TemporaryTypeSet *observed);
|
types::TemporaryTypeSet *observed);
|
||||||
bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
|
bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
|
||||||
|
|
|
@ -6,21 +6,42 @@
|
||||||
|
|
||||||
#include "jit/Recover.h"
|
#include "jit/Recover.h"
|
||||||
|
|
||||||
|
#include "jscntxt.h"
|
||||||
|
#include "jsmath.h"
|
||||||
|
|
||||||
#include "jit/IonSpewer.h"
|
#include "jit/IonSpewer.h"
|
||||||
|
#include "jit/JitFrameIterator.h"
|
||||||
#include "jit/MIR.h"
|
#include "jit/MIR.h"
|
||||||
#include "jit/MIRGraph.h"
|
#include "jit/MIRGraph.h"
|
||||||
|
|
||||||
|
#include "vm/Interpreter.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::jit;
|
using namespace js::jit;
|
||||||
|
|
||||||
|
bool
|
||||||
|
MNode::writeRecoverData(CompactBufferWriter &writer) const
|
||||||
|
{
|
||||||
|
MOZ_ASSUME_UNREACHABLE("This instruction is not serializable");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RInstruction::readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw)
|
RInstruction::readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw)
|
||||||
{
|
{
|
||||||
uint32_t op = reader.readUnsigned();
|
uint32_t op = reader.readUnsigned();
|
||||||
switch (Opcode(op)) {
|
switch (Opcode(op)) {
|
||||||
case Recover_ResumePoint:
|
# define MATCH_OPCODES_(op) \
|
||||||
new (raw->addr()) RResumePoint(reader);
|
case Recover_##op: \
|
||||||
|
static_assert(sizeof(R##op) <= sizeof(RInstructionStorage), \
|
||||||
|
"Storage space is too small to decode R" #op " instructions."); \
|
||||||
|
new (raw->addr()) R##op(reader); \
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
RECOVER_OPCODE_LIST(MATCH_OPCODES_)
|
||||||
|
# undef DEFINE_OPCODES_
|
||||||
|
|
||||||
|
case Recover_Invalid:
|
||||||
default:
|
default:
|
||||||
MOZ_ASSUME_UNREACHABLE("Bad decoding of the previous instruction?");
|
MOZ_ASSUME_UNREACHABLE("Bad decoding of the previous instruction?");
|
||||||
break;
|
break;
|
||||||
|
@ -101,10 +122,48 @@ MResumePoint::writeRecoverData(CompactBufferWriter &writer) const
|
||||||
|
|
||||||
RResumePoint::RResumePoint(CompactBufferReader &reader)
|
RResumePoint::RResumePoint(CompactBufferReader &reader)
|
||||||
{
|
{
|
||||||
static_assert(sizeof(*this) <= sizeof(RInstructionStorage),
|
|
||||||
"Storage space is too small to decode this recover instruction.");
|
|
||||||
pcOffset_ = reader.readUnsigned();
|
pcOffset_ = reader.readUnsigned();
|
||||||
numOperands_ = reader.readUnsigned();
|
numOperands_ = reader.readUnsigned();
|
||||||
IonSpew(IonSpew_Snapshots, "Read RResumePoint (pc offset %u, nslots %u)",
|
IonSpew(IonSpew_Snapshots, "Read RResumePoint (pc offset %u, nslots %u)",
|
||||||
pcOffset_, numOperands_);
|
pcOffset_, numOperands_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
RResumePoint::recover(JSContext *cx, SnapshotIterator &iter) const
|
||||||
|
{
|
||||||
|
MOZ_ASSUME_UNREACHABLE("This instruction is not recoverable.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MAdd::writeRecoverData(CompactBufferWriter &writer) const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(canRecoverOnBailout());
|
||||||
|
writer.writeUnsigned(uint32_t(RInstruction::Recover_Add));
|
||||||
|
writer.writeByte(specialization_ == MIRType_Float32);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RAdd::RAdd(CompactBufferReader &reader)
|
||||||
|
{
|
||||||
|
isFloatOperation_ = reader.readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
RAdd::recover(JSContext *cx, SnapshotIterator &iter) const
|
||||||
|
{
|
||||||
|
RootedValue lhs(cx, iter.read());
|
||||||
|
RootedValue rhs(cx, iter.read());
|
||||||
|
RootedValue result(cx);
|
||||||
|
|
||||||
|
MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
|
||||||
|
if (!js::AddValues(cx, &lhs, &rhs, &result))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MIRType_Float32 is a specialization embedding the fact that the result is
|
||||||
|
// rounded to a Float32.
|
||||||
|
if (isFloatOperation_ && !RoundFloat32(cx, result, &result))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
iter.storeInstructionResult(result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -11,44 +11,71 @@
|
||||||
|
|
||||||
#include "jit/Snapshots.h"
|
#include "jit/Snapshots.h"
|
||||||
|
|
||||||
|
class JSContext;
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
namespace jit {
|
namespace jit {
|
||||||
|
|
||||||
|
#define RECOVER_OPCODE_LIST(_) \
|
||||||
|
_(ResumePoint) \
|
||||||
|
_(Add)
|
||||||
|
|
||||||
class RResumePoint;
|
class RResumePoint;
|
||||||
|
class SnapshotIterator;
|
||||||
|
|
||||||
class RInstruction
|
class RInstruction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Opcode
|
enum Opcode
|
||||||
{
|
{
|
||||||
Recover_ResumePoint = 0
|
# define DEFINE_OPCODES_(op) Recover_##op,
|
||||||
|
RECOVER_OPCODE_LIST(DEFINE_OPCODES_)
|
||||||
|
# undef DEFINE_OPCODES_
|
||||||
|
Recover_Invalid
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual Opcode opcode() const = 0;
|
virtual Opcode opcode() const = 0;
|
||||||
|
|
||||||
|
// As opposed to the MIR, there is no need to add more methods as every
|
||||||
|
// other instruction is well abstracted under the "recover" method.
|
||||||
bool isResumePoint() const {
|
bool isResumePoint() const {
|
||||||
return opcode() == Recover_ResumePoint;
|
return opcode() == Recover_ResumePoint;
|
||||||
}
|
}
|
||||||
inline const RResumePoint *toResumePoint() const;
|
inline const RResumePoint *toResumePoint() const;
|
||||||
|
|
||||||
|
// Number of allocations which are encoded in the Snapshot for recovering
|
||||||
|
// the current instruction.
|
||||||
virtual uint32_t numOperands() const = 0;
|
virtual uint32_t numOperands() const = 0;
|
||||||
|
|
||||||
|
// Function used to recover the value computed by this instruction. This
|
||||||
|
// function reads its arguments from the allocations listed on the snapshot
|
||||||
|
// iterator and stores its returned value on the snapshot iterator too.
|
||||||
|
virtual bool recover(JSContext *cx, SnapshotIterator &iter) const = 0;
|
||||||
|
|
||||||
|
// Decode an RInstruction on top of the reserved storage space, based on the
|
||||||
|
// tag written by the writeRecoverData function of the corresponding MIR
|
||||||
|
// instruction.
|
||||||
static void readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw);
|
static void readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define RINSTRUCTION_HEADER_(op) \
|
||||||
|
private: \
|
||||||
|
friend class RInstruction; \
|
||||||
|
R##op(CompactBufferReader &reader); \
|
||||||
|
\
|
||||||
|
public: \
|
||||||
|
Opcode opcode() const { \
|
||||||
|
return RInstruction::Recover_##op; \
|
||||||
|
}
|
||||||
|
|
||||||
class RResumePoint MOZ_FINAL : public RInstruction
|
class RResumePoint MOZ_FINAL : public RInstruction
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
uint32_t pcOffset_; // Offset from script->code.
|
uint32_t pcOffset_; // Offset from script->code.
|
||||||
uint32_t numOperands_; // Number of slots.
|
uint32_t numOperands_; // Number of slots.
|
||||||
|
|
||||||
friend class RInstruction;
|
|
||||||
RResumePoint(CompactBufferReader &reader);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual Opcode opcode() const {
|
RINSTRUCTION_HEADER_(ResumePoint)
|
||||||
return Recover_ResumePoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t pcOffset() const {
|
uint32_t pcOffset() const {
|
||||||
return pcOffset_;
|
return pcOffset_;
|
||||||
|
@ -56,8 +83,26 @@ class RResumePoint MOZ_FINAL : public RInstruction
|
||||||
virtual uint32_t numOperands() const {
|
virtual uint32_t numOperands() const {
|
||||||
return numOperands_;
|
return numOperands_;
|
||||||
}
|
}
|
||||||
|
bool recover(JSContext *cx, SnapshotIterator &iter) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RAdd MOZ_FINAL : public RInstruction
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool isFloatOperation_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RINSTRUCTION_HEADER_(Add)
|
||||||
|
|
||||||
|
virtual uint32_t numOperands() const {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool recover(JSContext *cx, SnapshotIterator &iter) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef RINSTRUCTION_HEADER_
|
||||||
|
|
||||||
const RResumePoint *
|
const RResumePoint *
|
||||||
RInstruction::toResumePoint() const
|
RInstruction::toResumePoint() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,6 +76,9 @@ using namespace js::jit;
|
||||||
// first register/stack-offset correspond to the holder of the type,
|
// first register/stack-offset correspond to the holder of the type,
|
||||||
// and the second correspond to the payload of the JS Value.
|
// and the second correspond to the payload of the JS Value.
|
||||||
//
|
//
|
||||||
|
// RECOVER_INSTRUCTION [INDEX]
|
||||||
|
// Index into the list of recovered instruction results.
|
||||||
|
//
|
||||||
// TYPED_REG [PACKED_TAG, GPR_REG]:
|
// TYPED_REG [PACKED_TAG, GPR_REG]:
|
||||||
// Value with statically known type, which payload is stored in a
|
// Value with statically known type, which payload is stored in a
|
||||||
// register.
|
// register.
|
||||||
|
@ -219,6 +222,15 @@ RValueAllocation::layoutFromMode(Mode mode)
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
case RECOVER_INSTRUCTION: {
|
||||||
|
static const RValueAllocation::Layout layout = {
|
||||||
|
PAYLOAD_INDEX,
|
||||||
|
PAYLOAD_NONE,
|
||||||
|
"instruction"
|
||||||
|
};
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
static const RValueAllocation::Layout regLayout = {
|
static const RValueAllocation::Layout regLayout = {
|
||||||
PAYLOAD_PACKED_TAG,
|
PAYLOAD_PACKED_TAG,
|
||||||
|
@ -662,20 +674,20 @@ SnapshotWriter::endSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
RecoverOffset
|
RecoverOffset
|
||||||
RecoverWriter::startRecover(uint32_t frameCount, bool resumeAfter)
|
RecoverWriter::startRecover(uint32_t instructionCount, bool resumeAfter)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(frameCount);
|
MOZ_ASSERT(instructionCount);
|
||||||
nframes_ = frameCount;
|
instructionCount_ = instructionCount;
|
||||||
framesWritten_ = 0;
|
instructionsWritten_ = 0;
|
||||||
|
|
||||||
IonSpew(IonSpew_Snapshots, "starting recover with frameCount %u",
|
IonSpew(IonSpew_Snapshots, "starting recover with %u instruction(s)",
|
||||||
frameCount);
|
instructionCount);
|
||||||
|
|
||||||
MOZ_ASSERT(!(uint32_t(resumeAfter) &~ RECOVER_RESUMEAFTER_MASK));
|
MOZ_ASSERT(!(uint32_t(resumeAfter) &~ RECOVER_RESUMEAFTER_MASK));
|
||||||
MOZ_ASSERT(frameCount < uint32_t(1 << RECOVER_RINSCOUNT_BITS));
|
MOZ_ASSERT(instructionCount < uint32_t(1 << RECOVER_RINSCOUNT_BITS));
|
||||||
uint32_t bits =
|
uint32_t bits =
|
||||||
(uint32_t(resumeAfter) << RECOVER_RESUMEAFTER_SHIFT) |
|
(uint32_t(resumeAfter) << RECOVER_RESUMEAFTER_SHIFT) |
|
||||||
(frameCount << RECOVER_RINSCOUNT_SHIFT);
|
(instructionCount << RECOVER_RINSCOUNT_SHIFT);
|
||||||
|
|
||||||
RecoverOffset recoverOffset = writer_.length();
|
RecoverOffset recoverOffset = writer_.length();
|
||||||
writer_.writeUnsigned(bits);
|
writer_.writeUnsigned(bits);
|
||||||
|
@ -683,16 +695,16 @@ RecoverWriter::startRecover(uint32_t frameCount, bool resumeAfter)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
RecoverWriter::writeFrame(const MResumePoint *rp)
|
RecoverWriter::writeInstruction(const MNode *rp)
|
||||||
{
|
{
|
||||||
if (!rp->writeRecoverData(writer_))
|
if (!rp->writeRecoverData(writer_))
|
||||||
return false;
|
return false;
|
||||||
framesWritten_++;
|
instructionsWritten_++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RecoverWriter::endRecover()
|
RecoverWriter::endRecover()
|
||||||
{
|
{
|
||||||
JS_ASSERT(nframes_ == framesWritten_);
|
MOZ_ASSERT(instructionCount_ == instructionsWritten_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,8 @@ class RValueAllocation
|
||||||
UNTYPED_REG = 0x06,
|
UNTYPED_REG = 0x06,
|
||||||
UNTYPED_STACK = 0x07,
|
UNTYPED_STACK = 0x07,
|
||||||
#endif
|
#endif
|
||||||
|
RECOVER_INSTRUCTION = 0x0a,
|
||||||
|
|
||||||
// The JSValueType is packed in the Mode.
|
// The JSValueType is packed in the Mode.
|
||||||
TYPED_REG_MIN = 0x10,
|
TYPED_REG_MIN = 0x10,
|
||||||
TYPED_REG_MAX = 0x17,
|
TYPED_REG_MAX = 0x17,
|
||||||
|
@ -236,6 +238,11 @@ class RValueAllocation
|
||||||
return RValueAllocation(CONSTANT, payloadOfIndex(index));
|
return RValueAllocation(CONSTANT, payloadOfIndex(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recover instruction's index
|
||||||
|
static RValueAllocation RecoverInstruction(uint32_t index) {
|
||||||
|
return RValueAllocation(RECOVER_INSTRUCTION, payloadOfIndex(index));
|
||||||
|
}
|
||||||
|
|
||||||
void writeHeader(CompactBufferWriter &writer, JSValueType type, uint32_t regCode) const;
|
void writeHeader(CompactBufferWriter &writer, JSValueType type, uint32_t regCode) const;
|
||||||
public:
|
public:
|
||||||
static RValueAllocation read(CompactBufferReader &reader);
|
static RValueAllocation read(CompactBufferReader &reader);
|
||||||
|
@ -360,19 +367,19 @@ class SnapshotWriter
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class MResumePoint;
|
class MNode;
|
||||||
|
|
||||||
class RecoverWriter
|
class RecoverWriter
|
||||||
{
|
{
|
||||||
CompactBufferWriter writer_;
|
CompactBufferWriter writer_;
|
||||||
|
|
||||||
uint32_t nframes_;
|
uint32_t instructionCount_;
|
||||||
uint32_t framesWritten_;
|
uint32_t instructionsWritten_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SnapshotOffset startRecover(uint32_t frameCount, bool resumeAfter);
|
SnapshotOffset startRecover(uint32_t instructionCount, bool resumeAfter);
|
||||||
|
|
||||||
bool writeFrame(const MResumePoint *rp);
|
bool writeInstruction(const MNode *rp);
|
||||||
|
|
||||||
void endRecover();
|
void endRecover();
|
||||||
|
|
||||||
|
@ -473,6 +480,13 @@ class RecoverReader
|
||||||
public:
|
public:
|
||||||
RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size);
|
RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size);
|
||||||
|
|
||||||
|
uint32_t numInstructions() const {
|
||||||
|
return numInstructions_;
|
||||||
|
}
|
||||||
|
uint32_t numInstructionsRead() const {
|
||||||
|
return numInstructionsRead_;
|
||||||
|
}
|
||||||
|
|
||||||
bool moreInstructions() const {
|
bool moreInstructions() const {
|
||||||
return numInstructionsRead_ < numInstructions_;
|
return numInstructionsRead_ < numInstructions_;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1402,6 +1402,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||||
ma_mov(Imm32(0), reg, NoSetCond, Signed);
|
ma_mov(Imm32(0), reg, NoSetCond, Signed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void incrementInt32Value(const Address &addr) {
|
||||||
|
add32(Imm32(1), ToPayload(addr));
|
||||||
|
}
|
||||||
|
|
||||||
void cmp32(const Register &lhs, const Imm32 &rhs);
|
void cmp32(const Register &lhs, const Imm32 &rhs);
|
||||||
void cmp32(const Register &lhs, const Register &rhs);
|
void cmp32(const Register &lhs, const Register &rhs);
|
||||||
void cmp32(const Operand &lhs, const Imm32 &rhs);
|
void cmp32(const Operand &lhs, const Imm32 &rhs);
|
||||||
|
|
|
@ -137,25 +137,37 @@ ToStackIndex(LAllocation *a)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resumePoint,
|
CodeGeneratorShared::encodeAllocation(LSnapshot *snapshot, MDefinition *mir,
|
||||||
uint32_t *startIndex)
|
uint32_t *allocIndex)
|
||||||
{
|
{
|
||||||
IonSpew(IonSpew_Codegen, "Encoding %u of resume point %p's operands starting from %u",
|
|
||||||
resumePoint->numOperands(), (void *) resumePoint, *startIndex);
|
|
||||||
for (uint32_t allocno = 0, e = resumePoint->numOperands(); allocno < e; allocno++) {
|
|
||||||
uint32_t i = allocno + *startIndex;
|
|
||||||
MDefinition *mir = resumePoint->getOperand(allocno);
|
|
||||||
|
|
||||||
if (mir->isBox())
|
if (mir->isBox())
|
||||||
mir = mir->toBox()->getOperand(0);
|
mir = mir->toBox()->getOperand(0);
|
||||||
|
|
||||||
MIRType type = mir->isUnused()
|
MIRType type =
|
||||||
? MIRType_MagicOptimizedOut
|
mir->isRecoveredOnBailout() ? MIRType_None :
|
||||||
: mir->type();
|
mir->isUnused() ? MIRType_MagicOptimizedOut :
|
||||||
|
mir->type();
|
||||||
|
|
||||||
RValueAllocation alloc;
|
RValueAllocation alloc;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case MIRType_None:
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mir->isRecoveredOnBailout());
|
||||||
|
uint32_t index = 0;
|
||||||
|
LRecoverInfo *recoverInfo = snapshot->recoverInfo();
|
||||||
|
MNode **it = recoverInfo->begin(), **end = recoverInfo->end();
|
||||||
|
while (it != end && mir != *it) {
|
||||||
|
++it;
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This MDefinition is recovered, thus it should be listed in the
|
||||||
|
// LRecoverInfo.
|
||||||
|
MOZ_ASSERT(it != end && mir == *it);
|
||||||
|
alloc = RValueAllocation::RecoverInstruction(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case MIRType_Undefined:
|
case MIRType_Undefined:
|
||||||
alloc = RValueAllocation::Undefined();
|
alloc = RValueAllocation::Undefined();
|
||||||
break;
|
break;
|
||||||
|
@ -169,7 +181,7 @@ CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resume
|
||||||
case MIRType_Double:
|
case MIRType_Double:
|
||||||
case MIRType_Float32:
|
case MIRType_Float32:
|
||||||
{
|
{
|
||||||
LAllocation *payload = snapshot->payloadOfSlot(i);
|
LAllocation *payload = snapshot->payloadOfSlot(*allocIndex);
|
||||||
JSValueType valueType = ValueTypeFromMIRType(type);
|
JSValueType valueType = ValueTypeFromMIRType(type);
|
||||||
if (payload->isMemory()) {
|
if (payload->isMemory()) {
|
||||||
if (type == MIRType_Float32)
|
if (type == MIRType_Float32)
|
||||||
|
@ -209,9 +221,9 @@ CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resume
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
JS_ASSERT(mir->type() == MIRType_Value);
|
JS_ASSERT(mir->type() == MIRType_Value);
|
||||||
LAllocation *payload = snapshot->payloadOfSlot(i);
|
LAllocation *payload = snapshot->payloadOfSlot(*allocIndex);
|
||||||
#ifdef JS_NUNBOX32
|
#ifdef JS_NUNBOX32
|
||||||
LAllocation *type = snapshot->typeOfSlot(i);
|
LAllocation *type = snapshot->typeOfSlot(*allocIndex);
|
||||||
if (type->isRegister()) {
|
if (type->isRegister()) {
|
||||||
if (payload->isRegister())
|
if (payload->isRegister())
|
||||||
alloc = RValueAllocation::Untyped(ToRegister(type), ToRegister(payload));
|
alloc = RValueAllocation::Untyped(ToRegister(type), ToRegister(payload));
|
||||||
|
@ -234,9 +246,7 @@ CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resume
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshots_.add(alloc);
|
snapshots_.add(alloc);
|
||||||
}
|
*allocIndex += mir->isRecoveredOnBailout() ? 0 : 1;
|
||||||
|
|
||||||
*startIndex += resumePoint->numOperands();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,21 +256,18 @@ CodeGeneratorShared::encode(LRecoverInfo *recover)
|
||||||
if (recover->recoverOffset() != INVALID_RECOVER_OFFSET)
|
if (recover->recoverOffset() != INVALID_RECOVER_OFFSET)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
uint32_t frameCount = recover->mir()->frameCount();
|
uint32_t numInstructions = recover->numInstructions();
|
||||||
IonSpew(IonSpew_Snapshots, "Encoding LRecoverInfo %p (frameCount %u)",
|
IonSpew(IonSpew_Snapshots, "Encoding LRecoverInfo %p (frameCount %u, instructions %u)",
|
||||||
(void *)recover, frameCount);
|
(void *)recover, recover->mir()->frameCount(), numInstructions);
|
||||||
|
|
||||||
MResumePoint::Mode mode = recover->mir()->mode();
|
MResumePoint::Mode mode = recover->mir()->mode();
|
||||||
JS_ASSERT(mode != MResumePoint::Outer);
|
JS_ASSERT(mode != MResumePoint::Outer);
|
||||||
bool resumeAfter = (mode == MResumePoint::ResumeAfter);
|
bool resumeAfter = (mode == MResumePoint::ResumeAfter);
|
||||||
|
|
||||||
RecoverOffset offset = recovers_.startRecover(frameCount, resumeAfter);
|
RecoverOffset offset = recovers_.startRecover(numInstructions, resumeAfter);
|
||||||
|
|
||||||
for (MResumePoint **it = recover->begin(), **end = recover->end();
|
for (MNode **it = recover->begin(), **end = recover->end(); it != end; ++it) {
|
||||||
it != end;
|
if (!recovers_.writeInstruction(*it))
|
||||||
++it)
|
|
||||||
{
|
|
||||||
if (!recovers_.writeFrame(*it))
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,17 +314,17 @@ CodeGeneratorShared::encode(LSnapshot *snapshot)
|
||||||
snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId);
|
snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t startIndex = 0;
|
uint32_t allocIndex = 0;
|
||||||
for (MResumePoint **it = recoverInfo->begin(), **end = recoverInfo->end();
|
LRecoverInfo::OperandIter it(recoverInfo->begin());
|
||||||
it != end;
|
LRecoverInfo::OperandIter end(recoverInfo->end());
|
||||||
++it)
|
for (; it != end; ++it) {
|
||||||
{
|
DebugOnly<uint32_t> allocWritten = snapshots_.allocWritten();
|
||||||
MResumePoint *mir = *it;
|
if (!encodeAllocation(snapshot, *it, &allocIndex))
|
||||||
if (!encodeAllocations(snapshot, mir, &startIndex))
|
|
||||||
return false;
|
return false;
|
||||||
|
MOZ_ASSERT(allocWritten + 1 == snapshots_.allocWritten());
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(snapshots_.allocWritten() == snapshot->numSlots());
|
MOZ_ASSERT(allocIndex == snapshot->numSlots());
|
||||||
snapshots_.endSnapshot();
|
snapshots_.endSnapshot();
|
||||||
snapshot->setSnapshotOffset(offset);
|
snapshot->setSnapshotOffset(offset);
|
||||||
return !snapshots_.oom();
|
return !snapshots_.oom();
|
||||||
|
|
|
@ -272,7 +272,7 @@ class CodeGeneratorShared : public LInstructionVisitor
|
||||||
// false on failure.
|
// false on failure.
|
||||||
bool encode(LRecoverInfo *recover);
|
bool encode(LRecoverInfo *recover);
|
||||||
bool encode(LSnapshot *snapshot);
|
bool encode(LSnapshot *snapshot);
|
||||||
bool encodeAllocations(LSnapshot *snapshot, MResumePoint *resumePoint, uint32_t *startIndex);
|
bool encodeAllocation(LSnapshot *snapshot, MDefinition *def, uint32_t *startIndex);
|
||||||
|
|
||||||
// Attempts to assign a BailoutId to a snapshot, if one isn't already set.
|
// Attempts to assign a BailoutId to a snapshot, if one isn't already set.
|
||||||
// If the bailout table is full, this returns false, which is not a fatal
|
// If the bailout table is full, this returns false, which is not a fatal
|
||||||
|
|
|
@ -74,22 +74,25 @@ LIRGeneratorShared::getRecoverInfo(MResumePoint *rp)
|
||||||
LSnapshot *
|
LSnapshot *
|
||||||
LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKind kind)
|
LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKind kind)
|
||||||
{
|
{
|
||||||
LRecoverInfo *recover = getRecoverInfo(rp);
|
LRecoverInfo *recoverInfo = getRecoverInfo(rp);
|
||||||
if (!recover)
|
if (!recoverInfo)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
LSnapshot *snapshot = LSnapshot::New(gen, recover, kind);
|
LSnapshot *snapshot = LSnapshot::New(gen, recoverInfo, kind);
|
||||||
if (!snapshot)
|
if (!snapshot)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
size_t i = 0;
|
size_t index = 0;
|
||||||
for (MResumePoint **it = recover->begin(), **end = recover->end(); it != end; ++it) {
|
LRecoverInfo::OperandIter it(recoverInfo->begin());
|
||||||
MResumePoint *mir = *it;
|
LRecoverInfo::OperandIter end(recoverInfo->end());
|
||||||
for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) {
|
for (; it != end; ++it) {
|
||||||
MDefinition *ins = mir->getOperand(j);
|
MDefinition *ins = *it;
|
||||||
|
if (ins->isRecoveredOnBailout())
|
||||||
|
continue;
|
||||||
|
|
||||||
LAllocation *type = snapshot->typeOfSlot(i);
|
LAllocation *type = snapshot->typeOfSlot(index);
|
||||||
LAllocation *payload = snapshot->payloadOfSlot(i);
|
LAllocation *payload = snapshot->payloadOfSlot(index);
|
||||||
|
++index;
|
||||||
|
|
||||||
if (ins->isBox())
|
if (ins->isBox())
|
||||||
ins = ins->toBox()->getOperand(0);
|
ins = ins->toBox()->getOperand(0);
|
||||||
|
@ -118,7 +121,6 @@ LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKi
|
||||||
*payload = usePayload(ins, LUse::KEEPALIVE);
|
*payload = usePayload(ins, LUse::KEEPALIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
|
@ -128,19 +130,22 @@ LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKi
|
||||||
LSnapshot *
|
LSnapshot *
|
||||||
LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKind kind)
|
LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKind kind)
|
||||||
{
|
{
|
||||||
LRecoverInfo *recover = getRecoverInfo(rp);
|
LRecoverInfo *recoverInfo = getRecoverInfo(rp);
|
||||||
if (!recover)
|
if (!recoverInfo)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
LSnapshot *snapshot = LSnapshot::New(gen, recover, kind);
|
LSnapshot *snapshot = LSnapshot::New(gen, recoverInfo, kind);
|
||||||
if (!snapshot)
|
if (!snapshot)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
size_t i = 0;
|
size_t index = 0;
|
||||||
for (MResumePoint **it = recover->begin(), **end = recover->end(); it != end; ++it) {
|
LRecoverInfo::OperandIter it(recoverInfo->begin());
|
||||||
MResumePoint *mir = *it;
|
LRecoverInfo::OperandIter end(recoverInfo->end());
|
||||||
for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) {
|
for (; it != end; ++it) {
|
||||||
MDefinition *def = mir->getOperand(j);
|
MDefinition *def = *it;
|
||||||
|
|
||||||
|
if (def->isRecoveredOnBailout())
|
||||||
|
continue;
|
||||||
|
|
||||||
if (def->isBox())
|
if (def->isBox())
|
||||||
def = def->toBox()->getOperand(0);
|
def = def->toBox()->getOperand(0);
|
||||||
|
@ -153,7 +158,7 @@ LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKi
|
||||||
// code between an instruction and the LOsiPoint that follows it.
|
// code between an instruction and the LOsiPoint that follows it.
|
||||||
JS_ASSERT_IF(!def->isConstant(), !def->isEmittedAtUses());
|
JS_ASSERT_IF(!def->isConstant(), !def->isEmittedAtUses());
|
||||||
|
|
||||||
LAllocation *a = snapshot->getEntry(i);
|
LAllocation *a = snapshot->getEntry(index++);
|
||||||
|
|
||||||
if (def->isUnused()) {
|
if (def->isUnused()) {
|
||||||
*a = LConstantIndex::Bogus();
|
*a = LConstantIndex::Bogus();
|
||||||
|
@ -162,7 +167,6 @@ LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKi
|
||||||
|
|
||||||
*a = useKeepaliveOrConstant(def);
|
*a = useKeepaliveOrConstant(def);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1237,6 +1237,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void incrementInt32Value(const Address &addr) {
|
||||||
|
addPtr(Imm32(1), addr);
|
||||||
|
}
|
||||||
|
|
||||||
// If source is a double, load it into dest. If source is int32,
|
// If source is a double, load it into dest. If source is int32,
|
||||||
// convert it to double. Else, branch to failure.
|
// convert it to double. Else, branch to failure.
|
||||||
void ensureDouble(const ValueOperand &source, FloatRegister dest, Label *failure) {
|
void ensureDouble(const ValueOperand &source, FloatRegister dest, Label *failure) {
|
||||||
|
|
|
@ -1035,6 +1035,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||||
bind(&noOverflow);
|
bind(&noOverflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void incrementInt32Value(const Address &addr) {
|
||||||
|
addl(Imm32(1), payloadOf(addr));
|
||||||
|
}
|
||||||
|
|
||||||
// If source is a double, load it into dest. If source is int32,
|
// If source is a double, load it into dest. If source is int32,
|
||||||
// convert it to double. Else, branch to failure.
|
// convert it to double. Else, branch to failure.
|
||||||
|
|
|
@ -235,13 +235,10 @@ class AutoVectorRooter : protected AutoGCRooter
|
||||||
return vector.reserve(newLength);
|
return vector.reserve(newLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
T &operator[](size_t i) { return vector[i]; }
|
JS::MutableHandle<T> operator[](size_t i) {
|
||||||
const T &operator[](size_t i) const { return vector[i]; }
|
|
||||||
|
|
||||||
JS::MutableHandle<T> handleAt(size_t i) {
|
|
||||||
return JS::MutableHandle<T>::fromMarkedLocation(&vector[i]);
|
return JS::MutableHandle<T>::fromMarkedLocation(&vector[i]);
|
||||||
}
|
}
|
||||||
JS::Handle<T> handleAt(size_t i) const {
|
JS::Handle<T> operator[](size_t i) const {
|
||||||
return JS::Handle<T>::fromMarkedLocation(&vector[i]);
|
return JS::Handle<T>::fromMarkedLocation(&vector[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1744,14 +1744,14 @@ MergeSortByKey(K keys, size_t len, K scratch, C comparator, AutoValueVector *vec
|
||||||
do {
|
do {
|
||||||
size_t k = keys[j].elementIndex;
|
size_t k = keys[j].elementIndex;
|
||||||
keys[j].elementIndex = j;
|
keys[j].elementIndex = j;
|
||||||
(*vec)[j] = (*vec)[k];
|
(*vec)[j].set((*vec)[k]);
|
||||||
j = k;
|
j = k;
|
||||||
} while (j != i);
|
} while (j != i);
|
||||||
|
|
||||||
// We could assert the loop invariant that |i == keys[i].elementIndex|
|
// We could assert the loop invariant that |i == keys[i].elementIndex|
|
||||||
// here if we synced |keys[i].elementIndex|. But doing so would render
|
// here if we synced |keys[i].elementIndex|. But doing so would render
|
||||||
// the assertion vacuous, so don't bother, even in debug builds.
|
// the assertion vacuous, so don't bother, even in debug builds.
|
||||||
(*vec)[i] = tv;
|
(*vec)[i].set(tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1820,7 +1820,7 @@ SortNumerically(JSContext *cx, AutoValueVector *vec, size_t len, ComparatorMatch
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
double dv;
|
double dv;
|
||||||
if (!ToNumber(cx, vec->handleAt(i), &dv))
|
if (!ToNumber(cx, (*vec)[i], &dv))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
NumericElement el = { dv, i };
|
NumericElement el = { dv, i };
|
||||||
|
|
|
@ -2771,8 +2771,10 @@ BeginMarkPhase(JSRuntime *rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rt->gcShouldCleanUpEverything) {
|
if (!rt->gcShouldCleanUpEverything) {
|
||||||
|
#ifdef JS_ION
|
||||||
if (JSCompartment *comp = jit::TopmostJitActivationCompartment(rt))
|
if (JSCompartment *comp = jit::TopmostJitActivationCompartment(rt))
|
||||||
comp->zone()->setPreservingCode(true);
|
comp->zone()->setPreservingCode(true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -351,6 +351,26 @@ TypeSet::mightBeMIRType(jit::MIRType type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TypeSet::objectsAreSubset(TypeSet *other)
|
||||||
|
{
|
||||||
|
if (other->unknownObject())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (unknownObject())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < getObjectCount(); i++) {
|
||||||
|
TypeObjectKey *obj = getObject(i);
|
||||||
|
if (!obj)
|
||||||
|
continue;
|
||||||
|
if (!other->hasType(Type::ObjectType(obj)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TypeSet::isSubset(TypeSet *other)
|
TypeSet::isSubset(TypeSet *other)
|
||||||
{
|
{
|
||||||
|
|
|
@ -584,6 +584,12 @@ class TypeSet
|
||||||
*/
|
*/
|
||||||
bool isSubset(TypeSet *other);
|
bool isSubset(TypeSet *other);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get whether the objects in this TypeSet are a subset of the objects
|
||||||
|
* in other.
|
||||||
|
*/
|
||||||
|
bool objectsAreSubset(TypeSet *other);
|
||||||
|
|
||||||
/* Forward all types in this set to the specified constraint. */
|
/* Forward all types in this set to the specified constraint. */
|
||||||
bool addTypesToConstraint(JSContext *cx, TypeConstraint *constraint);
|
bool addTypesToConstraint(JSContext *cx, TypeConstraint *constraint);
|
||||||
|
|
||||||
|
|
|
@ -463,7 +463,7 @@ js::math_imul(JSContext *cx, unsigned argc, Value *vp)
|
||||||
|
|
||||||
// Implements Math.fround (20.2.2.16) up to step 3
|
// Implements Math.fround (20.2.2.16) up to step 3
|
||||||
bool
|
bool
|
||||||
js::RoundFloat32(JSContext *cx, Handle<Value> v, float *out)
|
js::RoundFloat32(JSContext *cx, HandleValue v, float *out)
|
||||||
{
|
{
|
||||||
double d;
|
double d;
|
||||||
bool success = ToNumber(cx, v, &d);
|
bool success = ToNumber(cx, v, &d);
|
||||||
|
@ -471,6 +471,17 @@ js::RoundFloat32(JSContext *cx, Handle<Value> v, float *out)
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::RoundFloat32(JSContext *cx, HandleValue arg, MutableHandleValue res)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
if (!RoundFloat32(cx, arg, &f))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
res.setDouble(static_cast<double>(f));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::math_fround(JSContext *cx, unsigned argc, Value *vp)
|
js::math_fround(JSContext *cx, unsigned argc, Value *vp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -110,7 +110,10 @@ extern bool
|
||||||
math_imul(JSContext *cx, unsigned argc, js::Value *vp);
|
math_imul(JSContext *cx, unsigned argc, js::Value *vp);
|
||||||
|
|
||||||
extern bool
|
extern bool
|
||||||
RoundFloat32(JSContext *cx, Handle<Value> v, float *out);
|
RoundFloat32(JSContext *cx, HandleValue v, float *out);
|
||||||
|
|
||||||
|
extern bool
|
||||||
|
RoundFloat32(JSContext *cx, HandleValue arg, MutableHandleValue res);
|
||||||
|
|
||||||
extern bool
|
extern bool
|
||||||
math_fround(JSContext *cx, unsigned argc, js::Value *vp);
|
math_fround(JSContext *cx, unsigned argc, js::Value *vp);
|
||||||
|
|
|
@ -1003,7 +1003,7 @@ js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
|
||||||
bool dummy;
|
bool dummy;
|
||||||
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
|
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
|
||||||
for (size_t i = 0, len = ids.length(); i < len; i++) {
|
for (size_t i = 0, len = ids.length(); i < len; i++) {
|
||||||
if (!DefinePropertyOnArray(cx, arr, ids.handleAt(i), descs[i], true, &dummy))
|
if (!DefinePropertyOnArray(cx, arr, ids[i], descs[i], true, &dummy))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1017,7 +1017,7 @@ js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
|
||||||
if (obj->is<ProxyObject>()) {
|
if (obj->is<ProxyObject>()) {
|
||||||
for (size_t i = 0, len = ids.length(); i < len; i++) {
|
for (size_t i = 0, len = ids.length(); i < len; i++) {
|
||||||
RootedValue pd(cx, descs[i].pd());
|
RootedValue pd(cx, descs[i].pd());
|
||||||
if (!Proxy::defineProperty(cx, obj, ids.handleAt(i), pd))
|
if (!Proxy::defineProperty(cx, obj, ids[i], pd))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1028,7 +1028,7 @@ js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
|
||||||
|
|
||||||
bool dummy;
|
bool dummy;
|
||||||
for (size_t i = 0, len = ids.length(); i < len; i++) {
|
for (size_t i = 0, len = ids.length(); i < len; i++) {
|
||||||
if (!DefinePropertyOnObject(cx, obj, ids.handleAt(i), descs[i], true, &dummy))
|
if (!DefinePropertyOnObject(cx, obj, ids[i], descs[i], true, &dummy))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1743,7 +1743,7 @@ JS_CopyPropertiesFrom(JSContext *cx, HandleObject target, HandleObject obj)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (size_t i = 0; i < props.length(); ++i) {
|
for (size_t i = 0; i < props.length(); ++i) {
|
||||||
if (!JS_CopyPropertyFrom(cx, props.handleAt(i), target, obj))
|
if (!JS_CopyPropertyFrom(cx, props[i], target, obj))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2013,7 +2013,7 @@ js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ASSERT(it.front().hasDefaultGetter());
|
JS_ASSERT(it.front().hasDefaultGetter());
|
||||||
ids[it.front().slot()] = it.front().propid();
|
ids[it.front().slot()].set(it.front().propid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -243,7 +243,7 @@ BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
|
||||||
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
|
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
|
||||||
return false;
|
return false;
|
||||||
if (desc.object() && desc.isEnumerable())
|
if (desc.object() && desc.isEnumerable())
|
||||||
props[i++] = id;
|
props[i++].set(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ASSERT(i <= props.length());
|
JS_ASSERT(i <= props.length());
|
||||||
|
@ -1582,7 +1582,7 @@ ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleVa
|
||||||
|
|
||||||
// step iii
|
// step iii
|
||||||
for (uint32_t j = 0; j < i; ++j) {
|
for (uint32_t j = 0; j < i; ++j) {
|
||||||
if (props[j] == id) {
|
if (props[j].get() == id) {
|
||||||
ReportInvalidTrapResult(cx, proxy, trapName);
|
ReportInvalidTrapResult(cx, proxy, trapName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1618,7 +1618,7 @@ ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleVa
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (size_t j = 0; j < props.length(); ++j) {
|
for (size_t j = 0; j < props.length(); ++j) {
|
||||||
if (props[j] == id) {
|
if (props[j].get() == id) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2453,7 +2453,7 @@ js::AppendUnique(JSContext *cx, AutoIdVector &base, AutoIdVector &others)
|
||||||
for (size_t i = 0; i < others.length(); ++i) {
|
for (size_t i = 0; i < others.length(); ++i) {
|
||||||
bool unique = true;
|
bool unique = true;
|
||||||
for (size_t j = 0; j < base.length(); ++j) {
|
for (size_t j = 0; j < base.length(); ++j) {
|
||||||
if (others[i] == base[j]) {
|
if (others[i].get() == base[j]) {
|
||||||
unique = false;
|
unique = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче