Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2014-04-29 18:23:29 +01:00
commit 5f8d0681d8
140 changed files with 3337 additions and 1402 deletions

View File

@ -31,6 +31,7 @@
gestures.forEach(AccessFuTest.addSequence);
AccessFuTest.addFunc(stopGestureTracker);
AccessFuTest.waitForExplicitFinish();
Logger.logLevel = Logger.DEBUG;
AccessFuTest.runTests();
});
}

View File

@ -156,11 +156,6 @@ pref("browser.search.suggest.enabled", true);
// tell the search service that we don't really expose the "current engine"
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
pref("browser.xul.error_pages.enabled", true);
@ -899,9 +894,9 @@ pref("osfile.reset_worker_delay", 5000);
pref("apz.asyncscroll.throttle", 40);
pref("apz.pan_repaint_interval", 16);
// Maximum fling velocity in inches/ms. Slower devices may need to reduce this
// to avoid checkerboarding. Note, float value must be set as a string.
pref("apz.max_velocity_inches_per_ms", "0.0375");
// APZ physics settings, tuned by UX designers
pref("apz.max_velocity_inches_per_ms", "0.07");
pref("apz.fling_friction", "0.003");
// Tweak default displayport values to reduce the risk of running out of
// memory when zooming in

View File

@ -5,3 +5,8 @@
#filter substitution
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");

Binary file not shown.

Binary file not shown.

View File

@ -212,3 +212,5 @@ http://example.cn:80 privileged
http://example.co.jp:80 privileged
http://example.fi:80 privileged
# Hosts for testing marketplace apps installations
https://marketplace.firefox.com:443 privileged

View File

@ -5964,18 +5964,6 @@ if test "$MOZ_GAMEPAD"; then
MOZ_GAMEPAD_BACKEND=cocoa
;;
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
;;
Linux)

View File

@ -3209,7 +3209,7 @@ nsObjectLoadingContent::LegacyCall(JSContext* aCx,
}
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);
return JS::UndefinedValue();
}

View File

@ -32,7 +32,7 @@ public:
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;
@ -40,11 +40,11 @@ public:
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;
@ -56,25 +56,25 @@ public:
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 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 QueueMetadata(int64_t aTime, 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_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 void NotifyWaitingForResourcesStatusChanged() MOZ_FINAL MOZ_OVERRIDE;
virtual void NotifyWaitingForResourcesStatusChanged() MOZ_OVERRIDE;
protected:
// This monitor object is not really used to synchronize access to anything.

View File

@ -164,6 +164,14 @@ VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
if (!chunk.IsNull()) {
gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize();
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,
intrinsicSize.width, intrinsicSize.height,
aTrackRate);

View File

@ -89,7 +89,9 @@ public:
return false;
}
MaybeSwitchVideoReaders(aTimeThreshold);
if (MaybeSwitchVideoReaders(aTimeThreshold)) {
GetVideoReader()->DecodeToTarget(aTimeThreshold);
}
bool rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
@ -129,7 +131,7 @@ public:
void CallDecoderInitialization();
private:
void MaybeSwitchVideoReaders(int64_t aTimeThreshold) {
bool MaybeSwitchVideoReaders(int64_t aTimeThreshold) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MOZ_ASSERT(mActiveVideoDecoder != -1);
@ -146,10 +148,11 @@ private:
MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
GetVideoReader()->SetActive();
GetVideoReader()->DecodeToTarget(aTimeThreshold);
break;
return true;
}
}
return false;
}
MediaDecoderReader* GetAudioReader() {

View File

@ -66,6 +66,12 @@ SubBufferDecoder::GetResource() const
return static_cast<SourceBufferResource*>(mResource.get());
}
void
SubBufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded)
{
return mParentDecoder->NotifyDecodedFrames(aParsed, aDecoded);
}
void
SubBufferDecoder::SetMediaDuration(int64_t aDuration)
{
@ -192,12 +198,8 @@ SourceBuffer::GetBuffered(ErrorResult& aRv)
return nullptr;
}
nsRefPtr<TimeRanges> ranges = new TimeRanges();
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
nsRefPtr<TimeRanges> r = new TimeRanges();
mDecoders[i]->GetBuffered(r);
if (r->Length() > 0) {
ranges->Add(r->GetStartTime(), r->GetEndTime());
}
if (mDecoder) {
mDecoder->GetBuffered(ranges);
}
ranges->Normalize();
return ranges.forget();
@ -264,10 +266,10 @@ SourceBuffer::Abort(ErrorResult& aRv)
mAppendWindowStart = 0;
mAppendWindowEnd = PositiveInfinity<double>();
MSE_DEBUG("%p Abort: Discarding decoders.", this);
if (mCurrentDecoder) {
mCurrentDecoder->GetResource()->Ended();
mCurrentDecoder = nullptr;
MSE_DEBUG("%p Abort: Discarding decoder.", this);
if (mDecoder) {
mDecoder->GetResource()->Ended();
mDecoder = nullptr;
}
}
@ -294,16 +296,15 @@ void
SourceBuffer::Detach()
{
Ended();
mDecoders.Clear();
mCurrentDecoder = nullptr;
mDecoder = nullptr;
mMediaSource = nullptr;
}
void
SourceBuffer::Ended()
{
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetResource()->Ended();
if (mDecoder) {
mDecoder->GetResource()->Ended();
}
}
@ -316,6 +317,7 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
, mTimestampOffset(0)
, mAppendMode(SourceBufferAppendMode::Segments)
, mUpdating(false)
, mDecoderInit(false)
{
MOZ_ASSERT(aMediaSource);
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.
mParser = new ContainerParser();
}
MSE_DEBUG("%p SourceBuffer: Creating initial decoder.", this);
InitNewDecoder();
}
already_AddRefed<SourceBuffer>
@ -335,8 +339,8 @@ SourceBuffer::Create(MediaSource* aMediaSource, const nsACString& aType)
SourceBuffer::~SourceBuffer()
{
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetResource()->Ended();
if (mDecoder) {
mDecoder->GetResource()->Ended();
}
}
@ -375,11 +379,7 @@ SourceBuffer::InitNewDecoder()
if (!decoder) {
return false;
}
mDecoders.AppendElement(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;
mDecoder = decoder;
return true;
}
@ -424,21 +424,24 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
// TODO: Test buffer full flag.
StartUpdating();
// TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
if (mParser->IsInitSegmentPresent(aData, aLength) || !mCurrentDecoder) {
MSE_DEBUG("%p AppendBuffer: New initialization segment, switching decoders.", this);
if (mCurrentDecoder) {
mCurrentDecoder->GetResource()->Ended();
}
if (!InitNewDecoder()) {
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
return;
if (!mDecoder || mParser->IsInitSegmentPresent(aData, aLength)) {
if (!mDecoder || mDecoderInit) {
MSE_DEBUG("%p AppendBuffer: New initialization segment, creating decoder.", this);
mDecoder->GetResource()->Ended();
if (!InitNewDecoder()) {
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
return;
}
}
MSE_DEBUG("%p AppendBuffer: Decoder marked as initialized.", this);
mDecoderInit = true;
}
// XXX: For future reference: NDA call must run on the main thread.
mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
aLength,
mCurrentDecoder->GetResource()->GetLength());
mCurrentDecoder->GetResource()->AppendData(aData, aLength);
mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
aLength,
mDecoder->GetResource()->GetLength());
mDecoder->GetResource()->AppendData(aData, aLength);
// Eviction uses a byte threshold. If the buffer is greater than the
// 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
// about.
const int evict_threshold = 1000000;
bool evicted = mCurrentDecoder->GetResource()->EvictData(evict_threshold);
bool evicted = mDecoder->GetResource()->EvictData(evict_threshold);
if (evicted) {
double start = 0.0;
double end = 0.0;
@ -479,14 +482,15 @@ SourceBuffer::GetBufferedStartEndTime(double* aStart, double* aEnd)
void
SourceBuffer::Evict(double aStart, double aEnd)
{
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
// Need to map time to byte offset then evict
int64_t end = mDecoders[i]->ConvertToByteOffset(aEnd);
if (end <= 0) {
NS_WARNING("SourceBuffer::Evict failed");
continue;
}
mDecoders[i]->GetResource()->EvictBefore(end);
if (!mDecoder) {
return;
}
// Need to map time to byte offset then evict
int64_t end = mDecoder->ConvertToByteOffset(aEnd);
if (end > 0) {
mDecoder->GetResource()->EvictBefore(end);
} else {
NS_WARNING("SourceBuffer::Evict failed");
}
}

View File

@ -140,10 +140,7 @@ private:
nsAutoPtr<ContainerParser> mParser;
// XXX: We only want to keep the current decoder alive, but need a way to
// query @buffered for everything this SourceBuffer is responsible for.
nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
nsRefPtr<SubBufferDecoder> mCurrentDecoder;
nsRefPtr<SubBufferDecoder> mDecoder;
double mAppendWindowStart;
double mAppendWindowEnd;
@ -152,6 +149,8 @@ private:
SourceBufferAppendMode mAppendMode;
bool mUpdating;
bool mDecoderInit;
};
} // namespace dom

View File

@ -40,6 +40,7 @@ public:
virtual bool OnStateMachineThread() const MOZ_OVERRIDE;
virtual bool OnDecodeThread() 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 UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;

View File

@ -678,6 +678,7 @@ MediaEngineWebRTCVideoSource::Shutdown()
void
MediaEngineWebRTCVideoSource::AllocImpl() {
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mCameraControl = ICameraControl::Create(mCaptureIndex);
if (mCameraControl) {
@ -818,6 +819,7 @@ MediaEngineWebRTCVideoSource::OnError(CameraErrorContext aContext, CameraError a
void
MediaEngineWebRTCVideoSource::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
{
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mLastCapture =
static_cast<nsIDOMFile*>(new nsDOMMemoryFile(static_cast<void*>(aData),
static_cast<uint64_t>(aLength),

View File

@ -2244,8 +2244,7 @@ this.DOMApplicationRegistry = {
queuedDownload: {},
queuedPackageDownload: {},
onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
aDontNeedNetwork) {
onInstallSuccessAck: function(aManifestURL, aDontNeedNetwork) {
// If we are offline, register to run when we'll be online.
if ((Services.io.offline) && !aDontNeedNetwork) {
let onlineWrapper = {
@ -2345,7 +2344,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
let dir = this._getAppDir(aId).path;
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.
@ -2418,7 +2417,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
});
}),
confirmInstall: function(aData, aProfileDir, aInstallSuccessCallback) {
confirmInstall: Task.async(function*(aData, aProfileDir, aInstallSuccessCallback) {
debug("confirmInstall");
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 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);
let manifest = new ManifestHelper(jsonManifest, app.origin);
@ -2477,42 +2476,15 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
aData.app[prop] = appObject[prop];
}
let dontNeedNetwork = false;
if (manifest.appcache_path) {
this.queuedDownload[app.manifestURL] = {
manifest: manifest,
app: appObject,
profileDir: aProfileDir
}
}
// 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) {
} else if (manifest.package_path) {
// If it is a local app then it must been installed from a local file
// instead of web.
#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 it's a local install, there's no content process so just
// ack the install.
this.onInstallSuccessAck(app.manifestURL, dontNeedNetwork);
}
},
}),
/**
* Install the package after successfully downloading it
@ -3104,7 +3104,10 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
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
// 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));
},
_openSignedPackage: function(aZipFile, aCertDb) {
_openSignedPackage: function(aInstallOrigin, aManifestURL, aZipFile, aCertDb) {
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(
TrustedRootCertificate.index, aZipFile,
root, aZipFile,
function(aRv, aZipReader) {
deferred.resolve([aRv, aZipReader]);
}

View File

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

View File

@ -0,0 +1 @@
Content-Type: application/x-web-app-manifest+json

Binary file not shown.

View File

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

View File

@ -0,0 +1 @@
Content-Type: application/x-web-app-manifest+json

View File

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

View File

@ -0,0 +1 @@
Content-Type: application/x-web-app-manifest+json

View File

@ -14,10 +14,14 @@ support-files =
signed_app_template.webapp
signed/*
test_packaged_app_common.js
marketplace/*
pkg_install_iframe.html
[test_app_update.html]
[test_bug_795164.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_update.html]
[test_receipt_operations.html]

View File

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

View File

@ -102,7 +102,7 @@ function readFile(path) {
function makeResource(templatePath, version, packagePath, packageSize,
appName, developerName, developerUrl) {
var res = readFile(templatePath, false).
var res = readFile(templatePath).
replace(/VERSIONTOKEN/g, version).
replace(/PACKAGEPATHTOKEN/g, packagePath).
replace(/PACKAGESIZETOKEN/g, packageSize).

View File

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

View File

@ -12,31 +12,18 @@ var PackagedTestHelper = (function PackagedTestHelper() {
var gAppName = "appname";
var gApp = null;
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) {
//dump("== PackageTestHelper debug == " + aMsg + "\n");
}
function next() {
if (timeoutID) {
clearTimeout(timeoutID);
}
index += 1;
if (index >= steps.length) {
ok(false, "Shouldn't get here!");
return;
}
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]();
} catch(ex) {
ok(false, "Caught exception", ex);
@ -48,10 +35,8 @@ var PackagedTestHelper = (function PackagedTestHelper() {
}
function finish() {
if (timeoutID) {
clearTimeout(timeoutID);
}
SpecialPowers.removePermission("webapps-manage", document);
SpecialPowers.removePermission("browser", document);
SimpleTest.finish();
}
@ -165,7 +150,7 @@ var PackagedTestHelper = (function PackagedTestHelper() {
is(aApp.manifest.size, aExpectedApp.size, "Check size");
}
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) {
is(aApp.manifestURL, aExpectedApp.manifestURL, "Check manifestURL");

View File

@ -476,10 +476,7 @@ private:
arguments.AppendElement(value);
}
console->ProfileMethod(cx, mAction, arguments, error);
if (error.Failed()) {
NS_WARNING("Failed to call call profile() method to the ConsoleAPI.");
}
console->ProfileMethod(cx, mAction, arguments);
}
private:
@ -662,23 +659,20 @@ Console::TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime)
}
void
Console::Profile(JSContext* aCx, const Sequence<JS::Value>& aData,
ErrorResult& aRv)
Console::Profile(JSContext* aCx, const Sequence<JS::Value>& aData)
{
ProfileMethod(aCx, NS_LITERAL_STRING("profile"), aData, aRv);
ProfileMethod(aCx, NS_LITERAL_STRING("profile"), aData);
}
void
Console::ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData,
ErrorResult& aRv)
Console::ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData)
{
ProfileMethod(aCx, NS_LITERAL_STRING("profileEnd"), aData, aRv);
ProfileMethod(aCx, NS_LITERAL_STRING("profileEnd"), aData);
}
void
Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
const Sequence<JS::Value>& aData,
ErrorResult& aRv)
const Sequence<JS::Value>& aData)
{
if (!NS_IsMainThread()) {
// Here we are in a worker thread.
@ -688,6 +682,8 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
return;
}
ClearException ce(aCx);
RootedDictionary<ConsoleProfileEvent> event(aCx);
event.mAction = aAction;
@ -700,15 +696,14 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
JS::Rooted<JS::Value> eventValue(aCx);
if (!event.ToObject(aCx, &eventValue)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
JS::Rooted<JSObject*> eventObj(aCx, &eventValue.toObject());
MOZ_ASSERT(eventObj);
if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue, JSPROP_ENUMERATE)) {
aRv.Throw(NS_ERROR_FAILURE);
if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue,
JSPROP_ENUMERATE)) {
return;
}
@ -717,7 +712,6 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
const nsIID& iid = NS_GET_IID(nsISupports);
if (NS_FAILED(xpc->WrapJS(aCx, eventObj, iid, getter_AddRefs(wrapper)))) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
@ -836,13 +830,14 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
ConsoleCallData* callData = new ConsoleCallData();
mQueuedCalls.insertBack(callData);
ClearException ce(aCx);
callData->Initialize(aCx, aMethodName, aMethodString, aData);
RAII raii(mQueuedCalls);
if (mWindow) {
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
if (!webNav) {
Throw(aCx, NS_ERROR_FAILURE);
return;
}
@ -857,7 +852,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, maxDepth);
if (!stack) {
Throw(aCx, NS_ERROR_FAILURE);
return;
}
@ -866,7 +860,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
uint32_t language;
nsresult rv = stack->GetLanguage(&language);
if (NS_FAILED(rv)) {
Throw(aCx, rv);
return;
}
@ -877,7 +870,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
callData->mTopStackFrame.ref(),
language);
if (NS_FAILED(rv)) {
Throw(aCx, rv);
return;
}
@ -887,7 +879,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
nsCOMPtr<nsIStackFrame> caller;
rv = stack->GetCaller(getter_AddRefs(caller));
if (NS_FAILED(rv)) {
Throw(aCx, rv);
return;
}
@ -902,7 +893,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
callData->mReifiedStack.construct();
nsresult rv = ReifyStack(stack, callData->mReifiedStack.ref());
if (NS_WARN_IF(NS_FAILED(rv))) {
Throw(aCx, rv);
return;
}
}
@ -915,7 +905,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
ErrorResult rv;
nsRefPtr<nsPerformance> performance = win->GetPerformance(rv);
if (rv.Failed()) {
Throw(aCx, rv.ErrorCode());
return;
}
@ -1111,7 +1100,6 @@ Console::ProcessCallData(ConsoleCallData* aData)
JS::Rooted<JS::Value> eventValue(cx);
if (!event.ToObject(cx, &eventValue)) {
Throw(cx, NS_ERROR_FAILURE);
return;
}

View File

@ -87,12 +87,10 @@ public:
TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime);
void
Profile(JSContext* aCx, const Sequence<JS::Value>& aData,
ErrorResult& aRv);
Profile(JSContext* aCx, const Sequence<JS::Value>& aData);
void
ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData,
ErrorResult& aRv);
ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData);
void
Assert(JSContext* aCx, bool aCondition, const Sequence<JS::Value>& aData);
@ -181,8 +179,7 @@ private:
void
ProfileMethod(JSContext* aCx, const nsAString& aAction,
const Sequence<JS::Value>& aData,
ErrorResult& aRv);
const Sequence<JS::Value>& aData);
JS::Value
IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,

View File

@ -1975,11 +1975,11 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
nsCOMPtr<nsIDOMWindow> currentWin(do_GetInterface(currentInner));
rv = WrapNative(cx, currentWin, &NS_GET_IID(nsIDOMWindow),
true, argv.handleAt(0));
true, argv[0]);
for (size_t i = 1; i < argc; ++i) {
argv[i] = args[i - 1];
if (!JS_WrapValue(cx, argv.handleAt(i)))
argv[i].set(args[i - 1]);
if (!JS_WrapValue(cx, argv[i]))
return NS_ERROR_FAILURE;
}

View File

@ -1002,7 +1002,7 @@ nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, n
// got the arguments, now attach them.
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;
}
}
@ -1066,7 +1066,7 @@ nsJSContext::ConvertSupportsTojsvals(nsISupports* aArgs,
if (argsArray) {
for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
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),
getter_AddRefs(arg));
if (!arg) {
@ -1099,7 +1099,7 @@ nsJSContext::ConvertSupportsTojsvals(nsISupports* aArgs,
} else {
nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
if (variant) {
rv = xpc->VariantToJS(cx, aScope, variant, aArgsOut.handleAt(0));
rv = xpc->VariantToJS(cx, aScope, variant, aArgsOut[0]);
} else {
NS_ERROR("Not an array, not an interface?");
rv = NS_ERROR_UNEXPECTED;

View File

@ -2054,7 +2054,7 @@ inline bool
AddStringToIDVector(JSContext* cx, JS::AutoIdVector& vector, const char* name)
{
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

View File

@ -12525,8 +12525,8 @@ class CallbackMember(CGNativeMember):
{
'result': result,
'successCode': "continue;\n" if arg.variadic else "break;\n",
'jsvalRef': "argv.handleAt(%s)" % jsvalIndex,
'jsvalHandle': "argv.handleAt(%s)" % jsvalIndex,
'jsvalRef': "argv[%s]" % jsvalIndex,
'jsvalHandle': "argv[%s]" % jsvalIndex,
# XXXbz we don't have anything better to use for 'obj',
# really... It's OK to use CallbackPreserveColor because
# CallSetup already handled the unmark-gray bits for us.
@ -12558,7 +12558,7 @@ class CallbackMember(CGNativeMember):
// This is our current trailing argument; reduce argc
--argc;
} else {
argv[${i}] = JS::UndefinedValue();
argv[${i}].setUndefined();
}
""",
argName=arg.identifier.name,
@ -12816,7 +12816,7 @@ class CallbackSetter(CallbackAccessor):
return fill(
"""
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);
return${errorReturn};
}

View File

@ -244,7 +244,7 @@ ToJSValue(JSContext* aCx,
return false;
}
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;
}
}

View File

@ -22,6 +22,19 @@ enum GamepadMappingType
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,
public nsWrapperCache
{

View File

@ -97,7 +97,9 @@ function createMediaElement(type, label) {
* The error callback if the stream fails to be retrieved
*/
function getUserMedia(constraints, onSuccess, onError) {
constraints["fake"] = FAKE_ENABLED;
if (!("fake" in constraints)) {
constraints["fake"] = FAKE_ENABLED;
}
info("Call getUserMedia for " + JSON.stringify(constraints));
navigator.mozGetUserMedia(constraints, onSuccess, onError);

View File

@ -131,7 +131,7 @@ MobileMessageManager::Send(JSContext* aCx, JS::Handle<JSObject*> aGlobal,
uint32_t aServiceId,
JS::Handle<JSString*> aNumber,
const nsAString& aMessage,
JS::Value* aRequest)
JS::MutableHandle<JS::Value> aRequest)
{
nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE);
@ -149,16 +149,14 @@ MobileMessageManager::Send(JSContext* aCx, JS::Handle<JSObject*> aGlobal,
NS_ENSURE_SUCCESS(rv, rv);
js::AssertSameCompartment(aCx, aGlobal);
JS::Rooted<JS::Value> rval(aCx);
rv = nsContentUtils::WrapNative(aCx,
static_cast<nsIDOMDOMRequest*>(request.get()),
&rval);
aRequest);
if (NS_FAILED(rv)) {
NS_ERROR("Failed to create the js value!");
return rv;
}
*aRequest = rval;
return NS_OK;
}
@ -208,7 +206,7 @@ MobileMessageManager::Send(JS::Handle<JS::Value> aNumber,
if (aNumber.isString()) {
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.
@ -236,7 +234,7 @@ MobileMessageManager::Send(JS::Handle<JS::Value> aNumber,
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);
}

View File

@ -38,7 +38,7 @@ private:
uint32_t aServiceId,
JS::Handle<JSString*> aNumber,
const nsAString& aMessage,
JS::Value* aRequest);
JS::MutableHandle<JS::Value> aRequest);
nsresult DispatchTrustedSmsEventToSelf(const char* aTopic,
const nsAString& aEventName,

View File

@ -272,6 +272,7 @@ let NotificationDB = {
var id = data.id;
if (!this.notifications[origin]) {
if (DEBUG) { debug("No notifications found for origin: " + origin); }
callback();
return;
}
@ -279,6 +280,7 @@ let NotificationDB = {
var oldNotification = this.notifications[origin][id];
if (!oldNotification) {
if (DEBUG) { debug("No notification found with id: " + id); }
callback();
return;
}

View File

@ -34,3 +34,4 @@ LOCAL_INCLUDES += [
'/dom/ipc',
]
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']

View File

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

View File

@ -0,0 +1,5 @@
[DEFAULT]
head =
tail =
[test_notificationdb.js]

View File

@ -20,10 +20,7 @@ interface Console {
void time(optional any time);
void timeEnd(optional any time);
[Throws]
void profile(any... data);
[Throws]
void profileEnd(any... data);
void assert(boolean condition, any... data);

View File

@ -728,7 +728,7 @@ APZCTreeManager::HandOffFling(AsyncPanZoomController* aPrev, ScreenPoint aVeloci
// otherwise built on touch-start and cleared on touch-end, and a fling
// happens after touch-end. Note that, unlike DispatchScroll() which is
// 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.
BuildOverscrollHandoffChain(aPrev);

View File

@ -66,7 +66,7 @@
#define APZC_LOG_FM(fm, 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 " \
"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__, \
fm.mPresShellId, fm.GetScrollId(), \
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.GetScrollOffset().x, fm.GetScrollOffset().y, \
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()); \
// Static helper functions
@ -163,6 +163,20 @@ typedef GeckoContentController::APZStateChange APZStateChange;
* generated displayport's size is beyond that of the scrollable rect on the
* 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"
* Amount of friction applied during flings.
*
@ -330,10 +344,40 @@ GetFrameTime() {
class FlingAnimation: public AsyncPanZoomAnimation {
public:
FlingAnimation(AsyncPanZoomController& aApzc)
FlingAnimation(AsyncPanZoomController& aApzc, bool aApplyAcceleration)
: AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
, 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|.
* This should be called whenever sampling the content transform for this
@ -344,6 +388,19 @@ public:
const TimeDuration& aDelta);
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;
};
@ -755,7 +812,7 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
mX.EndTouch();
mY.EndTouch();
SetState(FLING);
StartAnimation(new FlingAnimation(*this));
StartAnimation(new FlingAnimation(*this, true));
return nsEventStatus_eConsumeNoDefault;
case PINCHING:
@ -1183,7 +1240,7 @@ void AsyncPanZoomController::TakeOverFling(ScreenPoint aVelocity) {
mX.SetVelocity(mX.GetVelocity() + aVelocity.x);
mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
SetState(FLING);
StartAnimation(new FlingAnimation(*this));
StartAnimation(new FlingAnimation(*this, false));
}
void AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
@ -1294,20 +1351,20 @@ bool FlingAnimation::Sample(FrameMetrics& aFrameMetrics,
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
// the same velocity. For simplicity, the actual overscroll of the current
// sample is discarded rather than being handed off. The compositor should
// sample animations sufficiently frequently that this is not noticeable.
// 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.
APZCTreeManager* treeManagerLocal = mApzc.mTreeManager;
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
// we schedule HandleFlingOverscroll() to be called after mMonitor is
// we schedule HandOffFling() to be called after mMonitor is
// released.
mDeferredTasks.append(NewRunnableMethod(treeManagerLocal,
&APZCTreeManager::HandOffFling,
@ -1718,11 +1775,11 @@ gfx3DMatrix AsyncPanZoomController::GetTransformToLastDispatchedPaint() {
void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
ReentrantMonitorAutoEnter lock(mMonitor);
bool isDefault = mFrameMetrics.IsDefault();
mLastContentPaintMetrics = aLayerMetrics;
UpdateTransformScale();
bool isDefault = mFrameMetrics.IsDefault();
mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);

View File

@ -294,12 +294,6 @@ public:
*/
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.
* Internally performs a kind of hit testing based on the regions constructed
@ -501,14 +495,6 @@ protected:
*/
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
* layers code.
@ -777,7 +763,25 @@ private:
RefPtr<AsyncPanZoomAnimation> mAnimation;
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;
// The initial velocity of the most recent fling.
ScreenPoint mLastFlingVelocity;
// The time at which the most recent fling started.
TimeStamp mLastFlingTime;
/* ===================================================================

View File

@ -111,6 +111,9 @@ private:
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.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(Live, "apz.fling_repaint_interval", APZFlingRepaintInterval, int32_t, 75);
DECL_GFX_PREF(Once, "apz.fling_stopped_threshold", APZFlingStoppedThreshold, float, 0.01f);

File diff suppressed because it is too large Load Diff

View File

@ -501,7 +501,7 @@ JavaScriptChild::AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv
ContextOptionsRef(cx).setDontReportUncaught(true);
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)
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.
for (size_t i = 0; i < vals.length(); i++) {
JSVariant variant;
if (!toVariant(cx, vals.handleAt(i), &variant))
if (!toVariant(cx, vals[i], &variant))
return fail(cx, rs);
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++) {
nsString name;
if (!convertIdToGeckoString(cx, props.handleAt(i), &name))
if (!convertIdToGeckoString(cx, props[i], &name))
return fail(cx, rs);
names->AppendElement(name);

View File

@ -236,7 +236,7 @@ typedef enum JSWhyMagic
JS_BLOCK_NEEDS_CLONE, /* value of static block object slot */
JS_HASH_KEY_EMPTY, /* see class js::HashableValue */
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_GENERIC_MAGIC /* for local use */
} JSWhyMagic;

View File

@ -930,7 +930,7 @@ StructMetaTypeDescr::create(JSContext *cx,
// userFieldTypes[id] = typeObj
if (!JSObject::defineGeneric(cx, userFieldTypes, id,
fieldTypeObjs.handleAt(i), nullptr, nullptr,
fieldTypeObjs[i], nullptr, nullptr,
JSPROP_READONLY | JSPROP_PERMANENT))
return nullptr;

View File

@ -1163,27 +1163,27 @@ InitTypeClasses(JSContext* cx, HandleObject parent)
if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
sPointerFunction, nullptr, sPointerProps,
sPointerInstanceFunctions, sPointerInstanceProps,
protos.handleAt(SLOT_POINTERPROTO), protos.handleAt(SLOT_POINTERDATAPROTO)))
protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
return false;
if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
sArrayFunction, nullptr, sArrayProps,
sArrayInstanceFunctions, sArrayInstanceProps,
protos.handleAt(SLOT_ARRAYPROTO), protos.handleAt(SLOT_ARRAYDATAPROTO)))
protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
return false;
if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
sStructFunction, sStructFunctions, sStructProps,
sStructInstanceFunctions, nullptr,
protos.handleAt(SLOT_STRUCTPROTO), protos.handleAt(SLOT_STRUCTDATAPROTO)))
protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
return false;
if (!InitTypeConstructor(cx, parent, CTypeProto, protos.handleAt(SLOT_POINTERDATAPROTO),
if (!InitTypeConstructor(cx, parent, CTypeProto, protos[SLOT_POINTERDATAPROTO],
sFunctionFunction, nullptr, sFunctionProps, sFunctionInstanceFunctions, nullptr,
protos.handleAt(SLOT_FUNCTIONPROTO), protos.handleAt(SLOT_FUNCTIONDATAPROTO)))
protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
return false;
protos[SLOT_CDATAPROTO] = CDataProto;
protos[SLOT_CDATAPROTO].set(CDataProto);
// Create and attach the ctypes.{Int64,UInt64} constructors.
// Each of these has, respectively:
@ -1193,18 +1193,18 @@ InitTypeClasses(JSContext* cx, HandleObject parent)
// * 'prototype' property:
// * [[Class]] {"Int64Proto","UInt64Proto"}
// * 'constructor' property === ctypes.{Int64,UInt64}
protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass,
Int64::Construct, sInt64Functions, sInt64StaticFunctions);
protos[SLOT_INT64PROTO].set(InitInt64Class(cx, parent, &sInt64ProtoClass,
Int64::Construct, sInt64Functions, sInt64StaticFunctions));
if (!protos[SLOT_INT64PROTO])
return false;
protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass,
UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions);
protos[SLOT_UINT64PROTO].set(InitInt64Class(cx, parent, &sUInt64ProtoClass,
UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions));
if (!protos[SLOT_UINT64PROTO])
return false;
// Finally, store a pointer to the global ctypes 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,
// 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()));
if (!name)
return false;
fieldRoots[i] = JS::ObjectValue(*fieldType);
fieldRoots[i].setObject(*fieldType);
// Make sure each field name is unique
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()) {
const FieldInfoHash::Entry& entry = r.front();
// 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))
return nullptr;
}
@ -5620,7 +5620,7 @@ FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp)
// Pull out the argument types from the array, if any.
JS_ASSERT_IF(argTypes.length(), arrayObj);
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;
}
@ -5946,7 +5946,7 @@ FunctionType::ArgTypesGetter(JSContext* cx, JS::CallArgs args)
return false;
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);
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
// the existing buffers.
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;
}

View File

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

View File

@ -239,7 +239,7 @@ ValidateFFI(JSContext *cx, AsmJSModule::Global &global, HandleValue importVal,
if (!v.isObject() || !v.toObject().is<JSFunction>())
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;
}

View File

@ -47,7 +47,8 @@ SnapshotIterator::SnapshotIterator(const IonBailoutIterator &iter)
iter.ionScript()->recoversSize()),
fp_(iter.jsFrame()),
machine_(iter.machineState()),
ionScript_(iter.ionScript())
ionScript_(iter.ionScript()),
instructionResults_(nullptr)
{
}

View File

@ -558,9 +558,6 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
if (callerPC == nullptr) {
IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on top 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);
*builder.valuePointerAtStackOffset(argOffset) = arg;
} else {
startFrameFormals[i] = arg;
startFrameFormals[i].set(arg);
}
}
}
@ -758,7 +755,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
if (!savedCallerArgs.resize(inlined_args))
return false;
for (uint32_t i = 0; i < inlined_args; i++)
savedCallerArgs[i] = iter.read();
savedCallerArgs[i].set(iter.read());
if (IsSetPropPC(pc)) {
// We would love to just save all the arguments and leave them
@ -988,55 +985,24 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
blFrame->unsetPushedSPSFrame();
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
// for the outermost caller, and will have an uninitialized PC.
// This will be fixed up later in BailoutIonToBaseline.
//
// 2. If resuming into top-level code prologue, with ArgumentCheck,
// no SPS entry will have been pushed. Can be left alone.
//
// 3. If resuming into top-level code prologue, without ArgumentCheck,
// an SPS entry will have been pushed, and needs to be popped.
//
// 4. If resuming into top-level code main body, an SPS entry will
// have been pushed, and can be left alone.
//
// Only need to handle case 3 here.
if (!caller && bailoutKind != Bailout_ArgumentCheck) {
IonSpew(IonSpew_BaselineBailouts,
" Popping SPS entry for outermost frame");
cx->runtime()->spsProfiler.exit(script, fun);
}
// 1. If resuming into inline code, then the top SPS entry will be
// for the outermost caller, and will have an uninitialized PC.
// This will be fixed up later in BailoutIonToBaseline.
//
// 2. If resuming into top-level code prologue, with ArgumentCheck,
// no SPS entry will have been pushed. Can be left alone.
//
// 3. If resuming into top-level code prologue, without ArgumentCheck,
// an SPS entry will have been pushed, and needs to be popped.
//
// 4. If resuming into top-level code main body, an SPS entry will
// have been pushed, and can be left alone.
//
// Only need to handle case 3 here.
if (!caller && bailoutKind != Bailout_ArgumentCheck) {
IonSpew(IonSpew_BaselineBailouts,
" Popping SPS entry for outermost frame");
cx->runtime()->spsProfiler.exit(script, fun);
}
}
} else {
@ -1370,8 +1336,12 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
return BAILOUT_RETURN_FATAL_ERROR;
IonSpew(IonSpew_BaselineBailouts, " Incoming frame ptr = %p", builder.startFrame());
AutoValueVector instructionResults(cx);
SnapshotIterator snapIter(iter);
if (!snapIter.initIntructionResults(instructionResults))
return BAILOUT_RETURN_FATAL_ERROR;
RootedFunction callee(cx, iter.maybeCallee());
RootedScript scr(cx, iter.script());
if (callee) {
@ -1399,7 +1369,12 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
jsbytecode *topCallerPC = nullptr;
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) {
TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
@ -1453,10 +1428,9 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
}
IonSpew(IonSpew_BaselineBailouts, " Done restoring frames");
// If there were multiple inline frames unpacked, and inline frame profiling
// is off, then the current top SPS frame is for the outermost caller, and
// has an uninitialized PC. Initialize it now.
if (frameNo > 0 && !js_JitOptions.profileInlineFrames)
// If there were multiple inline frames unpacked, then the current top SPS frame
// is for the outermost caller, and has an uninitialized PC. Initialize it now.
if (frameNo > 0)
cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC);
BailoutKind bailoutKind = snapIter.bailoutKind();

View File

@ -2342,6 +2342,58 @@ BaselineCompiler::emit_JSOP_INITELEM_SETTER()
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
BaselineCompiler::emit_JSOP_GETLOCAL()
{

View File

@ -97,6 +97,8 @@ namespace jit {
_(JSOP_INITELEM) \
_(JSOP_INITELEM_GETTER) \
_(JSOP_INITELEM_SETTER) \
_(JSOP_INITELEM_INC) \
_(JSOP_SPREAD) \
_(JSOP_MUTATEPROTO) \
_(JSOP_INITPROP) \
_(JSOP_INITPROP_GETTER) \

View File

@ -111,7 +111,7 @@ BaselineFrame::copyRawFrameSlots(AutoValueVector *vec) const
mozilla::PodCopy(vec->begin(), argv(), nformals);
for (unsigned i = 0; i < nfixed; i++)
(*vec)[nformals + i] = *valueSlot(i);
(*vec)[nformals + i].set(*valueSlot(i));
return true;
}

View File

@ -5002,7 +5002,8 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_
JS_ASSERT(op == JSOP_SETELEM ||
op == JSOP_INITELEM ||
op == JSOP_INITELEM_ARRAY);
op == JSOP_INITELEM_ARRAY ||
op == JSOP_INITELEM_INC);
RootedObject obj(cx, ToObjectFromStack(cx, objv));
if (!obj)
@ -5025,6 +5026,9 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_
JS_ASSERT(uint32_t(index.toInt32()) == GET_UINT24(pc));
if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
return false;
} else if (op == JSOP_INITELEM_INC) {
if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
return false;
} else {
if (!SetObjectElement(cx, obj, index, rhs, script->strict(), script, pc))
return false;

View File

@ -1728,7 +1728,7 @@ CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
Register scratch = ToTempRegisterOrInvalid(lir->temp());
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()))
return false;
return true;
@ -1737,6 +1737,8 @@ CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
bool
CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir)
{
MOZ_ASSERT(lir->mir()->barrierKind() != BarrierKind::TypeTagOnly);
Register obj = ToRegister(lir->object());
Register scratch = ToTempRegisterOrInvalid(lir->temp());
@ -1754,7 +1756,7 @@ CodeGenerator::visitMonitorTypes(LMonitorTypes *lir)
Register scratch = ToTempUnboxRegister(lir->temp());
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()))
return false;
return true;
@ -2710,7 +2712,7 @@ CodeGenerator::generateArgumentsChecks(bool bailout)
// ... * sizeof(Value) - Scale by value size.
// ArgToStackOffset(...) - Compute displacement within arg vector.
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()) {
@ -3186,7 +3188,7 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
if (mir->resultTypeSet() && !mir->resultTypeSet()->unknown()) {
// We have a result TypeSet, assert this value is in it.
Label miss, ok;
masm.guardTypeSet(output, mir->resultTypeSet(), temp1, &miss);
masm.guardTypeSet(output, mir->resultTypeSet(), BarrierKind::TypeSet, temp1, &miss);
masm.jump(&ok);
masm.bind(&miss);
@ -8051,7 +8053,7 @@ CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
case MProfilerStackOp::Enter:
if (gen->options.spsSlowAssertionsEnabled()) {
if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
if (!inlinedFunction) {
saveLive(lir);
pushArg(ImmGCPtr(lir->script()));
if (!callVM(SPSEnterInfo, lir))
@ -8074,7 +8076,7 @@ CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
case MProfilerStackOp::Exit:
if (gen->options.spsSlowAssertionsEnabled()) {
if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
if (!inlinedFunction) {
saveLive(lir);
pushArg(ImmGCPtr(lir->script()));
// Once we've exited, then we shouldn't emit instrumentation for

View File

@ -171,6 +171,9 @@ jit::EliminateDeadCode(MIRGenerator *mir, MIRGraph &graph)
!inst->hasUses() && !inst->isGuard() &&
!inst->isControlInstruction()) {
inst = block->discardAt(inst);
} else if (!inst->hasLiveDefUses() && inst->canRecoverOnBailout()) {
inst->setRecoveredOnBailout();
inst++;
} else {
inst++;
}
@ -1382,11 +1385,6 @@ jit::AssertBasicGraphCoherency(MIRGraph &graph)
for (size_t i = 0; i < block->numPredecessors(); 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 (uint32_t i = 0, e = iter->numOperands(); i < e; i++) {
if (iter->getUseFor(i)->hasProducer())
@ -1395,11 +1393,16 @@ jit::AssertBasicGraphCoherency(MIRGraph &graph)
}
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) {
JS_ASSERT(phi->numOperands() == block->numPredecessors());
MOZ_ASSERT(!phi->isRecoveredOnBailout());
}
for (MDefinitionIterator iter(*block); iter; iter++) {
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 (MResumePoint *resume = iter->toInstruction()->resumePoint()) {
@ -1407,6 +1410,9 @@ jit::AssertBasicGraphCoherency(MIRGraph &graph)
JS_ASSERT(ins->block() == iter->block());
}
}
if (iter->isRecoveredOnBailout())
MOZ_ASSERT(!iter->hasLiveDefUses());
}
}

View File

@ -5049,7 +5049,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
return false;
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
@ -5378,7 +5378,7 @@ IonBuilder::makeCall(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsit
if (call->isCallDOMNative())
return pushDOMTypeBarrier(call, types, call->getSingleTarget());
return pushTypeBarrier(call, types, true);
return pushTypeBarrier(call, types, BarrierKind::TypeSet);
}
bool
@ -5426,7 +5426,7 @@ IonBuilder::jsop_eval(uint32_t argc)
if (!string->mightBeType(MIRType_String)) {
current->push(string);
types::TemporaryTypeSet *types = bytecodeTypes(pc);
return pushTypeBarrier(string, types, true);
return pushTypeBarrier(string, types, BarrierKind::TypeSet);
}
current->pushSlot(info().thisSlot());
@ -5465,7 +5465,7 @@ IonBuilder::jsop_eval(uint32_t argc)
current->push(ins);
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);
@ -6274,7 +6274,7 @@ IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, Pr
// instruction replaces the top of the stack.
// (5) Lastly, a type barrier instruction replaces the top of the stack.
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.
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
// to that point will explicitly monitor the new type.
if (!needsBarrier) {
if (kind == BarrierKind::NoBarrier) {
MDefinition *replace = ensureDefiniteType(def, observed->getKnownMIRType());
if (replace != def) {
current->pop();
@ -6301,7 +6301,7 @@ IonBuilder::pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed,
current->pop();
MInstruction *barrier = MTypeBarrier::New(alloc(), def, observed);
MInstruction *barrier = MTypeBarrier::New(alloc(), def, observed, kind);
current->add(barrier);
if (barrier->type() == MIRType_Undefined)
@ -6342,7 +6342,8 @@ IonBuilder::pushDOMTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *obser
JS_ASSERT(barrier);
}
return pushTypeBarrier(replace, observed, barrier);
return pushTypeBarrier(replace, observed,
barrier ? BarrierKind::TypeSet : BarrierKind::NoBarrier);
}
MDefinition *
@ -6449,13 +6450,13 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
}
types::TemporaryTypeSet *types = bytecodeTypes(pc);
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticType,
name, types, /* updateObserved = */ true);
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticType,
name, types, /* updateObserved = */ true);
JSObject *singleton = types->getSingleton();
MIRType knownType = types->getKnownMIRType();
if (!barrier) {
if (barrier == BarrierKind::NoBarrier) {
if (singleton) {
// Try to inline a known constant value.
if (testSingletonProperty(staticObject, name) == singleton)
@ -6470,7 +6471,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
MInstruction *obj = constant(ObjectValue(*staticObject));
MIRType rvalType = types->getKnownMIRType();
if (barrier)
if (barrier != BarrierKind::NoBarrier)
rvalType = MIRType_Value;
return loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
@ -6612,7 +6613,7 @@ IonBuilder::jsop_getname(PropertyName *name)
return false;
types::TemporaryTypeSet *types = bytecodeTypes(pc);
return pushTypeBarrier(ins, types, true);
return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
}
bool
@ -6631,7 +6632,7 @@ IonBuilder::jsop_intrinsic(PropertyName *name)
if (!resumeAfter(ins))
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.
@ -6696,7 +6697,7 @@ IonBuilder::jsop_getelem()
return false;
types::TemporaryTypeSet *types = bytecodeTypes(pc);
return pushTypeBarrier(ins, types, true);
return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
}
bool emitted = false;
@ -6738,7 +6739,7 @@ IonBuilder::jsop_getelem()
return false;
types::TemporaryTypeSet *types = bytecodeTypes(pc);
return pushTypeBarrier(ins, types, true);
return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
}
bool
@ -7004,7 +7005,7 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
{
derivedTypedObj->setResultTypeSet(observedTypes);
} else {
if (!pushTypeBarrier(derivedTypedObj, observedTypes, true))
if (!pushTypeBarrier(derivedTypedObj, observedTypes, BarrierKind::TypeSet))
return false;
}
@ -7186,7 +7187,7 @@ IonBuilder::getElemTryArguments(bool *emitted, MDefinition *obj, MDefinition *in
current->push(load);
types::TemporaryTypeSet *types = bytecodeTypes(pc);
if (!pushTypeBarrier(load, types, true))
if (!pushTypeBarrier(load, types, BarrierKind::TypeSet))
return false;
*emitted = true;
@ -7255,18 +7256,19 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
// Emit GetElementCache.
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
// can attach stubs for particular properties.
if (index->mightBeType(MIRType_String))
barrier = true;
barrier = BarrierKind::TypeSet;
// See note about always needing a barrier in jsop_getprop.
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->push(ins);
@ -7275,7 +7277,7 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
return false;
// Spice up type information.
if (index->type() == MIRType_Int32 && !barrier) {
if (index->type() == MIRType_Int32 && barrier == BarrierKind::NoBarrier) {
bool needHoleCheck = !ElementAccessIsPacked(constraints(), obj);
MIRType knownType = GetElemKnownType(needHoleCheck, types);
@ -7302,7 +7304,8 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
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);
// 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);
MIRType knownType = MIRType_Value;
if (!barrier)
if (barrier == BarrierKind::NoBarrier)
knownType = GetElemKnownType(needsHoleCheck, types);
// Ensure index is an integer.
@ -7341,7 +7344,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
ExecutionMode executionMode = info().executionMode();
bool loadDouble =
executionMode == SequentialExecution &&
!barrier &&
barrier == BarrierKind::NoBarrier &&
loopDepth_ &&
!readOutOfBounds &&
!needsHoleCheck &&
@ -7392,7 +7395,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
// NB: we have not added a MConvertElementsToDoubles MIR, so we
// cannot *assume* the result is a double.
if (executionMode == ParallelExecution &&
barrier &&
barrier != BarrierKind::NoBarrier &&
types->getKnownMIRType() == MIRType_Int32 &&
objTypes &&
objTypes->convertDoubleElements(constraints()) == types::TemporaryTypeSet::AlwaysConvertToDoubles)
@ -7402,7 +7405,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
if (!types)
return false;
barrier = false; // Don't need a barrier anymore
barrier = BarrierKind::NoBarrier; // Don't need a barrier anymore
}
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
// Uint32Array, we only check for int32: if allowDouble is false we
// will bailout when we read a double.
bool needsBarrier = true;
BarrierKind barrier = BarrierKind::TypeSet;
switch (arrayType) {
case ScalarTypeDescr::TYPE_INT8:
case ScalarTypeDescr::TYPE_UINT8:
@ -7571,12 +7574,12 @@ IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index,
case ScalarTypeDescr::TYPE_INT32:
case ScalarTypeDescr::TYPE_UINT32:
if (types->hasType(types::Type::Int32Type()))
needsBarrier = false;
barrier = BarrierKind::NoBarrier;
break;
case ScalarTypeDescr::TYPE_FLOAT32:
case ScalarTypeDescr::TYPE_FLOAT64:
if (allowDouble)
needsBarrier = false;
barrier = BarrierKind::NoBarrier;
break;
default:
MOZ_ASSUME_UNREACHABLE("Unknown typed array type");
@ -7590,7 +7593,7 @@ IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index,
current->add(load);
current->push(load);
return pushTypeBarrier(load, types, needsBarrier);
return pushTypeBarrier(load, types, barrier);
}
}
@ -8494,7 +8497,7 @@ IonBuilder::invalidatedIdempotentCache()
bool
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) {
MLoadFixedSlot *load = MLoadFixedSlot::New(alloc(), obj, slot);
@ -8518,7 +8521,7 @@ IonBuilder::loadSlot(MDefinition *obj, size_t slot, size_t nfixed, MIRType rvalT
bool
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);
}
@ -8568,8 +8571,8 @@ IonBuilder::jsop_getprop(PropertyName *name)
return emitted;
types::TemporaryTypeSet *types = bytecodeTypes(pc);
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
current->peek(-1), name, types);
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
current->peek(-1), name, types);
// Always use a call if we are performing analysis and
// not actually emitting code, to simplify later analysis. Also skip deeper
@ -8591,7 +8594,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
current->pop();
current->push(call);
return resumeAfter(call) && pushTypeBarrier(call, types, true);
return resumeAfter(call) && pushTypeBarrier(call, types, BarrierKind::TypeSet);
}
// Try to hardcode known constants.
@ -8626,7 +8629,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
if (!resumeAfter(call))
return false;
return pushTypeBarrier(call, types, true);
return pushTypeBarrier(call, types, BarrierKind::TypeSet);
}
bool
@ -8765,7 +8768,7 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
bool
IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
bool barrier, types::TemporaryTypeSet *types)
BarrierKind barrier, types::TemporaryTypeSet *types)
{
JS_ASSERT(*emitted == false);
types::HeapTypeSetKey property;
@ -8781,7 +8784,7 @@ IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
}
MLoadFixedSlot *fixed = MLoadFixedSlot::New(alloc(), useObj, property.maybeTypes()->definiteSlot());
if (!barrier)
if (barrier == BarrierKind::NoBarrier)
fixed->setResultType(types->getKnownMIRType());
current->add(fixed);
@ -8903,7 +8906,7 @@ CanInlinePropertyOpShapes(const BaselineInspector::ShapeVector &shapes)
bool
IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name,
bool barrier, types::TemporaryTypeSet *types)
BarrierKind barrier, types::TemporaryTypeSet *types)
{
JS_ASSERT(*emitted == false);
if (current->peek(-1)->type() != MIRType_Object)
@ -8917,7 +8920,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name,
return true;
MIRType rvalType = types->getKnownMIRType();
if (barrier || IsNullOrUndefined(rvalType))
if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
rvalType = MIRType_Value;
MDefinition *obj = current->pop();
@ -8964,7 +8967,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name,
bool
IonBuilder::getPropTryCache(bool *emitted, PropertyName *name,
bool barrier, types::TemporaryTypeSet *types)
BarrierKind barrier, types::TemporaryTypeSet *types)
{
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
// able to attach stubs for them.
if (inspector->hasSeenAccessedGetter(pc))
barrier = true;
barrier = BarrierKind::TypeSet;
if (needsToMonitorMissingProperties(types))
barrier = true;
barrier = BarrierKind::TypeSet;
// Caches can read values from prototypes, so update the barrier to
// reflect such possible values.
if (!barrier)
if (barrier == BarrierKind::NoBarrier)
barrier = PropertyReadOnPrototypeNeedsTypeBarrier(constraints(), obj, name, types);
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.
//
@ -9019,7 +9023,7 @@ IonBuilder::getPropTryCache(bool *emitted, PropertyName *name,
return false;
MIRType rvalType = types->getKnownMIRType();
if (barrier || IsNullOrUndefined(rvalType))
if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
rvalType = MIRType_Value;
load->setResultType(rvalType);
@ -9863,7 +9867,7 @@ IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc)
current->push(load);
types::TemporaryTypeSet *types = bytecodeTypes(pc);
return pushTypeBarrier(load, types, true);
return pushTypeBarrier(load, types, BarrierKind::TypeSet);
}
bool

View File

@ -343,7 +343,7 @@ class IonBuilder : public MIRGenerator
// Add a guard which ensure that the set of type which goes through this
// 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
// 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 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 barrier, types::TemporaryTypeSet *types);
BarrierKind barrier, types::TemporaryTypeSet *types);
bool storeSlot(MDefinition *obj, size_t slot, size_t nfixed,
MDefinition *value, bool needsBarrier,
MIRType slotType = MIRType_None);
@ -394,11 +394,11 @@ class IonBuilder : public MIRGenerator
bool getPropTryConstant(bool *emitted, PropertyName *name,
types::TemporaryTypeSet *types);
bool getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
bool barrier, types::TemporaryTypeSet *types);
BarrierKind barrier, types::TemporaryTypeSet *types);
bool getPropTryCommonGetter(bool *emitted, PropertyName *name,
types::TemporaryTypeSet *types);
bool getPropTryInlineAccess(bool *emitted, PropertyName *name,
bool barrier, types::TemporaryTypeSet *types);
BarrierKind barrier, types::TemporaryTypeSet *types);
bool getPropTryTypedObject(bool *emitted, PropertyName *name,
types::TemporaryTypeSet *resultTypes);
bool getPropTryScalarPropOfTypedObject(bool *emitted,
@ -411,7 +411,7 @@ class IonBuilder : public MIRGenerator
size_t fieldIndex,
types::TemporaryTypeSet *resultTypes);
bool getPropTryCache(bool *emitted, PropertyName *name,
bool barrier, types::TemporaryTypeSet *types);
BarrierKind barrier, types::TemporaryTypeSet *types);
bool needsToMonitorMissingProperties(types::TemporaryTypeSet *types);
// jsop_setprop() helpers.

View File

@ -1969,7 +1969,7 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
Register scratchReg = object;
masm.push(scratchReg);
masm.guardTypeSet(valReg, propTypes, scratchReg, &barrierFailure);
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &barrierFailure);
masm.pop(object);
}
}
@ -2521,7 +2521,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
JS_ASSERT(!propTypes->unknown());
Register scratchReg = object;
masm.guardTypeSet(valReg, propTypes, scratchReg, &failuresPopObject);
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &failuresPopObject);
masm.loadPtr(Address(StackPointer, 0), object);
}

View File

@ -631,9 +631,8 @@ HandleException(ResumeFromException *rfe)
if (invalidated)
popSPSFrame = ionScript->hasSPSInstrumentation();
// If inline-frames are not profiled, then don't pop an SPS frame
// for them.
if (frames.more() && !js_JitOptions.profileInlineFrames)
// Don't pop an SPS frame for inlined frames, since they are not instrumented.
if (frames.more())
popSPSFrame = false;
// When profiling, each frame popped needs a notification that
@ -1339,7 +1338,8 @@ SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshot
ionScript->recoversSize()),
fp_(fp),
machine_(machine),
ionScript_(ionScript)
ionScript_(ionScript),
instructionResults_(nullptr)
{
JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize());
}
@ -1354,7 +1354,8 @@ SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter)
iter.ionScript()->recoversSize()),
fp_(iter.jsFrame()),
machine_(iter.machineState()),
ionScript_(iter.ionScript())
ionScript_(iter.ionScript()),
instructionResults_(nullptr)
{
}
@ -1362,7 +1363,8 @@ SnapshotIterator::SnapshotIterator()
: snapshot_(nullptr, 0, 0, 0),
recover_(snapshot_, nullptr, 0),
fp_(nullptr),
ionScript_(nullptr)
ionScript_(nullptr),
instructionResults_(nullptr)
{
}
@ -1427,6 +1429,9 @@ SnapshotIterator::allocationReadable(const RValueAllocation &alloc)
return hasStack(alloc.stackOffset());
#endif
case RValueAllocation::RECOVER_INSTRUCTION:
return hasInstructionResult(alloc.index());
default:
return true;
}
@ -1532,6 +1537,9 @@ SnapshotIterator::allocationValue(const RValueAllocation &alloc)
}
#endif
case RValueAllocation::RECOVER_INSTRUCTION:
return fromInstructionResult(alloc.index());
default:
MOZ_ASSUME_UNREACHABLE("huh?");
}
@ -1546,7 +1554,7 @@ SnapshotIterator::resumePoint() const
uint32_t
SnapshotIterator::numAllocations() const
{
return resumePoint()->numOperands();
return instruction()->numOperands();
}
uint32_t
@ -1565,6 +1573,43 @@ SnapshotIterator::skipInstruction()
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
SnapshotIterator::nextFrame()
{

View File

@ -69,9 +69,10 @@ class TypeWrapper {
} /* anonymous namespace */
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)
{
JS_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
JS_ASSERT(!types->unknown());
Label matched;
@ -126,8 +127,10 @@ MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types,
// Test specific objects.
JS_ASSERT(scratch != InvalidReg);
branchTestObject(NotEqual, tag, miss);
Register obj = extractObject(address, scratch);
guardObjectType(obj, types, scratch, miss);
if (kind != BarrierKind::TypeTagOnly) {
Register obj = extractObject(address, scratch);
guardObjectType(obj, types, scratch, miss);
}
bind(&matched);
}
@ -203,30 +206,30 @@ MacroAssembler::guardType(const Source &address, types::Type type,
Register scratch, Label *miss)
{
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,
Register scratch, Label *miss);
BarrierKind kind, Register scratch, Label *miss);
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,
Register scratch, Label *miss);
BarrierKind kind, Register scratch, Label *miss);
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 &reg, 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,
Register scratch, Label *miss);
BarrierKind kind, Register scratch, Label *miss);
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,
Register scratch, Label *miss);
BarrierKind kind, Register scratch, Label *miss);
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,
Register scratch, Label *miss);

View File

@ -300,7 +300,7 @@ class MacroAssembler : public MacroAssemblerSpecific
// Emits a test of a value against all types in a TypeSet. A scratch
// register is required.
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>
void guardObjectType(Register obj, const TypeSet *types, Register scratch, Label *miss);
template <typename Source>

View File

@ -7,6 +7,8 @@
#ifndef jit_IonTypes_h
#define jit_IonTypes_h
#include "mozilla/TypedEnum.h"
#include "jstypes.h"
#include "js/Value.h"
@ -324,6 +326,19 @@ enum ABIFunctionType
(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 js

View File

@ -258,6 +258,7 @@ class SnapshotIterator
IonJSFrameLayout *fp_;
MachineState machine_;
IonScript *ionScript_;
AutoValueVector *instructionResults_;
private:
// Read a spilled register from the machine state.
@ -281,6 +282,11 @@ class SnapshotIterator
}
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);
bool allocationReadable(const RValueAllocation &a);
void warnUnreadableAllocation();
@ -338,6 +344,14 @@ class SnapshotIterator
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:
// Handle iterating over frames of the snapshots.
void nextFrame();

View File

@ -110,9 +110,6 @@ JitOptions::JitOptions()
// How many uses of a parallel kernel before we attempt compilation.
usesBeforeCompilePar = 1;
// Whether to profile inlined functions in Ion or not.
profileInlineFrames = false;
}
bool

View File

@ -69,7 +69,6 @@ struct JitOptions
uint32_t osrPcMismatchesBeforeRecompile;
uint32_t smallFunctionMaxBytecodeLength_;
uint32_t usesBeforeCompilePar;
bool profileInlineFrames;
JitOptions();
bool isSmallFunction(JSScript *script) const;

View File

@ -111,11 +111,16 @@ LBlock::getExitMoveGroup(TempAllocator &alloc)
}
static size_t
TotalOperandCount(MResumePoint *mir)
TotalOperandCount(LRecoverInfo *recoverInfo)
{
size_t accum = mir->numOperands();
while ((mir = mir->caller()))
accum += mir->numOperands();
LRecoverInfo::OperandIter it(recoverInfo->begin());
LRecoverInfo::OperandIter end(recoverInfo->end());
size_t accum = 0;
for (; it != end; ++it) {
if (!it->isRecoveredOnBailout())
accum++;
}
return accum;
}
@ -137,28 +142,71 @@ LRecoverInfo::New(MIRGenerator *gen, MResumePoint *mir)
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
LRecoverInfo::init(MResumePoint *rp)
{
MResumePoint *it = rp;
// 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
// current frame, are located before the current frame. The inner-most
// resume point should be the last element in the list.
do {
if (!instructions_.append(it))
return false;
it = it->caller();
} while (it);
if (!appendResumePoint(rp))
return false;
// Remove temporary flags from all definitions.
for (MNode **it = begin(); it != end(); it++) {
if (!(*it)->isDefinition())
continue;
(*it)->toDefinition()->setNotInWorklist();
}
Reverse(instructions_.begin(), instructions_.end());
MOZ_ASSERT(mir() == rp);
return true;
}
LSnapshot::LSnapshot(LRecoverInfo *recoverInfo, BailoutKind kind)
: numSlots_(TotalOperandCount(recoverInfo->mir()) * BOX_PIECES),
: numSlots_(TotalOperandCount(recoverInfo) * BOX_PIECES),
slots_(nullptr),
recoverInfo_(recoverInfo),
snapshotOffset_(INVALID_SNAPSHOT_OFFSET),
@ -368,8 +416,6 @@ LInstruction::dump(FILE *fp)
fprintf(fp, "} <- ");
printName(fp);
printInfo(fp);
if (numTemps()) {
@ -381,13 +427,13 @@ LInstruction::dump(FILE *fp)
}
fprintf(fp, ")");
}
fprintf(fp, "\n");
}
void
LInstruction::dump()
{
return dump(stderr);
dump(stderr);
fprintf(stderr, "\n");
}
void

View File

@ -879,7 +879,7 @@ class LCallInstructionHelper : public LInstructionHelper<Defs, Operands, Temps>
class LRecoverInfo : public TempObject
{
public:
typedef Vector<MResumePoint *, 2, IonAllocPolicy> Instructions;
typedef Vector<MNode *, 2, IonAllocPolicy> Instructions;
private:
// List of instructions needed to recover the stack frames.
@ -892,12 +892,17 @@ class LRecoverInfo : public TempObject
LRecoverInfo(TempAllocator &alloc);
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:
static LRecoverInfo *New(MIRGenerator *gen, MResumePoint *mir);
// Resume point of the inner most function.
MResumePoint *mir() const {
return instructions_.back();
return instructions_.back()->toResumePoint();
}
RecoverOffset recoverOffset() const {
return recoverOffset_;
@ -907,12 +912,47 @@ class LRecoverInfo : public TempObject
recoverOffset_ = offset;
}
MResumePoint **begin() {
MNode **begin() {
return instructions_.begin();
}
MResumePoint **end() {
MNode **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,

View File

@ -2303,7 +2303,8 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
}
// 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();
LTypeBarrierO *barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
@ -3597,6 +3598,9 @@ SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint
bool
LIRGenerator::visitInstruction(MInstruction *ins)
{
if (ins->isRecoveredOnBailout())
return true;
if (!gen->ensureBallast())
return false;
if (!ins->accept(this))

View File

@ -385,9 +385,9 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
callInfo.thisArg(), nullptr, returnTypes);
if (barrier)
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
callInfo.thisArg(), nullptr, returnTypes);
if (barrier != BarrierKind::NoBarrier)
returnType = MIRType_Value;
MArrayPopShift *ins = MArrayPopShift::New(alloc(), callInfo.thisArg(), mode,
@ -1231,7 +1231,7 @@ IonBuilder::inlineRegExpExec(CallInfo &callInfo)
if (!resumeAfter(exec))
return InliningStatus_Error;
if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), true))
if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), BarrierKind::TypeSet))
return InliningStatus_Error;
return InliningStatus_Inlined;
@ -1782,7 +1782,7 @@ IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo)
current->push(load);
// 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_Inlined;

View File

@ -359,6 +359,20 @@ MDefinition::hasDefUses() const
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
MDefinition::removeUse(MUseIterator use)
{
@ -3089,7 +3103,7 @@ jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinit
return elementType;
}
static bool
static BarrierKind
PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::TypeSet *observed)
@ -3105,13 +3119,22 @@ PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints,
if (object->unknownProperties() || observed->empty() ||
object->clasp()->isProxy())
{
return true;
return BarrierKind::TypeSet;
}
jsid id = name ? NameToId(name) : JSID_VOID;
types::HeapTypeSetKey property = object->property(id);
if (property.maybeTypes() && !TypeSetIncludes(observed, MIRType_Value, property.maybeTypes()))
return true;
if (property.maybeTypes()) {
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
// initial 'undefined' value for properties, in particular global
@ -3121,15 +3144,15 @@ PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints,
if (name && types::CanHaveEmptyPropertyTypesForOwnProperty(obj) &&
(!property.maybeTypes() || property.maybeTypes()->empty()))
{
return true;
return BarrierKind::TypeSet;
}
}
property.freeze(constraints);
return false;
return BarrierKind::NoBarrier;
}
bool
BarrierKind
jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
@ -3177,45 +3200,55 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
return PropertyReadNeedsTypeBarrier(constraints, object, name, observed);
}
bool
BarrierKind
jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed)
{
if (observed->unknown())
return false;
return BarrierKind::NoBarrier;
types::TypeSet *types = obj->resultTypeSet();
if (!types || types->unknownObject())
return true;
return BarrierKind::TypeSet;
BarrierKind res = BarrierKind::NoBarrier;
bool updateObserved = types->getObjectCount() == 1;
for (size_t i = 0; i < types->getObjectCount(); i++) {
types::TypeObjectKey *object = types->getObject(i);
if (object) {
if (PropertyReadNeedsTypeBarrier(propertycx, constraints, object, name,
observed, updateObserved))
{
return true;
BarrierKind kind = PropertyReadNeedsTypeBarrier(propertycx, constraints, object, name,
observed, updateObserved);
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
BarrierKind
jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed)
{
if (observed->unknown())
return false;
return BarrierKind::NoBarrier;
types::TypeSet *types = obj->resultTypeSet();
if (!types || types->unknownObject())
return true;
return BarrierKind::TypeSet;
BarrierKind res = BarrierKind::NoBarrier;
for (size_t i = 0; i < types->getObjectCount(); i++) {
types::TypeObjectKey *object = types->getObject(i);
@ -3223,16 +3256,24 @@ jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *cons
continue;
while (true) {
if (!object->hasTenuredProto())
return true;
return BarrierKind::TypeSet;
if (!object->proto().isObject())
break;
object = types::TypeObjectKey::get(object->proto().toObject());
if (PropertyReadNeedsTypeBarrier(constraints, object, name, observed))
return true;
BarrierKind kind = PropertyReadNeedsTypeBarrier(constraints, object, name, observed);
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
@ -3382,7 +3423,13 @@ TryAddTypeBarrierForWrite(TempAllocator &alloc, types::CompilerConstraintList *c
if (!types)
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);
return true;
}

View File

@ -89,7 +89,14 @@ MIRType MIRTypeFromValue(const js::Value &vp)
* Truncate Doubles. So every time removeUse is called, UseRemoved needs
* 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 MInstruction;
@ -208,6 +215,8 @@ class MNode : public TempObject
inline MDefinition *toDefinition();
inline MResumePoint *toResumePoint();
virtual bool writeRecoverData(CompactBufferWriter &writer) const;
protected:
// Sets an unset operand, updating use information.
virtual void setOperand(size_t index, MDefinition *operand) = 0;
@ -531,6 +540,10 @@ class MDefinition : public MNode
// (only counting MDefinitions, ignoring MResumePoints)
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 {
return !uses_.empty();
}
@ -606,6 +619,10 @@ class MDefinition : public MNode
JS_ASSERT(getAliasSet().flags() & store->getAliasSet().flags());
return true;
}
virtual bool canRecoverOnBailout() const {
return false;
}
};
// 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);
bool truncate();
bool isOperandTruncated(size_t index) const;
bool writeRecoverData(CompactBufferWriter &writer) const;
bool canRecoverOnBailout() const {
return specialization_ < MIRType_Object;
}
};
class MSub : public MBinaryArithInstruction
@ -9070,10 +9092,15 @@ class MTypeBarrier
: public MUnaryInstruction,
public TypeBarrierPolicy
{
MTypeBarrier(MDefinition *def, types::TemporaryTypeSet *types)
: MUnaryInstruction(def)
BarrierKind barrierKind_;
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());
setResultTypeSet(types);
@ -9084,8 +9111,9 @@ class MTypeBarrier
public:
INSTRUCTION_HEADER(TypeBarrier)
static MTypeBarrier *New(TempAllocator &alloc, MDefinition *def, types::TemporaryTypeSet *types) {
return new(alloc) MTypeBarrier(def, types);
static MTypeBarrier *New(TempAllocator &alloc, MDefinition *def, types::TemporaryTypeSet *types,
BarrierKind kind = BarrierKind::TypeSet) {
return new(alloc) MTypeBarrier(def, types, kind);
}
void printOpcode(FILE *fp) const;
@ -9103,6 +9131,9 @@ class MTypeBarrier
virtual bool neverHoist() const {
return resultTypeSet()->empty();
}
BarrierKind barrierKind() const {
return barrierKind_;
}
bool alwaysBails() const {
// If mirtype of input doesn't agree with mirtype of barrier,
@ -9122,20 +9153,25 @@ class MTypeBarrier
class MMonitorTypes : public MUnaryInstruction, public BoxInputsPolicy
{
const types::TemporaryTypeSet *typeSet_;
BarrierKind barrierKind_;
MMonitorTypes(MDefinition *def, const types::TemporaryTypeSet *types)
MMonitorTypes(MDefinition *def, const types::TemporaryTypeSet *types, BarrierKind kind)
: MUnaryInstruction(def),
typeSet_(types)
typeSet_(types),
barrierKind_(kind)
{
MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
setGuard();
JS_ASSERT(!types->unknown());
MOZ_ASSERT(!types->unknown());
}
public:
INSTRUCTION_HEADER(MonitorTypes)
static MMonitorTypes *New(TempAllocator &alloc, MDefinition *def, const types::TemporaryTypeSet *types) {
return new(alloc) MMonitorTypes(def, types);
static MMonitorTypes *New(TempAllocator &alloc, MDefinition *def, const types::TemporaryTypeSet *types,
BarrierKind kind) {
return new(alloc) MMonitorTypes(def, types, kind);
}
TypePolicy *typePolicy() {
@ -9145,6 +9181,10 @@ class MMonitorTypes : public MUnaryInstruction, public BoxInputsPolicy
const types::TemporaryTypeSet *typeSet() const {
return typeSet_;
}
BarrierKind barrierKind() const {
return barrierKind_;
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
@ -10094,17 +10134,17 @@ bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefiniti
bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
MDefinition *obj);
MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj);
bool PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::TemporaryTypeSet *observed, bool updateObserved);
bool PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed);
bool PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed);
BarrierKind PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::TemporaryTypeSet *observed, bool updateObserved);
BarrierKind PropertyReadNeedsTypeBarrier(JSContext *propertycx,
types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed);
BarrierKind PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed);
bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name);
void AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,

View File

@ -6,21 +6,42 @@
#include "jit/Recover.h"
#include "jscntxt.h"
#include "jsmath.h"
#include "jit/IonSpewer.h"
#include "jit/JitFrameIterator.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
#include "vm/Interpreter.h"
using namespace js;
using namespace js::jit;
bool
MNode::writeRecoverData(CompactBufferWriter &writer) const
{
MOZ_ASSUME_UNREACHABLE("This instruction is not serializable");
return false;
}
void
RInstruction::readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw)
{
uint32_t op = reader.readUnsigned();
switch (Opcode(op)) {
case Recover_ResumePoint:
new (raw->addr()) RResumePoint(reader);
# define MATCH_OPCODES_(op) \
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;
RECOVER_OPCODE_LIST(MATCH_OPCODES_)
# undef DEFINE_OPCODES_
case Recover_Invalid:
default:
MOZ_ASSUME_UNREACHABLE("Bad decoding of the previous instruction?");
break;
@ -101,10 +122,48 @@ MResumePoint::writeRecoverData(CompactBufferWriter &writer) const
RResumePoint::RResumePoint(CompactBufferReader &reader)
{
static_assert(sizeof(*this) <= sizeof(RInstructionStorage),
"Storage space is too small to decode this recover instruction.");
pcOffset_ = reader.readUnsigned();
numOperands_ = reader.readUnsigned();
IonSpew(IonSpew_Snapshots, "Read RResumePoint (pc offset %u, nslots %u)",
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;
}

View File

@ -11,44 +11,71 @@
#include "jit/Snapshots.h"
class JSContext;
namespace js {
namespace jit {
#define RECOVER_OPCODE_LIST(_) \
_(ResumePoint) \
_(Add)
class RResumePoint;
class SnapshotIterator;
class RInstruction
{
public:
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;
// 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 {
return opcode() == Recover_ResumePoint;
}
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;
// 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);
};
#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
{
private:
uint32_t pcOffset_; // Offset from script->code.
uint32_t numOperands_; // Number of slots.
friend class RInstruction;
RResumePoint(CompactBufferReader &reader);
public:
virtual Opcode opcode() const {
return Recover_ResumePoint;
}
RINSTRUCTION_HEADER_(ResumePoint)
uint32_t pcOffset() const {
return pcOffset_;
@ -56,8 +83,26 @@ class RResumePoint MOZ_FINAL : public RInstruction
virtual uint32_t numOperands() const {
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 *
RInstruction::toResumePoint() const
{

View File

@ -76,6 +76,9 @@ using namespace js::jit;
// first register/stack-offset correspond to the holder of the type,
// 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]:
// Value with statically known type, which payload is stored in a
// register.
@ -219,6 +222,15 @@ RValueAllocation::layoutFromMode(Mode mode)
return layout;
}
#endif
case RECOVER_INSTRUCTION: {
static const RValueAllocation::Layout layout = {
PAYLOAD_INDEX,
PAYLOAD_NONE,
"instruction"
};
return layout;
}
default: {
static const RValueAllocation::Layout regLayout = {
PAYLOAD_PACKED_TAG,
@ -662,20 +674,20 @@ SnapshotWriter::endSnapshot()
}
RecoverOffset
RecoverWriter::startRecover(uint32_t frameCount, bool resumeAfter)
RecoverWriter::startRecover(uint32_t instructionCount, bool resumeAfter)
{
MOZ_ASSERT(frameCount);
nframes_ = frameCount;
framesWritten_ = 0;
MOZ_ASSERT(instructionCount);
instructionCount_ = instructionCount;
instructionsWritten_ = 0;
IonSpew(IonSpew_Snapshots, "starting recover with frameCount %u",
frameCount);
IonSpew(IonSpew_Snapshots, "starting recover with %u instruction(s)",
instructionCount);
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(resumeAfter) << RECOVER_RESUMEAFTER_SHIFT) |
(frameCount << RECOVER_RINSCOUNT_SHIFT);
(instructionCount << RECOVER_RINSCOUNT_SHIFT);
RecoverOffset recoverOffset = writer_.length();
writer_.writeUnsigned(bits);
@ -683,16 +695,16 @@ RecoverWriter::startRecover(uint32_t frameCount, bool resumeAfter)
}
bool
RecoverWriter::writeFrame(const MResumePoint *rp)
RecoverWriter::writeInstruction(const MNode *rp)
{
if (!rp->writeRecoverData(writer_))
return false;
framesWritten_++;
instructionsWritten_++;
return true;
}
void
RecoverWriter::endRecover()
{
JS_ASSERT(nframes_ == framesWritten_);
MOZ_ASSERT(instructionCount_ == instructionsWritten_);
}

View File

@ -54,6 +54,8 @@ class RValueAllocation
UNTYPED_REG = 0x06,
UNTYPED_STACK = 0x07,
#endif
RECOVER_INSTRUCTION = 0x0a,
// The JSValueType is packed in the Mode.
TYPED_REG_MIN = 0x10,
TYPED_REG_MAX = 0x17,
@ -236,6 +238,11 @@ class RValueAllocation
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;
public:
static RValueAllocation read(CompactBufferReader &reader);
@ -360,19 +367,19 @@ class SnapshotWriter
}
};
class MResumePoint;
class MNode;
class RecoverWriter
{
CompactBufferWriter writer_;
uint32_t nframes_;
uint32_t framesWritten_;
uint32_t instructionCount_;
uint32_t instructionsWritten_;
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();
@ -473,6 +480,13 @@ class RecoverReader
public:
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 {
return numInstructionsRead_ < numInstructions_;
}

View File

@ -1402,6 +1402,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
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 Register &rhs);
void cmp32(const Operand &lhs, const Imm32 &rhs);

View File

@ -137,106 +137,116 @@ ToStackIndex(LAllocation *a)
}
bool
CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resumePoint,
uint32_t *startIndex)
CodeGeneratorShared::encodeAllocation(LSnapshot *snapshot, MDefinition *mir,
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())
mir = mir->toBox()->getOperand(0);
if (mir->isBox())
mir = mir->toBox()->getOperand(0);
MIRType type =
mir->isRecoveredOnBailout() ? MIRType_None :
mir->isUnused() ? MIRType_MagicOptimizedOut :
mir->type();
MIRType type = mir->isUnused()
? MIRType_MagicOptimizedOut
: mir->type();
RValueAllocation alloc;
RValueAllocation alloc;
switch (type) {
case MIRType_Undefined:
alloc = RValueAllocation::Undefined();
break;
case MIRType_Null:
alloc = RValueAllocation::Null();
break;
case MIRType_Int32:
case MIRType_String:
case MIRType_Object:
case MIRType_Boolean:
case MIRType_Double:
case MIRType_Float32:
{
LAllocation *payload = snapshot->payloadOfSlot(i);
JSValueType valueType = ValueTypeFromMIRType(type);
if (payload->isMemory()) {
if (type == MIRType_Float32)
alloc = RValueAllocation::Float32(ToStackIndex(payload));
else
alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload));
} else if (payload->isGeneralReg()) {
alloc = RValueAllocation::Typed(valueType, ToRegister(payload));
} else if (payload->isFloatReg()) {
FloatRegister reg = ToFloatRegister(payload);
if (type == MIRType_Float32)
alloc = RValueAllocation::Float32(reg);
else
alloc = RValueAllocation::Double(reg);
} else {
MConstant *constant = mir->toConstant();
uint32_t index;
if (!graph.addConstantToPool(constant->value(), &index))
return false;
alloc = RValueAllocation::ConstantPool(index);
}
break;
}
case MIRType_MagicOptimizedArguments:
case MIRType_MagicOptimizedOut:
{
uint32_t index;
JSWhyMagic why = (type == MIRType_MagicOptimizedArguments
? JS_OPTIMIZED_ARGUMENTS
: JS_OPTIMIZED_OUT);
Value v = MagicValue(why);
if (!graph.addConstantToPool(v, &index))
return false;
alloc = RValueAllocation::ConstantPool(index);
break;
}
default:
{
JS_ASSERT(mir->type() == MIRType_Value);
LAllocation *payload = snapshot->payloadOfSlot(i);
#ifdef JS_NUNBOX32
LAllocation *type = snapshot->typeOfSlot(i);
if (type->isRegister()) {
if (payload->isRegister())
alloc = RValueAllocation::Untyped(ToRegister(type), ToRegister(payload));
else
alloc = RValueAllocation::Untyped(ToRegister(type), ToStackIndex(payload));
} else {
if (payload->isRegister())
alloc = RValueAllocation::Untyped(ToStackIndex(type), ToRegister(payload));
else
alloc = RValueAllocation::Untyped(ToStackIndex(type), ToStackIndex(payload));
}
#elif JS_PUNBOX64
if (payload->isRegister())
alloc = RValueAllocation::Untyped(ToRegister(payload));
else
alloc = RValueAllocation::Untyped(ToStackIndex(payload));
#endif
break;
}
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;
}
snapshots_.add(alloc);
// 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:
alloc = RValueAllocation::Undefined();
break;
case MIRType_Null:
alloc = RValueAllocation::Null();
break;
case MIRType_Int32:
case MIRType_String:
case MIRType_Object:
case MIRType_Boolean:
case MIRType_Double:
case MIRType_Float32:
{
LAllocation *payload = snapshot->payloadOfSlot(*allocIndex);
JSValueType valueType = ValueTypeFromMIRType(type);
if (payload->isMemory()) {
if (type == MIRType_Float32)
alloc = RValueAllocation::Float32(ToStackIndex(payload));
else
alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload));
} else if (payload->isGeneralReg()) {
alloc = RValueAllocation::Typed(valueType, ToRegister(payload));
} else if (payload->isFloatReg()) {
FloatRegister reg = ToFloatRegister(payload);
if (type == MIRType_Float32)
alloc = RValueAllocation::Float32(reg);
else
alloc = RValueAllocation::Double(reg);
} else {
MConstant *constant = mir->toConstant();
uint32_t index;
if (!graph.addConstantToPool(constant->value(), &index))
return false;
alloc = RValueAllocation::ConstantPool(index);
}
break;
}
case MIRType_MagicOptimizedArguments:
case MIRType_MagicOptimizedOut:
{
uint32_t index;
JSWhyMagic why = (type == MIRType_MagicOptimizedArguments
? JS_OPTIMIZED_ARGUMENTS
: JS_OPTIMIZED_OUT);
Value v = MagicValue(why);
if (!graph.addConstantToPool(v, &index))
return false;
alloc = RValueAllocation::ConstantPool(index);
break;
}
default:
{
JS_ASSERT(mir->type() == MIRType_Value);
LAllocation *payload = snapshot->payloadOfSlot(*allocIndex);
#ifdef JS_NUNBOX32
LAllocation *type = snapshot->typeOfSlot(*allocIndex);
if (type->isRegister()) {
if (payload->isRegister())
alloc = RValueAllocation::Untyped(ToRegister(type), ToRegister(payload));
else
alloc = RValueAllocation::Untyped(ToRegister(type), ToStackIndex(payload));
} else {
if (payload->isRegister())
alloc = RValueAllocation::Untyped(ToStackIndex(type), ToRegister(payload));
else
alloc = RValueAllocation::Untyped(ToStackIndex(type), ToStackIndex(payload));
}
#elif JS_PUNBOX64
if (payload->isRegister())
alloc = RValueAllocation::Untyped(ToRegister(payload));
else
alloc = RValueAllocation::Untyped(ToStackIndex(payload));
#endif
break;
}
}
*startIndex += resumePoint->numOperands();
snapshots_.add(alloc);
*allocIndex += mir->isRecoveredOnBailout() ? 0 : 1;
return true;
}
@ -246,21 +256,18 @@ CodeGeneratorShared::encode(LRecoverInfo *recover)
if (recover->recoverOffset() != INVALID_RECOVER_OFFSET)
return true;
uint32_t frameCount = recover->mir()->frameCount();
IonSpew(IonSpew_Snapshots, "Encoding LRecoverInfo %p (frameCount %u)",
(void *)recover, frameCount);
uint32_t numInstructions = recover->numInstructions();
IonSpew(IonSpew_Snapshots, "Encoding LRecoverInfo %p (frameCount %u, instructions %u)",
(void *)recover, recover->mir()->frameCount(), numInstructions);
MResumePoint::Mode mode = recover->mir()->mode();
JS_ASSERT(mode != MResumePoint::Outer);
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();
it != end;
++it)
{
if (!recovers_.writeFrame(*it))
for (MNode **it = recover->begin(), **end = recover->end(); it != end; ++it) {
if (!recovers_.writeInstruction(*it))
return false;
}
@ -307,17 +314,17 @@ CodeGeneratorShared::encode(LSnapshot *snapshot)
snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId);
#endif
uint32_t startIndex = 0;
for (MResumePoint **it = recoverInfo->begin(), **end = recoverInfo->end();
it != end;
++it)
{
MResumePoint *mir = *it;
if (!encodeAllocations(snapshot, mir, &startIndex))
uint32_t allocIndex = 0;
LRecoverInfo::OperandIter it(recoverInfo->begin());
LRecoverInfo::OperandIter end(recoverInfo->end());
for (; it != end; ++it) {
DebugOnly<uint32_t> allocWritten = snapshots_.allocWritten();
if (!encodeAllocation(snapshot, *it, &allocIndex))
return false;
MOZ_ASSERT(allocWritten + 1 == snapshots_.allocWritten());
}
MOZ_ASSERT(snapshots_.allocWritten() == snapshot->numSlots());
MOZ_ASSERT(allocIndex == snapshot->numSlots());
snapshots_.endSnapshot();
snapshot->setSnapshotOffset(offset);
return !snapshots_.oom();

View File

@ -272,7 +272,7 @@ class CodeGeneratorShared : public LInstructionVisitor
// false on failure.
bool encode(LRecoverInfo *recover);
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.
// If the bailout table is full, this returns false, which is not a fatal

View File

@ -74,49 +74,51 @@ LIRGeneratorShared::getRecoverInfo(MResumePoint *rp)
LSnapshot *
LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKind kind)
{
LRecoverInfo *recover = getRecoverInfo(rp);
if (!recover)
LRecoverInfo *recoverInfo = getRecoverInfo(rp);
if (!recoverInfo)
return nullptr;
LSnapshot *snapshot = LSnapshot::New(gen, recover, kind);
LSnapshot *snapshot = LSnapshot::New(gen, recoverInfo, kind);
if (!snapshot)
return nullptr;
size_t i = 0;
for (MResumePoint **it = recover->begin(), **end = recover->end(); it != end; ++it) {
MResumePoint *mir = *it;
for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) {
MDefinition *ins = mir->getOperand(j);
size_t index = 0;
LRecoverInfo::OperandIter it(recoverInfo->begin());
LRecoverInfo::OperandIter end(recoverInfo->end());
for (; it != end; ++it) {
MDefinition *ins = *it;
if (ins->isRecoveredOnBailout())
continue;
LAllocation *type = snapshot->typeOfSlot(i);
LAllocation *payload = snapshot->payloadOfSlot(i);
LAllocation *type = snapshot->typeOfSlot(index);
LAllocation *payload = snapshot->payloadOfSlot(index);
++index;
if (ins->isBox())
ins = ins->toBox()->getOperand(0);
if (ins->isBox())
ins = ins->toBox()->getOperand(0);
// Guards should never be eliminated.
JS_ASSERT_IF(ins->isUnused(), !ins->isGuard());
// Guards should never be eliminated.
JS_ASSERT_IF(ins->isUnused(), !ins->isGuard());
// Snapshot operands other than constants should never be
// emitted-at-uses. Try-catch support depends on there being no
// code between an instruction and the LOsiPoint that follows it.
JS_ASSERT_IF(!ins->isConstant(), !ins->isEmittedAtUses());
// Snapshot operands other than constants should never be
// emitted-at-uses. Try-catch support depends on there being no
// code between an instruction and the LOsiPoint that follows it.
JS_ASSERT_IF(!ins->isConstant(), !ins->isEmittedAtUses());
// The register allocation will fill these fields in with actual
// register/stack assignments. During code generation, we can restore
// interpreter state with the given information. Note that for
// constants, including known types, we record a dummy placeholder,
// since we can recover the same information, much cleaner, from MIR.
if (ins->isConstant() || ins->isUnused()) {
*type = LConstantIndex::Bogus();
*payload = LConstantIndex::Bogus();
} else if (ins->type() != MIRType_Value) {
*type = LConstantIndex::Bogus();
*payload = use(ins, LUse::KEEPALIVE);
} else {
*type = useType(ins, LUse::KEEPALIVE);
*payload = usePayload(ins, LUse::KEEPALIVE);
}
// The register allocation will fill these fields in with actual
// register/stack assignments. During code generation, we can restore
// interpreter state with the given information. Note that for
// constants, including known types, we record a dummy placeholder,
// since we can recover the same information, much cleaner, from MIR.
if (ins->isConstant() || ins->isUnused()) {
*type = LConstantIndex::Bogus();
*payload = LConstantIndex::Bogus();
} else if (ins->type() != MIRType_Value) {
*type = LConstantIndex::Bogus();
*payload = use(ins, LUse::KEEPALIVE);
} else {
*type = useType(ins, LUse::KEEPALIVE);
*payload = usePayload(ins, LUse::KEEPALIVE);
}
}
@ -128,40 +130,42 @@ LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKi
LSnapshot *
LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKind kind)
{
LRecoverInfo *recover = getRecoverInfo(rp);
if (!recover)
LRecoverInfo *recoverInfo = getRecoverInfo(rp);
if (!recoverInfo)
return nullptr;
LSnapshot *snapshot = LSnapshot::New(gen, recover, kind);
LSnapshot *snapshot = LSnapshot::New(gen, recoverInfo, kind);
if (!snapshot)
return nullptr;
size_t i = 0;
for (MResumePoint **it = recover->begin(), **end = recover->end(); it != end; ++it) {
MResumePoint *mir = *it;
for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) {
MDefinition *def = mir->getOperand(j);
size_t index = 0;
LRecoverInfo::OperandIter it(recoverInfo->begin());
LRecoverInfo::OperandIter end(recoverInfo->end());
for (; it != end; ++it) {
MDefinition *def = *it;
if (def->isBox())
def = def->toBox()->getOperand(0);
if (def->isRecoveredOnBailout())
continue;
// Guards should never be eliminated.
JS_ASSERT_IF(def->isUnused(), !def->isGuard());
if (def->isBox())
def = def->toBox()->getOperand(0);
// Snapshot operands other than constants should never be
// emitted-at-uses. Try-catch support depends on there being no
// code between an instruction and the LOsiPoint that follows it.
JS_ASSERT_IF(!def->isConstant(), !def->isEmittedAtUses());
// Guards should never be eliminated.
JS_ASSERT_IF(def->isUnused(), !def->isGuard());
LAllocation *a = snapshot->getEntry(i);
// Snapshot operands other than constants should never be
// emitted-at-uses. Try-catch support depends on there being no
// code between an instruction and the LOsiPoint that follows it.
JS_ASSERT_IF(!def->isConstant(), !def->isEmittedAtUses());
if (def->isUnused()) {
*a = LConstantIndex::Bogus();
continue;
}
LAllocation *a = snapshot->getEntry(index++);
*a = useKeepaliveOrConstant(def);
if (def->isUnused()) {
*a = LConstantIndex::Bogus();
continue;
}
*a = useKeepaliveOrConstant(def);
}
return snapshot;

View File

@ -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,
// convert it to double. Else, branch to failure.
void ensureDouble(const ValueOperand &source, FloatRegister dest, Label *failure) {

View File

@ -1035,6 +1035,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
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,
// convert it to double. Else, branch to failure.

View File

@ -235,13 +235,10 @@ class AutoVectorRooter : protected AutoGCRooter
return vector.reserve(newLength);
}
T &operator[](size_t i) { return vector[i]; }
const T &operator[](size_t i) const { return vector[i]; }
JS::MutableHandle<T> handleAt(size_t i) {
JS::MutableHandle<T> operator[](size_t 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]);
}

View File

@ -1744,14 +1744,14 @@ MergeSortByKey(K keys, size_t len, K scratch, C comparator, AutoValueVector *vec
do {
size_t k = keys[j].elementIndex;
keys[j].elementIndex = j;
(*vec)[j] = (*vec)[k];
(*vec)[j].set((*vec)[k]);
j = k;
} while (j != i);
// We could assert the loop invariant that |i == keys[i].elementIndex|
// here if we synced |keys[i].elementIndex|. But doing so would render
// the assertion vacuous, so don't bother, even in debug builds.
(*vec)[i] = tv;
(*vec)[i].set(tv);
}
return true;
@ -1820,7 +1820,7 @@ SortNumerically(JSContext *cx, AutoValueVector *vec, size_t len, ComparatorMatch
return false;
double dv;
if (!ToNumber(cx, vec->handleAt(i), &dv))
if (!ToNumber(cx, (*vec)[i], &dv))
return false;
NumericElement el = { dv, i };

View File

@ -2771,8 +2771,10 @@ BeginMarkPhase(JSRuntime *rt)
}
if (!rt->gcShouldCleanUpEverything) {
#ifdef JS_ION
if (JSCompartment *comp = jit::TopmostJitActivationCompartment(rt))
comp->zone()->setPreservingCode(true);
#endif
}
/*

View File

@ -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
TypeSet::isSubset(TypeSet *other)
{

View File

@ -584,6 +584,12 @@ class TypeSet
*/
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. */
bool addTypesToConstraint(JSContext *cx, TypeConstraint *constraint);

View File

@ -463,7 +463,7 @@ js::math_imul(JSContext *cx, unsigned argc, Value *vp)
// Implements Math.fround (20.2.2.16) up to step 3
bool
js::RoundFloat32(JSContext *cx, Handle<Value> v, float *out)
js::RoundFloat32(JSContext *cx, HandleValue v, float *out)
{
double d;
bool success = ToNumber(cx, v, &d);
@ -471,6 +471,17 @@ js::RoundFloat32(JSContext *cx, Handle<Value> v, float *out)
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
js::math_fround(JSContext *cx, unsigned argc, Value *vp)
{

View File

@ -110,7 +110,10 @@ extern bool
math_imul(JSContext *cx, unsigned argc, js::Value *vp);
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
math_fround(JSContext *cx, unsigned argc, js::Value *vp);

View File

@ -1003,7 +1003,7 @@ js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
bool dummy;
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
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 true;
@ -1017,7 +1017,7 @@ js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
if (obj->is<ProxyObject>()) {
for (size_t i = 0, len = ids.length(); i < len; i++) {
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 true;
@ -1028,7 +1028,7 @@ js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
bool dummy;
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;
}
@ -1743,7 +1743,7 @@ JS_CopyPropertiesFrom(JSContext *cx, HandleObject target, HandleObject obj)
return false;
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;
}
@ -2013,7 +2013,7 @@ js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
}
JS_ASSERT(it.front().hasDefaultGetter());
ids[it.front().slot()] = it.front().propid();
ids[it.front().slot()].set(it.front().propid());
}
}

View File

@ -243,7 +243,7 @@ BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
return false;
if (desc.object() && desc.isEnumerable())
props[i++] = id;
props[i++].set(id);
}
JS_ASSERT(i <= props.length());
@ -1582,7 +1582,7 @@ ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleVa
// step iii
for (uint32_t j = 0; j < i; ++j) {
if (props[j] == id) {
if (props[j].get() == id) {
ReportInvalidTrapResult(cx, proxy, trapName);
return false;
}
@ -1618,7 +1618,7 @@ ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleVa
bool found = false;
for (size_t j = 0; j < props.length(); ++j) {
if (props[j] == id) {
if (props[j].get() == id) {
found = true;
break;
}
@ -2453,7 +2453,7 @@ js::AppendUnique(JSContext *cx, AutoIdVector &base, AutoIdVector &others)
for (size_t i = 0; i < others.length(); ++i) {
bool unique = true;
for (size_t j = 0; j < base.length(); ++j) {
if (others[i] == base[j]) {
if (others[i].get() == base[j]) {
unique = false;
break;
}

Some files were not shown because too many files have changed in this diff Show More