зеркало из https://github.com/mozilla/gecko-dev.git
Merge last PGO-green inbound changeset to m-c.
--HG-- rename : dom/bindings/BindingUtils.h => dom/bindings/OwningNonNull.h
This commit is contained in:
Коммит
d9d7605e4f
|
@ -307,9 +307,7 @@
|
|||
#endif
|
||||
@BINPATH@/components/uconv.xpt
|
||||
@BINPATH@/components/unicharutil.xpt
|
||||
#ifdef MOZ_UPDATER
|
||||
@BINPATH@/components/update.xpt
|
||||
#endif
|
||||
@BINPATH@/components/uriloader.xpt
|
||||
@BINPATH@/components/urlformatter.xpt
|
||||
@BINPATH@/components/webBrowser_core.xpt
|
||||
|
|
|
@ -10,7 +10,8 @@ let SocialUI,
|
|||
SocialShare,
|
||||
SocialMenu,
|
||||
SocialToolbar,
|
||||
SocialSidebar;
|
||||
SocialSidebar,
|
||||
SocialStatus;
|
||||
|
||||
(function() {
|
||||
|
||||
|
|
|
@ -6368,7 +6368,7 @@ var gIdentityHandler = {
|
|||
IDENTITY_MODE_UNKNOWN : "unknownIdentity", // No trusted identity information
|
||||
IDENTITY_MODE_MIXED_DISPLAY_LOADED : "unknownIdentity mixedContent mixedDisplayContent", // SSL with unauthenticated display content
|
||||
IDENTITY_MODE_MIXED_ACTIVE_LOADED : "unknownIdentity mixedContent mixedActiveContent", // SSL with unauthenticated active (and perhaps also display) content
|
||||
IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED : "unknownIdentity mixedContent mixedDisplayContent", // SSL with unauthenticated display content; unauthenticated active content is blocked.
|
||||
IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED : "unknownIdentity mixedContent mixedDisplayContentLoadedActiveBlocked", // SSL with unauthenticated display content; unauthenticated active content is blocked.
|
||||
IDENTITY_MODE_CHROMEUI : "chromeUI", // Part of the product's UI
|
||||
|
||||
// Cache the most recent SSLStatus and Location seen in checkIdentity
|
||||
|
|
|
@ -191,12 +191,19 @@ const DownloadsPanel = {
|
|||
//// Panel interface
|
||||
|
||||
/**
|
||||
* Main panel element in the browser window.
|
||||
* Main panel element in the browser window, or null if the panel overlay
|
||||
* hasn't been loaded yet.
|
||||
*/
|
||||
get panel()
|
||||
{
|
||||
// If the downloads panel overlay hasn't loaded yet, just return null
|
||||
// without reseting this.panel.
|
||||
let downloadsPanel = document.getElementById("downloadsPanel");
|
||||
if (!downloadsPanel)
|
||||
return null;
|
||||
|
||||
delete this.panel;
|
||||
return this.panel = document.getElementById("downloadsPanel");
|
||||
return this.panel = downloadsPanel;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,9 @@ function test_task()
|
|||
];
|
||||
|
||||
try {
|
||||
// Wait for focus first
|
||||
yield promiseFocus();
|
||||
|
||||
// Ensure that state is reset in case previous tests didn't finish.
|
||||
yield task_resetState();
|
||||
|
||||
|
|
|
@ -10,6 +10,14 @@
|
|||
*/
|
||||
function test_task()
|
||||
{
|
||||
// Clear the download panel has shown preference first as this test is used to
|
||||
// verify this preference's behaviour.
|
||||
let oldPrefValue = true;
|
||||
try {
|
||||
oldPrefValue = Services.prefs.getBoolPref("browser.download.panel.shown");
|
||||
} catch(ex) { }
|
||||
Services.prefs.setBoolPref("browser.download.panel.shown", false);
|
||||
|
||||
try {
|
||||
// Ensure that state is reset in case previous tests didn't finish.
|
||||
yield task_resetState();
|
||||
|
@ -49,5 +57,9 @@ function test_task()
|
|||
} finally {
|
||||
// Clean up when the test finishes.
|
||||
yield task_resetState();
|
||||
// Set the preference instead of clearing it afterwards to ensure the
|
||||
// right value is used no matter what the default was. This ensures the
|
||||
// panel doesn't appear and affect other tests.
|
||||
Services.prefs.setBoolPref("browser.download.panel.shown", oldPrefValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,10 @@ function promisePanelOpened()
|
|||
{
|
||||
let deferred = Promise.defer();
|
||||
|
||||
if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") {
|
||||
return deferred.resolve();
|
||||
}
|
||||
|
||||
// Hook to wait until the panel is shown.
|
||||
let originalOnPopupShown = DownloadsPanel.onPopupShown;
|
||||
DownloadsPanel.onPopupShown = function () {
|
||||
|
@ -75,9 +79,6 @@ function task_resetState()
|
|||
yield download.finalize(true);
|
||||
}
|
||||
|
||||
// Reset any prefs that might have been changed.
|
||||
Services.prefs.clearUserPref("browser.download.panel.shown");
|
||||
|
||||
DownloadsPanel.hidePanel();
|
||||
|
||||
yield promiseFocus();
|
||||
|
|
|
@ -49,6 +49,9 @@ function test() {
|
|||
oe.off("get", callback);
|
||||
ok(event, "event defined");
|
||||
ok(path, "path defined");
|
||||
if (index >= expected.length) {
|
||||
return;
|
||||
}
|
||||
let e = expected[index];
|
||||
is(event, e.type, "[" + index + "] Right event received");
|
||||
is(path.join("."), e.path, "[" + index + "] Path valid");
|
||||
|
|
|
@ -1404,6 +1404,10 @@ toolbar[mode="icons"] #zoom-in-button {
|
|||
list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display@2x.png);
|
||||
}
|
||||
|
||||
.mixedDisplayContentLoadedActiveBlocked > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display@2x.png);
|
||||
}
|
||||
|
||||
#identity-box:hover:active > #page-proxy-favicon,
|
||||
#identity-box[open=true] > #page-proxy-favicon {
|
||||
-moz-image-region: rect(0, 64px, 32px, 32px);
|
||||
|
|
|
@ -76,6 +76,10 @@
|
|||
list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display.png);
|
||||
}
|
||||
|
||||
.mixedDisplayContentLoadedActiveBlocked > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display.png);
|
||||
}
|
||||
|
||||
#page-proxy-favicon[pageproxystate="invalid"] {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ MACH_MODULES = [
|
|||
'python/mozbuild/mozbuild/config.py',
|
||||
'python/mozbuild/mozbuild/mach_commands.py',
|
||||
'python/mozbuild/mozbuild/frontend/mach_commands.py',
|
||||
'testing/mach_commands.py',
|
||||
'testing/marionette/mach_commands.py',
|
||||
'testing/mochitest/mach_commands.py',
|
||||
'testing/xpcshell/mach_commands.py',
|
||||
|
|
|
@ -1534,6 +1534,7 @@ public:
|
|||
|
||||
static JSContext *GetCurrentJSContext();
|
||||
static JSContext *GetSafeJSContext();
|
||||
static JSContext *GetDefaultJSContextForThread();
|
||||
|
||||
/**
|
||||
* Case insensitive comparison between two strings. However it only ignores
|
||||
|
|
|
@ -1766,6 +1766,7 @@ namespace mozilla {
|
|||
namespace dom {
|
||||
namespace workers {
|
||||
extern bool IsCurrentThreadRunningChromeWorker();
|
||||
extern JSContext* GetCurrentThreadJSContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5197,6 +5198,17 @@ nsContentUtils::GetSafeJSContext()
|
|||
return sXPConnect->GetSafeJSContext();
|
||||
}
|
||||
|
||||
/* static */
|
||||
JSContext *
|
||||
nsContentUtils::GetDefaultJSContextForThread()
|
||||
{
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
return GetSafeJSContext();
|
||||
} else {
|
||||
return workers::GetCurrentThreadJSContext();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsContentUtils::ASCIIToLower(nsAString& aStr)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "nsJSUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "Units.h"
|
||||
|
||||
class nsPresContext;
|
||||
|
|
|
@ -75,7 +75,7 @@ public:
|
|||
// Sets the duration of the media in microseconds. The MediaDecoder
|
||||
// fires a durationchange event to its owner (e.g., an HTML audio
|
||||
// tag).
|
||||
virtual void UpdateMediaDuration(int64_t aDuration) = 0;
|
||||
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) = 0;
|
||||
|
||||
// Set the media as being seekable or not.
|
||||
virtual void SetMediaSeekable(bool aMediaSeekable) = 0;
|
||||
|
|
|
@ -109,7 +109,7 @@ BufferDecoder::SetMediaDuration(int64_t aDuration)
|
|||
}
|
||||
|
||||
void
|
||||
BufferDecoder::UpdateMediaDuration(int64_t aDuration)
|
||||
BufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
|
||||
void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
||||
|
||||
void UpdateMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
||||
void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
||||
|
||||
void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -1272,10 +1272,14 @@ void MediaDecoder::SetMediaDuration(int64_t aDuration)
|
|||
GetStateMachine()->SetDuration(aDuration);
|
||||
}
|
||||
|
||||
void MediaDecoder::UpdateMediaDuration(int64_t aDuration)
|
||||
void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mPlayState <= PLAY_STATE_LOADING) {
|
||||
return;
|
||||
}
|
||||
NS_ENSURE_TRUE_VOID(GetStateMachine());
|
||||
GetStateMachine()->UpdateDuration(aDuration);
|
||||
GetStateMachine()->UpdateEstimatedDuration(aDuration);
|
||||
}
|
||||
|
||||
void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
|
||||
|
|
|
@ -497,8 +497,18 @@ public:
|
|||
// from a content header. Must be called from the main thread only.
|
||||
virtual void SetDuration(double aDuration);
|
||||
|
||||
// Sets the initial duration of the media. Called while the media metadata
|
||||
// is being read and the decode is being setup.
|
||||
void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
||||
void UpdateMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
||||
// Updates the media duration. This is called while the media is being
|
||||
// played, calls before the media has reached loaded metadata are ignored.
|
||||
// The duration is assumed to be an estimate, and so a degree of
|
||||
// instability is expected; if the incoming duration is not significantly
|
||||
// different from the existing duration, the change request is ignored.
|
||||
// If the incoming duration is significantly different, the duration is
|
||||
// changed, this causes a durationchanged event to fire to the media
|
||||
// element.
|
||||
void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
|
||||
|
||||
// Set a flag indicating whether seeking is supported
|
||||
virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;
|
||||
|
|
|
@ -118,6 +118,12 @@ PR_STATIC_ASSERT(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS);
|
|||
// This value has been chosen empirically.
|
||||
static const uint32_t AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS = 200000;
|
||||
|
||||
// The amount of instability we tollerate in calls to
|
||||
// MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration
|
||||
// less than this are ignored, as they're assumed to be the result of
|
||||
// instability in the duration estimation.
|
||||
static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
|
||||
|
||||
static TimeDuration UsecsToDuration(int64_t aUsecs) {
|
||||
return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
|
||||
}
|
||||
|
@ -1444,9 +1450,12 @@ void MediaDecoderStateMachine::SetDuration(int64_t aDuration)
|
|||
}
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::UpdateDuration(int64_t aDuration)
|
||||
void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
|
||||
{
|
||||
if (aDuration != GetDuration()) {
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
int64_t duration = GetDuration();
|
||||
if (aDuration != duration &&
|
||||
abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
|
||||
SetDuration(aDuration);
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
|
||||
|
|
|
@ -160,9 +160,12 @@ public:
|
|||
// aEndTime is in microseconds.
|
||||
void SetMediaEndTime(int64_t aEndTime);
|
||||
|
||||
// Called from decode thread to update the duration. Can result in
|
||||
// a durationchangeevent. aDuration is in microseconds.
|
||||
void UpdateDuration(int64_t aDuration);
|
||||
// Called from main thread to update the duration with an estimated value.
|
||||
// The duration is only changed if its significantly different than the
|
||||
// the current duration, as the incoming duration is an estimate and so
|
||||
// often is unstable as more data is read and the estimate is updated.
|
||||
// Can result in a durationchangeevent. aDuration is in microseconds.
|
||||
void UpdateEstimatedDuration(int64_t aDuration);
|
||||
|
||||
// Functions used by assertions to ensure we're calling things
|
||||
// on the appropriate threads.
|
||||
|
|
|
@ -36,12 +36,14 @@ GetDirectShowLog() {
|
|||
|
||||
DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaDecoderReader(aDecoder),
|
||||
mMP3FrameParser(aDecoder->GetResource()->GetLength()),
|
||||
#ifdef DEBUG
|
||||
mRotRegister(0),
|
||||
#endif
|
||||
mNumChannels(0),
|
||||
mAudioRate(0),
|
||||
mBytesPerSample(0)
|
||||
mBytesPerSample(0),
|
||||
mDuration(0)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_CTOR(DirectShowReader);
|
||||
|
@ -366,4 +368,21 @@ DirectShowReader::OnDecodeThreadFinish()
|
|||
CoUninitialize();
|
||||
}
|
||||
|
||||
void
|
||||
DirectShowReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mMP3FrameParser.IsMP3()) {
|
||||
return;
|
||||
}
|
||||
mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
|
||||
int64_t duration = mMP3FrameParser.GetDuration();
|
||||
if (duration != mDuration) {
|
||||
mDuration = duration;
|
||||
MOZ_ASSERT(mDecoder);
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mDecoder->UpdateEstimatedMediaDuration(mDuration);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "Windows.h" // HRESULT, DWORD
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "MP3FrameParser.h"
|
||||
|
||||
class IGraphBuilder;
|
||||
class IMediaControl;
|
||||
|
@ -70,6 +71,10 @@ public:
|
|||
void OnDecodeThreadStart() MOZ_OVERRIDE;
|
||||
void OnDecodeThreadFinish() MOZ_OVERRIDE;
|
||||
|
||||
void NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
// Calls mAudioQueue.Finish(), and notifies the filter graph that playback
|
||||
|
@ -91,6 +96,11 @@ private:
|
|||
// The graph will block while this is blocked, i.e. it will pause decoding.
|
||||
RefPtr<AudioSinkFilter> mAudioSinkFilter;
|
||||
|
||||
// Some MP3s are variable bitrate, so DirectShow's duration estimation
|
||||
// can make its duration estimation based on the wrong bitrate. So we parse
|
||||
// the MP3 frames to get a more accuate estimate of the duration.
|
||||
MP3FrameParser mMP3FrameParser;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Used to add/remove the filter graph to the Running Object Table. You can
|
||||
// connect GraphEdit/GraphStudio to the graph to observe and/or debug its
|
||||
|
@ -106,6 +116,9 @@ private:
|
|||
|
||||
// Number of bytes per sample. Can be either 1 or 2.
|
||||
uint32_t mBytesPerSample;
|
||||
|
||||
// Duration of the stream, in microseconds.
|
||||
int64_t mDuration;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -632,7 +632,7 @@ void OmxDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_
|
|||
|
||||
MOZ_ASSERT(mDecoder);
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mDecoder->UpdateMediaDuration(mDurationUs);
|
||||
mDecoder->UpdateEstimatedMediaDuration(mDurationUs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "nsIErrorService.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsIXPConnect.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsIScriptGlobalObject.h"
|
||||
#include "nsIDOMScriptObjectFactory.h"
|
||||
#include "js/Id.h"
|
||||
#include "nsIXPConnect.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef GetClassName
|
||||
|
|
|
@ -673,6 +673,28 @@ NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
|
|||
bool aAllowNativeWrapper)
|
||||
{
|
||||
nsresult rv;
|
||||
// Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
|
||||
// on all threads.
|
||||
nsWrapperCache *cache = aHelper.GetWrapperCache();
|
||||
|
||||
if (cache && cache->IsDOMBinding()) {
|
||||
JS::RootedObject obj(aCx, cache->GetWrapper());
|
||||
if (!obj) {
|
||||
obj = cache->WrapObject(aCx, aScope);
|
||||
}
|
||||
|
||||
if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, obj.address())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj) {
|
||||
*aRetval = JS::ObjectValue(*obj);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!XPCConvert::NativeInterface2JSObject(aRetval, NULL, aHelper, aIID,
|
||||
NULL, aAllowNativeWrapper, &rv)) {
|
||||
// I can't tell if NativeInterface2JSObject throws JS exceptions
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "nsCxPusher.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -49,7 +50,11 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
|
|||
, mCompartment(aCompartment)
|
||||
, mErrorResult(aRv)
|
||||
, mExceptionHandling(aExceptionHandling)
|
||||
, mIsMainThread(NS_IsMainThread())
|
||||
{
|
||||
if (mIsMainThread) {
|
||||
nsContentUtils::EnterMicroTask();
|
||||
}
|
||||
// We need to produce a useful JSContext here. Ideally one that the callback
|
||||
// is in some sense associated with, so that we can sort of treat it as a
|
||||
// "script entry point". Though once we actually have script entry points,
|
||||
|
@ -58,46 +63,50 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
|
|||
|
||||
// First, find the real underlying callback.
|
||||
JSObject* realCallback = js::UncheckedUnwrap(aCallback);
|
||||
|
||||
// Now get the nsIScriptGlobalObject for this callback.
|
||||
JSContext* cx = nullptr;
|
||||
nsIScriptContext* ctx = nullptr;
|
||||
nsIScriptGlobalObject* sgo = nsJSUtils::GetStaticScriptGlobal(realCallback);
|
||||
if (sgo) {
|
||||
// Make sure that if this is a window it's the current inner, since the
|
||||
// nsIScriptContext and hence JSContext are associated with the outer
|
||||
// window. Which means that if someone holds on to a function from a
|
||||
// now-unloaded document we'd have the new document as the script entry
|
||||
// point...
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(sgo);
|
||||
if (win) {
|
||||
MOZ_ASSERT(win->IsInnerWindow());
|
||||
nsPIDOMWindow* outer = win->GetOuterWindow();
|
||||
if (!outer || win != outer->GetCurrentInnerWindow()) {
|
||||
// Just bail out from here
|
||||
return;
|
||||
|
||||
if (mIsMainThread) {
|
||||
// Now get the nsIScriptGlobalObject for this callback.
|
||||
nsIScriptContext* ctx = nullptr;
|
||||
nsIScriptGlobalObject* sgo = nsJSUtils::GetStaticScriptGlobal(realCallback);
|
||||
if (sgo) {
|
||||
// Make sure that if this is a window it's the current inner, since the
|
||||
// nsIScriptContext and hence JSContext are associated with the outer
|
||||
// window. Which means that if someone holds on to a function from a
|
||||
// now-unloaded document we'd have the new document as the script entry
|
||||
// point...
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(sgo);
|
||||
if (win) {
|
||||
MOZ_ASSERT(win->IsInnerWindow());
|
||||
nsPIDOMWindow* outer = win->GetOuterWindow();
|
||||
if (!outer || win != outer->GetCurrentInnerWindow()) {
|
||||
// Just bail out from here
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if not a window at all, just press on
|
||||
|
||||
ctx = sgo->GetContext();
|
||||
if (ctx) {
|
||||
// We don't check whether scripts are enabled on ctx, because
|
||||
// CheckFunctionAccess will do that anyway... and because we ignore them
|
||||
// being disabled if the callee is system.
|
||||
cx = ctx->GetNativeContext();
|
||||
}
|
||||
}
|
||||
// if not a window at all, just press on
|
||||
|
||||
ctx = sgo->GetContext();
|
||||
if (ctx) {
|
||||
// We don't check whether scripts are enabled on ctx, because
|
||||
// CheckFunctionAccess will do that anyway... and because we ignore them
|
||||
// being disabled if the callee is system.
|
||||
cx = ctx->GetNativeContext();
|
||||
if (!cx) {
|
||||
// We didn't manage to hunt down a script global to work with. Just fall
|
||||
// back on using the safe context.
|
||||
cx = nsContentUtils::GetSafeJSContext();
|
||||
}
|
||||
}
|
||||
|
||||
if (!cx) {
|
||||
// We didn't manage to hunt down a script global to work with. Just fall
|
||||
// back on using the safe context.
|
||||
cx = nsContentUtils::GetSafeJSContext();
|
||||
// Make sure our JSContext is pushed on the stack.
|
||||
mCxPusher.Push(cx);
|
||||
} else {
|
||||
cx = workers::GetCurrentThreadJSContext();
|
||||
}
|
||||
|
||||
// Make sure our JSContext is pushed on the stack.
|
||||
mCxPusher.Push(cx);
|
||||
|
||||
// Unmark the callable, and stick it in a Rooted before it can go gray again.
|
||||
// Nothing before us in this function can trigger a CC, so it's safe to wait
|
||||
// until here it do the unmark. This allows us to order the following two
|
||||
|
@ -109,16 +118,18 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
|
|||
JS::ExposeObjectToActiveJS(aCallback);
|
||||
mRootedCallable.construct(cx, aCallback);
|
||||
|
||||
// Check that it's ok to run this callback at all.
|
||||
// FIXME: Bug 807371: we want a less silly check here.
|
||||
// Make sure to unwrap aCallback before passing it in, because
|
||||
// getting principals from wrappers is silly.
|
||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||
CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr);
|
||||
if (mIsMainThread) {
|
||||
// Check that it's ok to run this callback at all.
|
||||
// FIXME: Bug 807371: we want a less silly check here.
|
||||
// Make sure to unwrap aCallback before passing it in, because
|
||||
// getting principals from wrappers is silly.
|
||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||
CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
// Security check failed. We're done here.
|
||||
return;
|
||||
if (NS_FAILED(rv)) {
|
||||
// Security check failed. We're done here.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Enter the compartment of our callback, so we can actually work with it.
|
||||
|
@ -204,12 +215,19 @@ CallbackObject::CallSetup::~CallSetup()
|
|||
|
||||
// Popping an nsCxPusher is safe even if it never got pushed.
|
||||
mCxPusher.Pop();
|
||||
|
||||
// It is important that this is the last thing we do, after leaving the
|
||||
// compartment and popping the context.
|
||||
if (mIsMainThread) {
|
||||
nsContentUtils::LeaveMicroTask();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
|
||||
const nsIID& aIID) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!aCallback) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -151,10 +151,6 @@ protected:
|
|||
|
||||
// And now members whose construction/destruction order we need to control.
|
||||
|
||||
// Put our nsAutoMicrotask first, so it gets destroyed after everything else
|
||||
// is gone
|
||||
nsAutoMicroTask mMt;
|
||||
|
||||
nsCxPusher mCxPusher;
|
||||
|
||||
// Constructed the rooter within the scope of mCxPusher above, so that it's
|
||||
|
@ -172,6 +168,7 @@ protected:
|
|||
ErrorResult& mErrorResult;
|
||||
const ExceptionHandling mExceptionHandling;
|
||||
uint32_t mSavedJSContextOptions;
|
||||
const bool mIsMainThread;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import string
|
|||
import math
|
||||
import itertools
|
||||
|
||||
from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType
|
||||
from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute
|
||||
from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor
|
||||
|
||||
AUTOGENERATED_WARNING_COMMENT = \
|
||||
|
@ -2897,14 +2897,11 @@ for (uint32_t i = 0; i < length; ++i) {
|
|||
holderArgs=holderArgs)
|
||||
|
||||
if type.isUnion():
|
||||
if isMember:
|
||||
raise TypeError("Can't handle unions as members, we have a "
|
||||
"holderType")
|
||||
nullable = type.nullable();
|
||||
if nullable:
|
||||
type = type.inner
|
||||
|
||||
unionArgumentObj = "${holderName}"
|
||||
unionArgumentObj = "${declName}" if isMember else "${holderName}"
|
||||
if nullable:
|
||||
unionArgumentObj += ".ref()"
|
||||
|
||||
|
@ -3067,7 +3064,7 @@ for (uint32_t i = 0; i < length; ++i) {
|
|||
exceptionCodeIndented.define()))
|
||||
templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
|
||||
|
||||
typeName = type.name
|
||||
typeName = type.name + ("ReturnValue" if isMember else "")
|
||||
argumentTypeName = typeName + "Argument"
|
||||
if nullable:
|
||||
typeName = "Nullable<" + typeName + " >"
|
||||
|
@ -3083,7 +3080,7 @@ for (uint32_t i = 0; i < length; ++i) {
|
|||
templateBody = handleNull(templateBody, unionArgumentObj)
|
||||
|
||||
declType = CGGeneric(typeName)
|
||||
holderType = CGGeneric(argumentTypeName)
|
||||
holderType = CGGeneric(argumentTypeName) if not isMember else None
|
||||
|
||||
# If we're isOptional and not nullable the normal optional handling will
|
||||
# handle lazy construction of our holder. If we're nullable we do it
|
||||
|
@ -3157,10 +3154,8 @@ for (uint32_t i = 0; i < length; ++i) {
|
|||
return handleJSObjectType(type, isMember, failureCode)
|
||||
|
||||
if (descriptor.interface.isCallback() and
|
||||
descriptor.interface.identifier.name != "EventListener"):
|
||||
if descriptor.workers:
|
||||
return handleJSObjectType(type, isMember, failureCode)
|
||||
|
||||
(descriptor.interface.identifier.name != "EventListener" or
|
||||
descriptorProvider.workers)):
|
||||
name = descriptor.interface.identifier.name
|
||||
if type.nullable() or isCallbackReturnValue:
|
||||
declType = CGGeneric("nsRefPtr<%s>" % name);
|
||||
|
@ -3416,10 +3411,16 @@ for (uint32_t i = 0; i < length; ++i) {
|
|||
declType = "NonNull<nsAString>"
|
||||
|
||||
# No need to deal with optional here; we handled it already
|
||||
decl = ""
|
||||
if isInUnionReturnValue:
|
||||
decl += "FakeDependentString str;\n"
|
||||
return JSToNativeConversionInfo(
|
||||
"%s"
|
||||
"%s\n"
|
||||
"${declName} = &${holderName};" %
|
||||
getConversionCode("${holderName}"),
|
||||
"${declName} = %s" %
|
||||
(decl,
|
||||
getConversionCode("str" if isInUnionReturnValue else "${holderName}"),
|
||||
("str;" if isInUnionReturnValue else "&${holderName};")),
|
||||
declType=CGGeneric(declType),
|
||||
holderType=CGGeneric("FakeDependentString"))
|
||||
|
||||
|
@ -4122,7 +4123,8 @@ if (!returnArray) {
|
|||
|
||||
if (type.isGeckoInterface() and
|
||||
(not type.isCallbackInterface() or
|
||||
type.unroll().inner.identifier.name == "EventListener")):
|
||||
(type.unroll().inner.identifier.name == "EventListener" and
|
||||
not descriptorProvider.workers))):
|
||||
descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
|
||||
if type.nullable():
|
||||
wrappingCode = ("if (!%s) {\n" % (result) +
|
||||
|
@ -4628,6 +4630,15 @@ class CGCallGenerator(CGThing):
|
|||
def define(self):
|
||||
return self.cgRoot.define()
|
||||
|
||||
def getUnionMemberName(type):
|
||||
if type.isGeckoInterface():
|
||||
return type.inner.identifier.name
|
||||
if type.isEnum():
|
||||
return type.inner.identifier.name
|
||||
if type.isArray() or type.isSequence():
|
||||
return str(type)
|
||||
return type.name
|
||||
|
||||
class MethodNotCreatorError(Exception):
|
||||
def __init__(self, typename):
|
||||
self.typename = typename
|
||||
|
@ -4710,8 +4721,15 @@ def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
|
|||
return CGList(memberWraps, "\n") if len(memberWraps) != 0 else None
|
||||
|
||||
if type.isUnion():
|
||||
raise TypeError("Can't handle wrapping of unions in constructor "
|
||||
"arguments yet")
|
||||
memberWraps = []
|
||||
for member in type.flatMemberTypes:
|
||||
memberWrap = wrapTypeIntoCurrentCompartment(
|
||||
member,
|
||||
"%s.%s" % (value, getUnionMemberName(member)))
|
||||
if memberWrap:
|
||||
memberWrap = CGIfWrapper(memberWrap, "mType == %s" % member)
|
||||
memberWraps.append(memberWrap)
|
||||
return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None
|
||||
|
||||
if (type.isString() or type.isPrimitive() or type.isEnum() or
|
||||
type.isGeckoInterface() or type.isCallback() or type.isDate()):
|
||||
|
@ -5333,8 +5351,12 @@ class FakeArgument():
|
|||
self.defaultValue = None
|
||||
self.treatNullAs = interfaceMember.treatNullAs
|
||||
self.treatUndefinedAs = interfaceMember.treatUndefinedAs
|
||||
self.enforceRange = False
|
||||
self.clamp = False
|
||||
if isinstance(interfaceMember, IDLAttribute):
|
||||
self.enforceRange = interfaceMember.enforceRange
|
||||
self.clamp = interfaceMember.clamp
|
||||
else:
|
||||
self.enforceRange = False
|
||||
self.clamp = False
|
||||
class FakeIdentifier():
|
||||
def __init__(self):
|
||||
self.name = name
|
||||
|
@ -6122,23 +6144,17 @@ def getUnionTypeTemplateVars(unionType, type, descriptorProvider, isReturnValue=
|
|||
if type.isDictionary() or type.isSequence():
|
||||
raise TypeError("Can't handle dictionaries or sequences in unions")
|
||||
|
||||
if type.isGeckoInterface():
|
||||
name = type.inner.identifier.name
|
||||
elif type.isEnum():
|
||||
name = type.inner.identifier.name
|
||||
elif type.isArray() or type.isSequence():
|
||||
name = str(type)
|
||||
else:
|
||||
name = type.name
|
||||
name = getUnionMemberName(type)
|
||||
|
||||
ctorArgs = "cx" if type.isSpiderMonkeyInterface() else ""
|
||||
|
||||
tryNextCode = ("tryNext = true;\n"
|
||||
"return true;")
|
||||
if type.isGeckoInterface():
|
||||
tryNextCode = ("if (mUnion.mType != mUnion.eUninitialized) {"
|
||||
" mUnion.Destroy%s();"
|
||||
"}" % name) + tryNextCode
|
||||
prefix = "" if isReturnValue else "mUnion."
|
||||
tryNextCode = ("if (%smType != %seUninitialized) {"
|
||||
" %sDestroy%s();"
|
||||
"}") % (prefix, prefix, prefix, name) + tryNextCode
|
||||
conversionInfo = getJSToNativeConversionInfo(
|
||||
type, descriptorProvider, failureCode=tryNextCode,
|
||||
isDefinitelyObject=True, isInUnionReturnValue=isReturnValue,
|
||||
|
@ -6154,11 +6170,11 @@ def getUnionTypeTemplateVars(unionType, type, descriptorProvider, isReturnValue=
|
|||
if type.isObject():
|
||||
body = ("mUnion.mValue.mObject.SetValue(cx, obj);\n"
|
||||
"mUnion.mType = mUnion.eObject;")
|
||||
setters = [ClassMethod("SetToObject", "void",
|
||||
[Argument("JSContext*", "cx"),
|
||||
Argument("JSObject*", "obj")],
|
||||
inline=True, bodyInHeader=True,
|
||||
body=body)]
|
||||
setter = ClassMethod("SetToObject", "void",
|
||||
[Argument("JSContext*", "cx"),
|
||||
Argument("JSObject*", "obj")],
|
||||
inline=True, bodyInHeader=True,
|
||||
body=body)
|
||||
|
||||
else:
|
||||
jsConversion = string.Template(conversionInfo.template).substitute(
|
||||
|
@ -6173,25 +6189,20 @@ def getUnionTypeTemplateVars(unionType, type, descriptorProvider, isReturnValue=
|
|||
pre="tryNext = false;\n",
|
||||
post="\n"
|
||||
"return true;")
|
||||
setters = [ClassMethod("TrySetTo" + name, "bool",
|
||||
[Argument("JSContext*", "cx"),
|
||||
Argument("JS::Handle<JS::Value>", "value"),
|
||||
Argument("JS::MutableHandle<JS::Value>", "pvalue"),
|
||||
Argument("bool&", "tryNext")],
|
||||
inline=True, bodyInHeader=True,
|
||||
body=jsConversion.define())]
|
||||
if type.isString():
|
||||
setters.append(ClassMethod("SetStringData", "void",
|
||||
[Argument("const nsDependentString::char_type*", "aData"),
|
||||
Argument("nsDependentString::size_type", "aLength")],
|
||||
inline=True, bodyInHeader=True,
|
||||
body="mStringHolder.SetData(aData, aLength);"))
|
||||
setter = ClassMethod("TrySetTo" + name, "bool",
|
||||
[Argument("JSContext*", "cx"),
|
||||
Argument("JS::Handle<JS::Value>", "value"),
|
||||
Argument("JS::MutableHandle<JS::Value>", "pvalue"),
|
||||
Argument("bool&", "tryNext")],
|
||||
inline=not isReturnValue,
|
||||
bodyInHeader=not isReturnValue,
|
||||
body=jsConversion.define())
|
||||
|
||||
return {
|
||||
"name": name,
|
||||
"structType": structType,
|
||||
"externalType": externalType,
|
||||
"setters": setters,
|
||||
"setter": setter,
|
||||
"holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None,
|
||||
"ctorArgs": ctorArgs,
|
||||
"ctorArgList": [Argument("JSContext*", "cx")] if type.isSpiderMonkeyInterface() else []
|
||||
|
@ -6254,6 +6265,16 @@ class CGUnionStruct(CGThing):
|
|||
vars["ctorArgList"],
|
||||
bodyInHeader=not self.isReturnValue,
|
||||
body=body))
|
||||
if self.isReturnValue:
|
||||
methods.append(vars["setter"])
|
||||
if t.isString():
|
||||
methods.append(
|
||||
ClassMethod("SetStringData", "void",
|
||||
[Argument("const nsString::char_type*", "aData"),
|
||||
Argument("nsString::size_type", "aLength")],
|
||||
inline=True, bodyInHeader=True,
|
||||
body="mValue.mString.Value().Assign(aData, aLength);"))
|
||||
|
||||
body = string.Template('MOZ_ASSERT(Is${name}(), "Wrong type!");\n'
|
||||
'mValue.m${name}.Destroy();\n'
|
||||
'mType = eUninitialized;').substitute(vars)
|
||||
|
@ -6352,7 +6373,7 @@ class CGUnionConversionStruct(CGThing):
|
|||
for t in self.type.flatMemberTypes:
|
||||
vars = getUnionTypeTemplateVars(self.type,
|
||||
t, self.descriptorProvider)
|
||||
methods.extend(vars["setters"])
|
||||
methods.append(vars["setter"])
|
||||
if vars["name"] != "Object":
|
||||
body=string.Template("mUnion.mType = mUnion.e${name};\n"
|
||||
"return mUnion.mValue.m${name}.SetValue(${ctorArgs});").substitute(vars)
|
||||
|
@ -6362,6 +6383,13 @@ class CGUnionConversionStruct(CGThing):
|
|||
bodyInHeader=True,
|
||||
body=body,
|
||||
visibility="private"))
|
||||
if t.isString():
|
||||
methods.append(ClassMethod("SetStringData", "void",
|
||||
[Argument("const nsDependentString::char_type*", "aData"),
|
||||
Argument("nsDependentString::size_type", "aLength")],
|
||||
inline=True, bodyInHeader=True,
|
||||
body="mStringHolder.SetData(aData, aLength);"))
|
||||
|
||||
if vars["holderType"] is not None:
|
||||
members.append(ClassMember("m%sHolder" % vars["name"],
|
||||
vars["holderType"]))
|
||||
|
@ -8115,8 +8143,12 @@ class CGDictionary(CGThing):
|
|||
getJSToNativeConversionInfo(
|
||||
member.type,
|
||||
descriptorProvider,
|
||||
isEnforceRange=member.enforceRange,
|
||||
isClamp=member.clamp,
|
||||
isMember="Dictionary",
|
||||
isOptional=(not member.defaultValue),
|
||||
# Set this to true so that we get an owning union.
|
||||
isInUnionReturnValue=True,
|
||||
defaultValue=member.defaultValue,
|
||||
sourceDescription=("'%s' member of %s" %
|
||||
(member.identifier.name,
|
||||
|
@ -10524,6 +10556,7 @@ struct PrototypeTraits;
|
|||
config)
|
||||
includes.add("mozilla/dom/OwningNonNull.h")
|
||||
includes.add("mozilla/dom/UnionMember.h")
|
||||
implincludes.add("mozilla/dom/PrimitiveConversions.h")
|
||||
|
||||
# Wrap all of that in our namespaces.
|
||||
curr = CGNamespace.build(['mozilla', 'dom'], unions)
|
||||
|
|
|
@ -209,10 +209,7 @@ class Descriptor(DescriptorProvider):
|
|||
else:
|
||||
nativeTypeDefault = "nsIDOM" + ifaceName
|
||||
elif self.interface.isCallback():
|
||||
if self.workers:
|
||||
nativeTypeDefault = "JSObject"
|
||||
else:
|
||||
nativeTypeDefault = "mozilla::dom::" + ifaceName
|
||||
nativeTypeDefault = "mozilla::dom::" + ifaceName
|
||||
else:
|
||||
if self.workers:
|
||||
nativeTypeDefault = "mozilla::dom::workers::" + ifaceName
|
||||
|
|
|
@ -39,6 +39,10 @@ public:
|
|||
new (mStorage.addr()) T(aValue1, aValue2);
|
||||
return *mStorage.addr();
|
||||
}
|
||||
T& Value()
|
||||
{
|
||||
return *mStorage.addr();
|
||||
}
|
||||
const T& Value() const
|
||||
{
|
||||
return *mStorage.addr();
|
||||
|
|
|
@ -2564,6 +2564,8 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
self.lenientThis = False
|
||||
self._unforgeable = False
|
||||
self.stringifier = stringifier
|
||||
self.enforceRange = False
|
||||
self.clamp = False
|
||||
|
||||
if static and identifier.name == "prototype":
|
||||
raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
|
||||
|
@ -2687,6 +2689,16 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
raise WebIDLError("[LenientFloat] used on an attribute with a "
|
||||
"non-restricted-float type",
|
||||
[attr.location, self.location])
|
||||
elif identifier == "EnforceRange":
|
||||
if self.readonly:
|
||||
raise WebIDLError("[EnforceRange] used on a readonly attribute",
|
||||
[attr.location, self.location])
|
||||
self.enforceRange = True
|
||||
elif identifier == "Clamp":
|
||||
if self.readonly:
|
||||
raise WebIDLError("[Clamp] used on a readonly attribute",
|
||||
[attr.location, self.location])
|
||||
self.clamp = True
|
||||
elif (identifier == "Pref" or
|
||||
identifier == "SetterThrows" or
|
||||
identifier == "Pure" or
|
||||
|
|
|
@ -686,6 +686,10 @@ public:
|
|||
void DontEnforceRangeOrClamp(int8_t);
|
||||
void DoEnforceRange(int8_t);
|
||||
void DoClamp(int8_t);
|
||||
void SetEnforcedByte(int8_t);
|
||||
int8_t EnforcedByte();
|
||||
void SetClampedByte(int8_t);
|
||||
int8_t ClampedByte();
|
||||
|
||||
private:
|
||||
// We add signatures here that _could_ start matching if the codegen
|
||||
|
|
|
@ -536,6 +536,8 @@ interface TestInterface {
|
|||
void dontEnforceRangeOrClamp(byte arg);
|
||||
void doEnforceRange([EnforceRange] byte arg);
|
||||
void doClamp([Clamp] byte arg);
|
||||
[EnforceRange] attribute byte enforcedByte;
|
||||
[Clamp] attribute byte clampedByte;
|
||||
|
||||
// Typedefs
|
||||
const myLong myLongConstant = 5;
|
||||
|
@ -707,6 +709,8 @@ dictionary Dict : ParentDict {
|
|||
long a;
|
||||
long b = 8;
|
||||
long z = 9;
|
||||
[EnforceRange] unsigned long enforcedUnsignedLong;
|
||||
[Clamp] unsigned long clampedUnsignedLong;
|
||||
DOMString str;
|
||||
DOMString empty = "";
|
||||
TestEnum otherEnum = "b";
|
||||
|
@ -736,6 +740,8 @@ dictionary Dict : ParentDict {
|
|||
unrestricted double negativeInfUrDouble = -Infinity;
|
||||
unrestricted double nanUrDouble = NaN;
|
||||
|
||||
(float or DOMString) floatOrString = "str";
|
||||
|
||||
ArrayBuffer arrayBuffer;
|
||||
ArrayBuffer? nullableArrayBuffer;
|
||||
Uint8Array uint8Array;
|
||||
|
@ -764,6 +770,7 @@ dictionary DictContainingSequence {
|
|||
sequence<object?>? ourSequence7;
|
||||
sequence<object>? ourSequence8 = null;
|
||||
sequence<object?>? ourSequence9 = null;
|
||||
sequence<(float or DOMString)> ourSequence10;
|
||||
};
|
||||
|
||||
dictionary DictForConstructor {
|
||||
|
|
|
@ -433,6 +433,8 @@ interface TestExampleInterface {
|
|||
void dontEnforceRangeOrClamp(byte arg);
|
||||
void doEnforceRange([EnforceRange] byte arg);
|
||||
void doClamp([Clamp] byte arg);
|
||||
[EnforceRange] attribute byte enforcedByte;
|
||||
[Clamp] attribute byte clampedByte;
|
||||
|
||||
// Typedefs
|
||||
const myLong myLongConstant = 5;
|
||||
|
|
|
@ -458,6 +458,8 @@ interface TestJSImplInterface {
|
|||
void dontEnforceRangeOrClamp(byte arg);
|
||||
void doEnforceRange([EnforceRange] byte arg);
|
||||
void doClamp([Clamp] byte arg);
|
||||
[EnforceRange] attribute byte enforcedByte;
|
||||
[Clamp] attribute byte clampedByte;
|
||||
|
||||
// Typedefs
|
||||
const myLong myLongConstant = 5;
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EventTarget.h"
|
||||
#include "mozilla/dom/EventListenerBinding.h"
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
using mozilla::ErrorResult;
|
||||
using namespace mozilla::dom;
|
||||
using mozilla::dom::EventListener;
|
||||
using mozilla::dom::Nullable;
|
||||
|
||||
void
|
||||
EventTarget::_trace(JSTracer* aTrc)
|
||||
|
@ -58,7 +60,7 @@ EventTarget::SetEventListener(const nsAString& aType,
|
|||
|
||||
void
|
||||
EventTarget::AddEventListener(const nsAString& aType,
|
||||
JS::Handle<JSObject*> aListener,
|
||||
EventListener* aListener,
|
||||
bool aCapturing, Nullable<bool> aWantsUntrusted,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
|
@ -77,13 +79,13 @@ EventTarget::AddEventListener(const nsAString& aType,
|
|||
|
||||
bool wantsUntrusted = !aWantsUntrusted.IsNull() && aWantsUntrusted.Value();
|
||||
mListenerManager.AddEventListener(cx, INTERNED_STRING_TO_JSID(cx, type),
|
||||
aListener, aCapturing, wantsUntrusted,
|
||||
aRv);
|
||||
aListener->Callback(), aCapturing,
|
||||
wantsUntrusted, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
EventTarget::RemoveEventListener(const nsAString& aType,
|
||||
JS::Handle<JSObject*> aListener,
|
||||
EventListener* aListener,
|
||||
bool aCapturing, ErrorResult& aRv)
|
||||
{
|
||||
if (!aListener) {
|
||||
|
@ -100,5 +102,5 @@ EventTarget::RemoveEventListener(const nsAString& aType,
|
|||
}
|
||||
|
||||
mListenerManager.RemoveEventListener(cx, INTERNED_STRING_TO_JSID(cx, type),
|
||||
aListener, aCapturing);
|
||||
aListener->Callback(), aCapturing);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#include "mozilla/dom/Nullable.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class EventListener;
|
||||
} // namespace mozilla
|
||||
} // namespace dom
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
|
@ -37,12 +42,12 @@ public:
|
|||
_finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
|
||||
|
||||
void
|
||||
AddEventListener(const nsAString& aType, JS::Handle<JSObject*> aListener,
|
||||
AddEventListener(const nsAString& aType, EventListener* aListener,
|
||||
bool aCapture, Nullable<bool> aWantsUntrusted,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
RemoveEventListener(const nsAString& aType, JS::Handle<JSObject*> aListener,
|
||||
RemoveEventListener(const nsAString& aType, EventListener* aListener,
|
||||
bool aCapture, ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "mozilla/Util.h"
|
||||
|
||||
#include "Events.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
@ -16,15 +17,9 @@
|
|||
#include "WorkerInlines.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#define PROPERTY_FLAGS \
|
||||
(JSPROP_ENUMERATE | JSPROP_SHARED)
|
||||
|
||||
#define FUNCTION_FLAGS \
|
||||
JSPROP_ENUMERATE
|
||||
|
||||
#define CONSTANT_FLAGS \
|
||||
JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY
|
||||
|
||||
using namespace mozilla;
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
|
@ -37,7 +32,7 @@ class Event : public PrivatizableBase
|
|||
|
||||
static const JSPropertySpec sProperties[];
|
||||
static const JSFunctionSpec sFunctions[];
|
||||
static const JSPropertySpec sStaticProperties[];
|
||||
static const dom::ConstantSpec sStaticConstants[];
|
||||
|
||||
protected:
|
||||
bool mStopPropagationCalled;
|
||||
|
@ -77,9 +72,18 @@ public:
|
|||
JSClass* clasp = parentProto ? &sMainRuntimeClass : &sClass;
|
||||
|
||||
JS::Rooted<JSObject*> proto(aCx, JS_InitClass(aCx, aObj, parentProto, clasp, Construct, 0,
|
||||
sProperties, sFunctions, sStaticProperties,
|
||||
nullptr));
|
||||
if (proto && !JS_DefineProperties(aCx, proto, sStaticProperties)) {
|
||||
sProperties, sFunctions, nullptr, nullptr));
|
||||
if (!proto) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> ctor(aCx, JS_GetConstructor(aCx, proto));
|
||||
if (!ctor) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!dom::DefineConstants(aCx, ctor, sStaticConstants) ||
|
||||
!dom::DefineConstants(aCx, proto, sStaticConstants)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -145,7 +149,7 @@ protected:
|
|||
MOZ_COUNT_DTOR(mozilla::dom::workers::Event);
|
||||
}
|
||||
|
||||
enum {
|
||||
enum EventPhase {
|
||||
CAPTURING_PHASE = 1,
|
||||
AT_TARGET = 2,
|
||||
BUBBLING_PHASE = 3
|
||||
|
@ -220,34 +224,34 @@ private:
|
|||
}
|
||||
|
||||
static bool
|
||||
GetProperty(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
IsEvent(JS::Handle<JS::Value> v)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
|
||||
int32_t slot = JSID_TO_INT(aIdval);
|
||||
|
||||
const char* name = sProperties[slot - SLOT_FIRST].name;
|
||||
if (!GetInstancePrivate(aCx, aObj, name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aVp.set(JS_GetReservedSlot(aObj, slot));
|
||||
return true;
|
||||
return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
|
||||
}
|
||||
|
||||
template<SLOT Slot>
|
||||
static bool
|
||||
GetConstant(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> idval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(idval));
|
||||
JS_ASSERT(JSID_TO_INT(idval) >= CAPTURING_PHASE &&
|
||||
JSID_TO_INT(idval) <= BUBBLING_PHASE);
|
||||
|
||||
aVp.set(INT_TO_JSVAL(JSID_TO_INT(idval)));
|
||||
aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
|
||||
return true;
|
||||
}
|
||||
|
||||
// This struct (versus just templating the method directly) is needed only for
|
||||
// gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
|
||||
// GetProperty<Slot> and friends in the JSPropertySpec[] below.
|
||||
template<SLOT Slot>
|
||||
struct Property
|
||||
{
|
||||
static bool
|
||||
Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
static_assert(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsEvent, GetPropertyImpl<Slot> >(aCx, args);
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
StopPropagation(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
|
@ -343,25 +347,25 @@ DECL_EVENT_CLASS(Event::sMainRuntimeClass, "WorkerEvent")
|
|||
#undef DECL_EVENT_CLASS
|
||||
|
||||
const JSPropertySpec Event::sProperties[] = {
|
||||
{ "type", SLOT_type, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "target", SLOT_target, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "currentTarget", SLOT_currentTarget, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "eventPhase", SLOT_eventPhase, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "bubbles", SLOT_bubbles, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "cancelable", SLOT_cancelable, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "timeStamp", SLOT_timeStamp, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "defaultPrevented", SLOT_defaultPrevented, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "isTrusted", SLOT_isTrusted, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
JS_PSGS("type", Property<SLOT_type>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("target", Property<SLOT_target>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("currentTarget", Property<SLOT_currentTarget>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("eventPhase", Property<SLOT_eventPhase>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("bubbles", Property<SLOT_bubbles>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("cancelable", Property<SLOT_cancelable>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("timeStamp", Property<SLOT_timeStamp>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("defaultPrevented", Property<SLOT_defaultPrevented>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("isTrusted", Property<SLOT_isTrusted>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Event::sFunctions[] = {
|
||||
|
@ -372,13 +376,11 @@ const JSFunctionSpec Event::sFunctions[] = {
|
|||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSPropertySpec Event::sStaticProperties[] = {
|
||||
{ "CAPTURING_PHASE", CAPTURING_PHASE, CONSTANT_FLAGS,
|
||||
JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
|
||||
{ "AT_TARGET", AT_TARGET, CONSTANT_FLAGS, JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
|
||||
{ "BUBBLING_PHASE", BUBBLING_PHASE, CONSTANT_FLAGS,
|
||||
JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
const dom::ConstantSpec Event::sStaticConstants[] = {
|
||||
{ "CAPTURING_PHASE", JS::Int32Value(CAPTURING_PHASE) },
|
||||
{ "AT_TARGET", JS::Int32Value(AT_TARGET) },
|
||||
{ "BUBBLING_PHASE", JS::Int32Value(BUBBLING_PHASE) },
|
||||
{ nullptr, JS::UndefinedValue() }
|
||||
};
|
||||
|
||||
class MessageEvent : public Event
|
||||
|
@ -507,23 +509,27 @@ private:
|
|||
}
|
||||
|
||||
static bool
|
||||
GetProperty(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
IsMessageEvent(JS::Handle<JS::Value> v)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
|
||||
int32_t slot = JSID_TO_INT(aIdval);
|
||||
|
||||
JS_ASSERT(slot >= SLOT_data && slot < SLOT_COUNT);
|
||||
|
||||
const char* name = sProperties[slot - SLOT_FIRST].name;
|
||||
MessageEvent* event = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!event) {
|
||||
if (!v.isObject())
|
||||
return false;
|
||||
}
|
||||
JSObject* obj = &v.toObject();
|
||||
return IsThisClass(JS_GetClass(obj)) &&
|
||||
GetJSPrivateSafeish<MessageEvent>(obj) != nullptr;
|
||||
}
|
||||
|
||||
template<SLOT Slot>
|
||||
static bool
|
||||
GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
|
||||
const char* name = sProperties[Slot - SLOT_FIRST].name;
|
||||
MessageEvent* event = GetInstancePrivate(aCx, obj, name);
|
||||
MOZ_ASSERT(event);
|
||||
|
||||
// Deserialize and save the data value if we can.
|
||||
if (slot == SLOT_data && event->mBuffer.data()) {
|
||||
if (Slot == SLOT_data && event->mBuffer.data()) {
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
buffer.swap(event->mBuffer);
|
||||
|
||||
|
@ -537,16 +543,31 @@ private:
|
|||
WorkerStructuredCloneCallbacks(event->mMainRuntime))) {
|
||||
return false;
|
||||
}
|
||||
JS_SetReservedSlot(aObj, slot, data);
|
||||
JS_SetReservedSlot(obj, Slot, data);
|
||||
|
||||
aVp.set(data);
|
||||
aArgs.rval().set(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
aVp.set(JS_GetReservedSlot(aObj, slot));
|
||||
aArgs.rval().set(JS_GetReservedSlot(obj, Slot));
|
||||
return true;
|
||||
}
|
||||
|
||||
// This struct (versus just templating the method directly) is needed only for
|
||||
// gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
|
||||
// GetProperty<Slot> and friends in the JSPropertySpec[] below.
|
||||
template<SLOT Slot>
|
||||
struct Property
|
||||
{
|
||||
static bool
|
||||
Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
static_assert(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsMessageEvent, GetPropertyImpl<Slot> >(aCx, args);
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
InitMessageEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
|
@ -589,13 +610,13 @@ DECL_MESSAGEEVENT_CLASS(MessageEvent::sMainRuntimeClass, "WorkerMessageEvent")
|
|||
#undef DECL_MESSAGEEVENT_CLASS
|
||||
|
||||
const JSPropertySpec MessageEvent::sProperties[] = {
|
||||
{ "data", SLOT_data, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "origin", SLOT_origin, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "source", SLOT_source, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
JS_PSGS("data", Property<SLOT_data>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("origin", Property<SLOT_origin>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("source", Property<SLOT_source>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec MessageEvent::sFunctions[] = {
|
||||
|
@ -715,25 +736,38 @@ private:
|
|||
}
|
||||
|
||||
static bool
|
||||
GetProperty(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
IsErrorEvent(JS::Handle<JS::Value> v)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
|
||||
int32_t slot = JSID_TO_INT(aIdval);
|
||||
|
||||
JS_ASSERT(slot >= SLOT_message && slot < SLOT_COUNT);
|
||||
|
||||
const char* name = sProperties[slot - SLOT_FIRST].name;
|
||||
ErrorEvent* event = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!event) {
|
||||
if (!v.isObject())
|
||||
return false;
|
||||
}
|
||||
JSObject* obj = &v.toObject();
|
||||
return IsThisClass(JS_GetClass(obj)) &&
|
||||
GetJSPrivateSafeish<ErrorEvent>(obj) != nullptr;
|
||||
}
|
||||
|
||||
aVp.set(JS_GetReservedSlot(aObj, slot));
|
||||
template<SLOT Slot>
|
||||
static bool
|
||||
GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
|
||||
return true;
|
||||
}
|
||||
|
||||
// This struct (versus just templating the method directly) is needed only for
|
||||
// gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
|
||||
// GetProperty<Slot> and friends in the JSPropertySpec[] below.
|
||||
template<SLOT Slot>
|
||||
struct Property
|
||||
{
|
||||
static bool
|
||||
Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
static_assert(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsErrorEvent, GetPropertyImpl<Slot> >(aCx, args);
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
InitErrorEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
|
@ -776,13 +810,13 @@ DECL_ERROREVENT_CLASS(ErrorEvent::sMainRuntimeClass, "WorkerErrorEvent")
|
|||
#undef DECL_ERROREVENT_CLASS
|
||||
|
||||
const JSPropertySpec ErrorEvent::sProperties[] = {
|
||||
{ "message", SLOT_message, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "filename", SLOT_filename, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "lineno", SLOT_lineno, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
JS_PSGS("message", Property<SLOT_message>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("filename", Property<SLOT_filename>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("lineno", Property<SLOT_lineno>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec ErrorEvent::sFunctions[] = {
|
||||
|
@ -895,24 +929,37 @@ private:
|
|||
}
|
||||
|
||||
static bool
|
||||
GetProperty(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
IsProgressEvent(JS::Handle<JS::Value> v)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
|
||||
int32_t slot = JSID_TO_INT(aIdval);
|
||||
|
||||
JS_ASSERT(slot >= SLOT_lengthComputable && slot < SLOT_COUNT);
|
||||
|
||||
const char* name = sProperties[slot - SLOT_FIRST].name;
|
||||
ProgressEvent* event = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!event) {
|
||||
if (!v.isObject())
|
||||
return false;
|
||||
}
|
||||
JSObject* obj = &v.toObject();
|
||||
return JS_GetClass(obj) == &sClass &&
|
||||
GetJSPrivateSafeish<ProgressEvent>(obj) != nullptr;
|
||||
}
|
||||
|
||||
aVp.set(JS_GetReservedSlot(aObj, slot));
|
||||
template<SLOT Slot>
|
||||
static bool
|
||||
GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
|
||||
return true;
|
||||
}
|
||||
|
||||
// This struct (versus just templating the method directly) is needed only for
|
||||
// gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
|
||||
// GetProperty<Slot> and friends in the JSPropertySpec[] below.
|
||||
template<SLOT Slot>
|
||||
struct Property
|
||||
{
|
||||
static bool
|
||||
Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
static_assert(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsProgressEvent, GetPropertyImpl<Slot> >(aCx, args);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
JSClass ProgressEvent::sClass = {
|
||||
|
@ -923,13 +970,13 @@ JSClass ProgressEvent::sClass = {
|
|||
};
|
||||
|
||||
const JSPropertySpec ProgressEvent::sProperties[] = {
|
||||
{ "lengthComputable", SLOT_lengthComputable, PROPERTY_FLAGS,
|
||||
JSOP_WRAPPER(GetProperty), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "loaded", SLOT_loaded, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "total", SLOT_total, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
JS_PSGS("lengthComputable", Property<SLOT_lengthComputable>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("loaded", Property<SLOT_loaded>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("total", Property<SLOT_total>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
Event*
|
||||
|
|
|
@ -20,9 +20,6 @@
|
|||
#include "WorkerInlines.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#define PROPERTY_FLAGS \
|
||||
(JSPROP_ENUMERATE | JSPROP_SHARED)
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
using mozilla::dom::Throw;
|
||||
|
||||
|
@ -111,32 +108,40 @@ private:
|
|||
}
|
||||
|
||||
static bool
|
||||
GetSize(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
IsBlob(JS::Handle<JS::Value> v)
|
||||
{
|
||||
nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "size");
|
||||
if (!blob) {
|
||||
return false;
|
||||
}
|
||||
return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetSizeImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "size");
|
||||
MOZ_ASSERT(blob);
|
||||
|
||||
uint64_t size;
|
||||
if (NS_FAILED(blob->GetSize(&size))) {
|
||||
return Throw(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR);
|
||||
}
|
||||
|
||||
aVp.set(JS_NumberValue(double(size)));
|
||||
|
||||
aArgs.rval().setNumber(double(size));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetType(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
GetSize(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "type");
|
||||
if (!blob) {
|
||||
return false;
|
||||
}
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsBlob, GetSizeImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetTypeImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "type");
|
||||
MOZ_ASSERT(blob);
|
||||
|
||||
nsString type;
|
||||
if (NS_FAILED(blob->GetType(type))) {
|
||||
|
@ -148,11 +153,17 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
aVp.set(STRING_TO_JSVAL(jsType));
|
||||
|
||||
aArgs.rval().setString(jsType);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetType(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsBlob, GetTypeImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
Slice(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
|
@ -205,9 +216,9 @@ JSClass Blob::sClass = {
|
|||
};
|
||||
|
||||
const JSPropertySpec Blob::sProperties[] = {
|
||||
{ "size", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetSize), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "type", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetType), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
JS_PSGS("size", GetSize, GetterOnlyJSNative, JSPROP_ENUMERATE),
|
||||
JS_PSGS("type", GetType, GetterOnlyJSNative, JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Blob::sFunctions[] = {
|
||||
|
@ -299,13 +310,17 @@ private:
|
|||
}
|
||||
|
||||
static bool
|
||||
GetMozFullPath(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
IsFile(JS::Handle<JS::Value> v)
|
||||
{
|
||||
nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "mozFullPath");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetMozFullPathImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
nsIDOMFile* file = GetInstancePrivate(aCx, obj, "mozFullPath");
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
nsString fullPath;
|
||||
|
||||
|
@ -320,18 +335,23 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
aVp.set(STRING_TO_JSVAL(jsFullPath));
|
||||
aArgs.rval().setString(jsFullPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetName(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
GetMozFullPath(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "name");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsFile, GetMozFullPathImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetNameImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
nsIDOMFile* file = GetInstancePrivate(aCx, obj, "name");
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
nsString name;
|
||||
if (NS_FAILED(file->GetName(name))) {
|
||||
|
@ -343,18 +363,23 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
aVp.set(STRING_TO_JSVAL(jsName));
|
||||
aArgs.rval().setString(jsName);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetPath(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
GetName(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "path");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsFile, GetNameImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetPathImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
nsIDOMFile* file = GetInstancePrivate(aCx, obj, "path");
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
nsString path;
|
||||
if (NS_FAILED(file->GetPath(path))) {
|
||||
|
@ -366,27 +391,39 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
aVp.set(STRING_TO_JSVAL(jsPath));
|
||||
aArgs.rval().setString(jsPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetLastModifiedDate(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
GetPath(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "lastModifiedDate");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsFile, GetPathImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetLastModifiedDateImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
nsIDOMFile* file = GetInstancePrivate(aCx, obj, "lastModifiedDate");
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (NS_FAILED(file->GetLastModifiedDate(aCx, value.address()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aVp.set(value);
|
||||
aArgs.rval().set(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetLastModifiedDate(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsFile, GetLastModifiedDateImpl>(aCx, args);
|
||||
}
|
||||
};
|
||||
|
||||
JSClass File::sClass = {
|
||||
|
@ -397,15 +434,12 @@ JSClass File::sClass = {
|
|||
};
|
||||
|
||||
const JSPropertySpec File::sProperties[] = {
|
||||
{ "name", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetName),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "path", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetPath),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "lastModifiedDate", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetLastModifiedDate),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "mozFullPath", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetMozFullPath),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
JS_PSGS("name", GetName, GetterOnlyJSNative, JSPROP_ENUMERATE),
|
||||
JS_PSGS("path", GetPath, GetterOnlyJSNative, JSPROP_ENUMERATE),
|
||||
JS_PSGS("lastModifiedDate", GetLastModifiedDate, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("mozFullPath", GetMozFullPath, GetterOnlyJSNative, JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
nsIDOMBlob*
|
||||
|
|
|
@ -1511,6 +1511,9 @@ RuntimeService::Init()
|
|||
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mObserved = true;
|
||||
|
||||
if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) {
|
||||
|
@ -1598,13 +1601,15 @@ RuntimeService::Init()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// This spins the event loop until all workers are finished and their threads
|
||||
// have been joined.
|
||||
void
|
||||
RuntimeService::Cleanup()
|
||||
RuntimeService::Shutdown()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
// That's it, no more workers.
|
||||
mShuttingDown = true;
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_WARN_IF_FALSE(obs, "Failed to get observer service?!");
|
||||
|
||||
|
@ -1614,8 +1619,40 @@ RuntimeService::Cleanup()
|
|||
NS_WARNING("NotifyObservers failed!");
|
||||
}
|
||||
|
||||
// That's it, no more workers.
|
||||
mShuttingDown = true;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
nsAutoTArray<WorkerPrivate*, 100> workers;
|
||||
mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers);
|
||||
|
||||
if (!workers.IsEmpty()) {
|
||||
|
||||
// Cancel all top-level workers.
|
||||
{
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
for (uint32_t index = 0; index < workers.Length(); index++) {
|
||||
if (!workers[index]->Kill(cx)) {
|
||||
NS_WARNING("Failed to cancel worker!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This spins the event loop until all workers are finished and their threads
|
||||
// have been joined.
|
||||
void
|
||||
RuntimeService::Cleanup()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_WARN_IF_FALSE(obs, "Failed to get observer service?!");
|
||||
|
||||
if (mIdleThreadTimer) {
|
||||
if (NS_FAILED(mIdleThreadTimer->Cancel())) {
|
||||
|
@ -1631,24 +1668,8 @@ RuntimeService::Cleanup()
|
|||
mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers);
|
||||
|
||||
if (!workers.IsEmpty()) {
|
||||
nsIThread* currentThread;
|
||||
|
||||
// Cancel all top-level workers.
|
||||
{
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
|
||||
currentThread = NS_GetCurrentThread();
|
||||
NS_ASSERTION(currentThread, "This should never be null!");
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
for (uint32_t index = 0; index < workers.Length(); index++) {
|
||||
if (!workers[index]->Kill(cx)) {
|
||||
NS_WARNING("Failed to cancel worker!");
|
||||
}
|
||||
}
|
||||
}
|
||||
nsIThread* currentThread = NS_GetCurrentThread();
|
||||
NS_ASSERTION(currentThread, "This should never be null!");
|
||||
|
||||
// Shut down any idle threads.
|
||||
if (!mIdleThreadArray.IsEmpty()) {
|
||||
|
@ -1734,9 +1755,9 @@ RuntimeService::Cleanup()
|
|||
NS_WARNING("Failed to unregister for memory pressure notifications!");
|
||||
}
|
||||
|
||||
nsresult rv =
|
||||
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
|
||||
mObserved = NS_FAILED(rv);
|
||||
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
|
||||
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
mObserved = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1935,6 +1956,10 @@ RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
|
||||
Cleanup();
|
||||
return NS_OK;
|
||||
|
|
|
@ -208,6 +208,9 @@ private:
|
|||
nsresult
|
||||
Init();
|
||||
|
||||
void
|
||||
Shutdown();
|
||||
|
||||
void
|
||||
Cleanup();
|
||||
|
||||
|
|
|
@ -8,15 +8,13 @@
|
|||
#include "mozilla/dom/DOMJSClass.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "EventTarget.h"
|
||||
#include "RuntimeService.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#include "WorkerInlines.h"
|
||||
|
||||
#define PROPERTY_FLAGS \
|
||||
(JSPROP_ENUMERATE | JSPROP_SHARED)
|
||||
|
||||
#define FUNCTION_FLAGS \
|
||||
JSPROP_ENUMERATE
|
||||
|
||||
|
@ -34,16 +32,6 @@ class Worker
|
|||
static const JSPropertySpec sProperties[];
|
||||
static const JSFunctionSpec sFunctions[];
|
||||
|
||||
enum
|
||||
{
|
||||
STRING_onerror = 0,
|
||||
STRING_onmessage,
|
||||
|
||||
STRING_COUNT
|
||||
};
|
||||
|
||||
static const char* const sEventStrings[STRING_COUNT];
|
||||
|
||||
protected:
|
||||
enum {
|
||||
// The constructor function holds a WorkerPrivate* in its first reserved
|
||||
|
@ -176,60 +164,110 @@ private:
|
|||
~Worker();
|
||||
|
||||
static bool
|
||||
GetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
IsWorker(JS::Handle<JS::Value> v)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
|
||||
return v.isObject() && ClassIsWorker(JS_GetClass(&v.toObject()));
|
||||
}
|
||||
|
||||
const char* name = sEventStrings[JSID_TO_INT(aIdval)];
|
||||
WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!worker) {
|
||||
return !JS_IsExceptionPending(aCx);
|
||||
}
|
||||
static bool
|
||||
GetEventListener(JSContext* aCx, const JS::CallArgs aArgs,
|
||||
const nsAString &aNameStr)
|
||||
{
|
||||
WorkerPrivate* worker =
|
||||
GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
|
||||
NS_ConvertUTF16toUTF8(aNameStr).get());
|
||||
MOZ_ASSERT(worker);
|
||||
|
||||
NS_ConvertASCIItoUTF16 nameStr(name + 2);
|
||||
ErrorResult rv;
|
||||
JS::Rooted<JSObject*> listener(aCx, worker->GetEventListener(nameStr, rv));
|
||||
JS::Rooted<JSObject*> listener(aCx, worker->GetEventListener(Substring(aNameStr, 2), rv));
|
||||
|
||||
if (rv.Failed()) {
|
||||
JS_ReportError(aCx, "Failed to get listener!");
|
||||
return false;
|
||||
}
|
||||
|
||||
aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
|
||||
aArgs.rval().setObjectOrNull(listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
bool aStrict, JS::MutableHandle<JS::Value> aVp)
|
||||
GetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
|
||||
return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
|
||||
}
|
||||
|
||||
const char* name = sEventStrings[JSID_TO_INT(aIdval)];
|
||||
WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!worker) {
|
||||
return !JS_IsExceptionPending(aCx);
|
||||
}
|
||||
static bool
|
||||
GetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorker, GetOnerrorImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
|
||||
}
|
||||
|
||||
static bool
|
||||
GetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorker, GetOnmessageImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
SetEventListener(JSContext* aCx, JS::CallArgs aArgs,
|
||||
const nsAString& aNameStr)
|
||||
{
|
||||
WorkerPrivate* worker =
|
||||
GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
|
||||
NS_ConvertUTF16toUTF8(aNameStr).get());
|
||||
MOZ_ASSERT(worker);
|
||||
|
||||
JS::Rooted<JSObject*> listener(aCx);
|
||||
if (!JS_ValueToObject(aCx, aVp, listener.address())) {
|
||||
if (!JS_ValueToObject(aCx, aArgs.get(0), listener.address())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_ConvertASCIItoUTF16 nameStr(name + 2);
|
||||
ErrorResult rv;
|
||||
worker->SetEventListener(nameStr, listener, rv);
|
||||
worker->SetEventListener(Substring(aNameStr, 2), listener, rv);
|
||||
|
||||
if (rv.Failed()) {
|
||||
JS_ReportError(aCx, "Failed to set listener!");
|
||||
return false;
|
||||
}
|
||||
|
||||
aArgs.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
|
||||
}
|
||||
|
||||
static bool
|
||||
SetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorker, SetOnerrorImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
SetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
|
||||
}
|
||||
|
||||
static bool
|
||||
SetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorker, SetOnmessageImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
|
@ -355,11 +393,9 @@ DOMIfaceAndProtoJSClass Worker::sProtoClass = {
|
|||
};
|
||||
|
||||
const JSPropertySpec Worker::sProperties[] = {
|
||||
{ sEventStrings[STRING_onerror], STRING_onerror, PROPERTY_FLAGS,
|
||||
JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
|
||||
{ sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
|
||||
JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
JS_PSGS("onerror", GetOnerror, SetOnerror, JSPROP_ENUMERATE),
|
||||
JS_PSGS("onmessage", GetOnmessage, SetOnmessage, JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Worker::sFunctions[] = {
|
||||
|
@ -368,11 +404,6 @@ const JSFunctionSpec Worker::sFunctions[] = {
|
|||
JS_FS_END
|
||||
};
|
||||
|
||||
const char* const Worker::sEventStrings[STRING_COUNT] = {
|
||||
"onerror",
|
||||
"onmessage"
|
||||
};
|
||||
|
||||
class ChromeWorker : public Worker
|
||||
{
|
||||
static DOMJSClass sClass;
|
||||
|
@ -522,7 +553,7 @@ Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
|
|||
const char* aFunctionName)
|
||||
{
|
||||
JSClass* classPtr = JS_GetClass(aObj);
|
||||
if (classPtr == Class() || classPtr == ChromeWorker::Class()) {
|
||||
if (ClassIsWorker(classPtr)) {
|
||||
return UnwrapDOMObject<WorkerPrivate>(aObj);
|
||||
}
|
||||
|
||||
|
@ -580,4 +611,11 @@ ClassIsWorker(JSClass* aClass)
|
|||
return Worker::Class() == aClass || ChromeWorker::Class() == aClass;
|
||||
}
|
||||
|
||||
bool
|
||||
GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
|
||||
return false;
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
|
|
@ -48,9 +48,6 @@
|
|||
|
||||
#include "WorkerInlines.h"
|
||||
|
||||
#define PROPERTY_FLAGS \
|
||||
(JSPROP_ENUMERATE | JSPROP_SHARED)
|
||||
|
||||
#define FUNCTION_FLAGS \
|
||||
JSPROP_ENUMERATE
|
||||
|
||||
|
@ -155,18 +152,14 @@ protected:
|
|||
}
|
||||
|
||||
private:
|
||||
static bool
|
||||
GetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
|
||||
static bool IsWorkerGlobalScope(JS::Handle<JS::Value> v);
|
||||
|
||||
const char* name = sEventStrings[JSID_TO_INT(aIdval)];
|
||||
WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!scope) {
|
||||
return false;
|
||||
}
|
||||
static bool
|
||||
GetOnCloseImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
const char* name = sEventStrings[STRING_onclose];
|
||||
WorkerGlobalScope* scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
|
@ -178,30 +171,32 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
|
||||
aArgs.rval().setObjectOrNull(listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
bool aStrict, JS::MutableHandle<JS::Value> aVp)
|
||||
GetOnClose(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetOnCloseImpl>(aCx, args);
|
||||
}
|
||||
|
||||
const char* name = sEventStrings[JSID_TO_INT(aIdval)];
|
||||
WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!scope) {
|
||||
return false;
|
||||
}
|
||||
static bool
|
||||
SetOnCloseImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
const char* name = sEventStrings[STRING_onclose];
|
||||
WorkerGlobalScope* scope =
|
||||
GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(aVp)) {
|
||||
if (aArgs.length() == 0 || !aArgs[0].isObject()) {
|
||||
JS_ReportError(aCx, "Not an event listener!");
|
||||
return false;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
JS::Rooted<JSObject*> listenerObj(aCx, JSVAL_TO_OBJECT(aVp));
|
||||
JS::Rooted<JSObject*> listenerObj(aCx, &aArgs[0].toObject());
|
||||
scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2),
|
||||
listenerObj, rv);
|
||||
if (rv.Failed()) {
|
||||
|
@ -209,9 +204,17 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
aArgs.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetOnClose(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorkerGlobalScope, SetOnCloseImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static WorkerGlobalScope*
|
||||
GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
|
||||
|
||||
|
@ -224,32 +227,32 @@ private:
|
|||
}
|
||||
|
||||
static bool
|
||||
GetSelf(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
GetSelfImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
if (!GetInstancePrivate(aCx, aObj, "self")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aVp.set(OBJECT_TO_JSVAL(aObj));
|
||||
aArgs.rval().setObject(aArgs.thisv().toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetLocation(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
GetSelf(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
WorkerGlobalScope* scope =
|
||||
GetInstancePrivate(aCx, aObj, sProperties[SLOT_location].name);
|
||||
if (!scope) {
|
||||
return false;
|
||||
}
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetSelfImpl>(aCx, args);
|
||||
}
|
||||
|
||||
if (JSVAL_IS_VOID(scope->mSlots[SLOT_location])) {
|
||||
static bool
|
||||
GetLocationImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
WorkerGlobalScope* scope =
|
||||
GetInstancePrivate(aCx, obj, sProperties[SLOT_location].name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
if (scope->mSlots[SLOT_location].isUndefined()) {
|
||||
WorkerPrivate::LocationInfo& info = scope->mWorker->GetLocationInfo();
|
||||
|
||||
nsRefPtr<WorkerLocation> location =
|
||||
WorkerLocation::Create(aCx, aObj, info);
|
||||
WorkerLocation::Create(aCx, obj, info);
|
||||
if (!location) {
|
||||
return false;
|
||||
}
|
||||
|
@ -257,10 +260,17 @@ private:
|
|||
scope->mSlots[SLOT_location] = OBJECT_TO_JSVAL(location->GetJSObject());
|
||||
}
|
||||
|
||||
aVp.set(scope->mSlots[SLOT_location]);
|
||||
aArgs.rval().set(scope->mSlots[SLOT_location]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetLocation(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetLocationImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
UnwrapErrorEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
|
@ -301,14 +311,11 @@ private:
|
|||
}
|
||||
|
||||
static bool
|
||||
GetOnErrorListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
GetOnErrorListenerImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
const char* name = sEventStrings[STRING_onerror];
|
||||
WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!scope) {
|
||||
return false;
|
||||
}
|
||||
WorkerGlobalScope* scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
|
@ -321,28 +328,31 @@ private:
|
|||
}
|
||||
|
||||
if (!adaptor) {
|
||||
aVp.setNull();
|
||||
aArgs.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
aVp.set(js::GetFunctionNativeReserved(adaptor, SLOT_wrappedFunction));
|
||||
|
||||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(aVp));
|
||||
|
||||
aArgs.rval().set(js::GetFunctionNativeReserved(adaptor, SLOT_wrappedFunction));
|
||||
MOZ_ASSERT(aArgs.rval().isObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetOnErrorListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
bool aStrict, JS::MutableHandle<JS::Value> aVp)
|
||||
GetOnErrorListener(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
const char* name = sEventStrings[STRING_onerror];
|
||||
WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!scope) {
|
||||
return false;
|
||||
}
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetOnErrorListenerImpl>(aCx, args);
|
||||
}
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(aVp)) {
|
||||
static bool
|
||||
SetOnErrorListenerImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
const char* name = sEventStrings[STRING_onerror];
|
||||
WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
if (aArgs.length() == 0 || !aArgs[0].isObject()) {
|
||||
JS_ReportError(aCx, "Not an event listener!");
|
||||
return false;
|
||||
}
|
||||
|
@ -360,8 +370,8 @@ private:
|
|||
}
|
||||
|
||||
js::SetFunctionNativeReserved(listener, SLOT_wrappedScope,
|
||||
OBJECT_TO_JSVAL(aObj));
|
||||
js::SetFunctionNativeReserved(listener, SLOT_wrappedFunction, aVp);
|
||||
JS::ObjectValue(*obj));
|
||||
js::SetFunctionNativeReserved(listener, SLOT_wrappedFunction, aArgs[0]);
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
|
@ -372,21 +382,27 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
aArgs.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetNavigator(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
SetOnErrorListener(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
WorkerGlobalScope* scope =
|
||||
GetInstancePrivate(aCx, aObj, sProperties[SLOT_navigator].name);
|
||||
if (!scope) {
|
||||
return false;
|
||||
}
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorkerGlobalScope, SetOnErrorListenerImpl>(aCx, args);
|
||||
}
|
||||
|
||||
if (JSVAL_IS_VOID(scope->mSlots[SLOT_navigator])) {
|
||||
nsRefPtr<WorkerNavigator> navigator = WorkerNavigator::Create(aCx, aObj);
|
||||
static bool
|
||||
GetNavigatorImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
|
||||
WorkerGlobalScope* scope =
|
||||
GetInstancePrivate(aCx, obj, sProperties[SLOT_navigator].name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
if (scope->mSlots[SLOT_navigator].isUndefined()) {
|
||||
nsRefPtr<WorkerNavigator> navigator = WorkerNavigator::Create(aCx, obj);
|
||||
if (!navigator) {
|
||||
return false;
|
||||
}
|
||||
|
@ -394,10 +410,17 @@ private:
|
|||
scope->mSlots[SLOT_navigator] = OBJECT_TO_JSVAL(navigator->GetJSObject());
|
||||
}
|
||||
|
||||
aVp.set(scope->mSlots[SLOT_navigator]);
|
||||
aArgs.rval().set(scope->mSlots[SLOT_navigator]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetNavigator(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetNavigatorImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
Close(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
|
@ -637,16 +660,14 @@ JSClass WorkerGlobalScope::sClass = {
|
|||
};
|
||||
|
||||
const JSPropertySpec WorkerGlobalScope::sProperties[] = {
|
||||
{ "location", SLOT_location, PROPERTY_FLAGS, JSOP_WRAPPER(GetLocation),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ sEventStrings[STRING_onerror], STRING_onerror, PROPERTY_FLAGS,
|
||||
JSOP_WRAPPER(GetOnErrorListener), JSOP_WRAPPER(SetOnErrorListener) },
|
||||
{ sEventStrings[STRING_onclose], STRING_onclose, PROPERTY_FLAGS,
|
||||
JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
|
||||
{ "navigator", SLOT_navigator, PROPERTY_FLAGS, JSOP_WRAPPER(GetNavigator),
|
||||
JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ "self", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetSelf), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
JS_PSGS("location", GetLocation, GetterOnlyJSNative, JSPROP_ENUMERATE),
|
||||
JS_PSGS(sEventStrings[STRING_onerror], GetOnErrorListener, SetOnErrorListener,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS(sEventStrings[STRING_onclose], GetOnClose, SetOnClose,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("navigator", GetNavigator, GetterOnlyJSNative, JSPROP_ENUMERATE),
|
||||
JS_PSGS("self", GetSelf, GetterOnlyJSNative, JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec WorkerGlobalScope::sFunctions[] = {
|
||||
|
@ -751,17 +772,18 @@ private:
|
|||
using EventTarget::SetEventListener;
|
||||
|
||||
static bool
|
||||
GetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
JS::MutableHandle<JS::Value> aVp)
|
||||
IsDedicatedWorkerGlobalScope(JS::Handle<JS::Value> v)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
|
||||
return v.isObject() && JS_GetClass(&v.toObject()) == Class();
|
||||
}
|
||||
|
||||
const char* name = sEventStrings[JSID_TO_INT(aIdval)];
|
||||
DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!scope) {
|
||||
return false;
|
||||
}
|
||||
static bool
|
||||
GetOnMessageImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
const char* name = sEventStrings[STRING_onmessage];
|
||||
DedicatedWorkerGlobalScope* scope =
|
||||
GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
|
@ -773,31 +795,33 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
|
||||
aArgs.rval().setObjectOrNull(listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetEventListener(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aIdval,
|
||||
bool aStrict, JS::MutableHandle<JS::Value> aVp)
|
||||
GetOnMessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS_ASSERT(JSID_IS_INT(aIdval));
|
||||
JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsDedicatedWorkerGlobalScope, GetOnMessageImpl>(aCx, args);
|
||||
}
|
||||
|
||||
const char* name = sEventStrings[JSID_TO_INT(aIdval)];
|
||||
DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
|
||||
if (!scope) {
|
||||
return false;
|
||||
}
|
||||
static bool
|
||||
SetOnMessageImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
const char* name = sEventStrings[STRING_onmessage];
|
||||
DedicatedWorkerGlobalScope* scope =
|
||||
GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(aVp)) {
|
||||
if (aArgs.length() == 0 || !aArgs[0].isObject()) {
|
||||
JS_ReportError(aCx, "Not an event listener!");
|
||||
return false;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
JS::Rooted<JSObject*> listenerObj(aCx, JSVAL_TO_OBJECT(aVp));
|
||||
JS::Rooted<JSObject*> listenerObj(aCx, &aArgs[0].toObject());
|
||||
scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2),
|
||||
listenerObj, rv);
|
||||
|
||||
|
@ -806,9 +830,17 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
aArgs.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetOnMessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsDedicatedWorkerGlobalScope, SetOnMessageImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static DedicatedWorkerGlobalScope*
|
||||
GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
|
||||
{
|
||||
|
@ -947,9 +979,9 @@ DOMIfaceAndProtoJSClass DedicatedWorkerGlobalScope::sProtoClass = {
|
|||
};
|
||||
|
||||
const JSPropertySpec DedicatedWorkerGlobalScope::sProperties[] = {
|
||||
{ sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
|
||||
JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
|
||||
{ 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
|
||||
JS_PSGS(sEventStrings[STRING_onmessage], GetOnMessage, SetOnMessage,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec DedicatedWorkerGlobalScope::sFunctions[] = {
|
||||
|
@ -980,6 +1012,12 @@ WorkerGlobalScope::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> v)
|
||||
{
|
||||
return v.isObject() && JS_GetClass(&v.toObject()) == DedicatedWorkerGlobalScope::Class();
|
||||
}
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
|
|
@ -225,6 +225,14 @@ ThrowDOMExceptionForNSResult(JSContext* aCx, nsresult aNSResult);
|
|||
|
||||
} // namespace exceptions
|
||||
|
||||
// Throws the JSMSG_GETTER_ONLY exception. This shouldn't be used going
|
||||
// forward -- getter-only properties should just use JS_PSG for the setter
|
||||
// (implying no setter at all), which will not throw when set in non-strict
|
||||
// code but will in strict code. Old code should use this only for temporary
|
||||
// compatibility reasons.
|
||||
extern bool
|
||||
GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_workers_h__
|
||||
|
|
|
@ -480,11 +480,9 @@ BasicCompositor::EndFrame()
|
|||
// Most platforms require us to buffer drawing to the widget surface.
|
||||
// That's why we don't draw to mDrawTarget directly.
|
||||
RefPtr<SourceSurface> source = mRenderTarget->mDrawTarget->Snapshot();
|
||||
mDrawTarget->DrawSurface(source,
|
||||
Rect(0, 0, mWidgetSize.width, mWidgetSize.height),
|
||||
Rect(0, 0, mWidgetSize.width, mWidgetSize.height),
|
||||
DrawSurfaceOptions(),
|
||||
DrawOptions());
|
||||
mDrawTarget->CopySurface(source,
|
||||
IntRect(0, 0, mWidgetSize.width, mWidgetSize.height),
|
||||
IntPoint(0, 0));
|
||||
mWidget->EndRemoteDrawing();
|
||||
}
|
||||
mDrawTarget = nullptr;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _NS_APPUNITS_H_
|
||||
#define _NS_APPUNITS_H_
|
||||
namespace mozilla {
|
||||
static int32_t AppUnitsPerCSSPixel() { return 60; }
|
||||
}
|
||||
#endif /* _NS_APPUNITS_H_ */
|
|
@ -34,6 +34,10 @@ EXPORTS += [
|
|||
'nsTransform2D.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'AppUnits.h',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_X11']:
|
||||
EXPORTS.mozilla += ['X11Util.h']
|
||||
CPP_SOURCES += [
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING
|
||||
#include "nsMathUtils.h" // for NS_round
|
||||
#include "nscore.h" // for PRUnichar, nsAString
|
||||
#include "mozilla/AppUnits.h" // for AppUnits
|
||||
|
||||
class gfxASurface;
|
||||
class gfxUserFontSet;
|
||||
|
@ -65,7 +66,7 @@ public:
|
|||
* Gets the number of app units in one CSS pixel; this number is global,
|
||||
* not unique to each device context.
|
||||
*/
|
||||
static int32_t AppUnitsPerCSSPixel() { return 60; }
|
||||
static int32_t AppUnitsPerCSSPixel() { return mozilla::AppUnitsPerCSSPixel(); }
|
||||
|
||||
/**
|
||||
* Gets the number of app units in one device pixel; this number
|
||||
|
|
|
@ -1273,6 +1273,14 @@ DoubleValue(double dbl)
|
|||
return v;
|
||||
}
|
||||
|
||||
static inline Value
|
||||
Float32Value(float f)
|
||||
{
|
||||
Value v;
|
||||
v.setDouble(f);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Value
|
||||
StringValue(JSString *str)
|
||||
{
|
||||
|
|
|
@ -2180,6 +2180,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void addss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("addss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void addsd_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("addsd %s0x%x(%s), %s",
|
||||
|
@ -2188,6 +2196,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void addss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("addss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void addsd_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
|
@ -2196,6 +2212,13 @@ public:
|
|||
m_formatter.prefix(PRE_SSE_F2);
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address);
|
||||
}
|
||||
void addss_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
spew("addss %p, %s",
|
||||
address, nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address);
|
||||
}
|
||||
#endif
|
||||
|
||||
void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
|
@ -2214,6 +2237,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_CVTSD2SS_VsdEd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void cvtsi2ss_rr(RegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("cvtsi2ss %s, %s",
|
||||
nameIReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src);
|
||||
}
|
||||
|
||||
void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("cvtsi2sd %s, %s",
|
||||
|
@ -2248,6 +2279,22 @@ public:
|
|||
m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, index, scale, offset);
|
||||
}
|
||||
|
||||
void cvtsi2ss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("cvtsi2ss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void cvtsi2ss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
|
||||
{
|
||||
spew("cvtsi2ss %d(%s,%s,%d), %s",
|
||||
offset, nameIReg(base), nameIReg(index), 1<<scale, nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, index, scale, offset);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void cvtsi2sd_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
|
@ -2266,6 +2313,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void cvttss2si_rr(XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("cvttss2si %s, %s",
|
||||
nameFPReg(src), nameIReg(4, dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
#if WTF_CPU_X86_64
|
||||
void cvttsd2sq_rr(XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
|
@ -2391,16 +2446,6 @@ public:
|
|||
m_formatter.twoByteOp_disp32(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void movss_rm(XMMRegisterID src, const void* addr)
|
||||
{
|
||||
spew("movss %s, %p",
|
||||
nameFPReg(src), addr);
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void movss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("movss %s0x%x(%s), %s",
|
||||
|
@ -2417,16 +2462,6 @@ public:
|
|||
m_formatter.twoByteOp_disp32(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void movss_mr(const void* addr, XMMRegisterID dst)
|
||||
{
|
||||
spew("movss %p, %s",
|
||||
addr, nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("movsd %s, %d(%s,%s,%d)",
|
||||
|
@ -2483,6 +2518,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void movss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("movss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void movsd_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
|
@ -2492,6 +2535,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
|
||||
}
|
||||
|
||||
void movss_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
spew("movss %p, %s",
|
||||
address, nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
|
||||
}
|
||||
|
||||
void movsd_rm(XMMRegisterID src, const void* address)
|
||||
{
|
||||
spew("movsd %s, %p",
|
||||
|
@ -2499,6 +2550,14 @@ public:
|
|||
m_formatter.prefix(PRE_SSE_F2);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address);
|
||||
}
|
||||
|
||||
void movss_rm(XMMRegisterID src, const void* address)
|
||||
{
|
||||
spew("movss %s, %p",
|
||||
nameFPReg(src), address);
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address);
|
||||
}
|
||||
#else
|
||||
JmpSrc movsd_ripr(XMMRegisterID dst)
|
||||
{
|
||||
|
@ -2558,6 +2617,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void mulss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("mulss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("mulsd %s0x%x(%s), %s",
|
||||
|
@ -2566,6 +2633,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void mulss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("mulss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
FIXME_INSN_PRINTING;
|
||||
|
@ -2582,6 +2657,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void subss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("subss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void subsd_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("subsd %s0x%x(%s), %s",
|
||||
|
@ -2590,6 +2673,21 @@ public:
|
|||
m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void subss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("subss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void ucomiss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("ucomiss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void ucomisd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("ucomisd %s, %s",
|
||||
|
@ -2614,6 +2712,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void divss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("divss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void divsd_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("divsd %s0x%x(%s), %s",
|
||||
|
@ -2622,6 +2728,14 @@ public:
|
|||
m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void divss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("divss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void xorpd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("xorpd %s, %s",
|
||||
|
@ -2630,6 +2744,13 @@ public:
|
|||
m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void xorps_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("xorps %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void orpd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("orpd %s, %s",
|
||||
|
@ -2794,6 +2915,11 @@ public:
|
|||
spew(".double %.20f", d);
|
||||
m_formatter.doubleConstant(d);
|
||||
}
|
||||
void floatConstant(float f)
|
||||
{
|
||||
spew(".float %.20f", f);
|
||||
m_formatter.floatConstant(f);
|
||||
}
|
||||
|
||||
void int64Constant(int64_t i)
|
||||
{
|
||||
|
@ -3434,6 +3560,17 @@ private:
|
|||
m_buffer.putInt64Unchecked(u.u64);
|
||||
}
|
||||
|
||||
void floatConstant(float f)
|
||||
{
|
||||
m_buffer.ensureSpace(sizeof(float));
|
||||
union {
|
||||
uint32_t u32;
|
||||
float f;
|
||||
} u;
|
||||
u.f = f;
|
||||
m_buffer.putIntUnchecked(u.u32);
|
||||
}
|
||||
|
||||
void int64Constant(int64_t i)
|
||||
{
|
||||
m_buffer.ensureSpace(sizeof(int64_t));
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef builtin_Iterator_inl_h
|
||||
#define builtin_Iterator_inl_h
|
||||
|
||||
#include "jsiter.h"
|
||||
|
||||
#include "vm/ObjectImpl-inl.h"
|
||||
|
||||
inline void
|
||||
js::PropertyIteratorObject::setNativeIterator(js::NativeIterator *ni)
|
||||
{
|
||||
setPrivate(ni);
|
||||
}
|
||||
|
||||
#endif /* builtin_Iterator_inl_h */
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/RegExpObject-inl.h"
|
||||
#include "vm/RegExpStatics-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
|
|
@ -18,32 +18,6 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
template <typename T, typename Unioned>
|
||||
void
|
||||
EncapsulatedPtr<T, Unioned>::pre()
|
||||
{
|
||||
T::writeBarrierPre(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void
|
||||
RelocatablePtr<T>::post()
|
||||
{
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
JS_ASSERT(this->value);
|
||||
T::writeBarrierPostRelocate(this->value, &this->value);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void
|
||||
RelocatablePtr<T>::relocate(JSRuntime *rt)
|
||||
{
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
T::writeBarrierPostRemove(this->value, &this->value);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
class DenseRangeRef : public gc::BufferableRef
|
||||
{
|
||||
|
|
|
@ -49,6 +49,11 @@ HeapSlot::preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slo
|
|||
static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target);
|
||||
}
|
||||
|
||||
bool
|
||||
RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone)
|
||||
{
|
||||
return shadowZone->runtimeFromMainThread()->isHeapMajorCollecting();
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -197,7 +197,7 @@ class EncapsulatedPtr
|
|||
operator T*() const { return value; }
|
||||
|
||||
protected:
|
||||
void pre();
|
||||
void pre() { T::writeBarrierPre(value); }
|
||||
};
|
||||
|
||||
template <class T, class Unioned = uintptr_t>
|
||||
|
@ -320,8 +320,18 @@ class RelocatablePtr : public EncapsulatedPtr<T>
|
|||
}
|
||||
|
||||
protected:
|
||||
inline void post();
|
||||
inline void relocate(JSRuntime *rt);
|
||||
void post() {
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
JS_ASSERT(this->value);
|
||||
T::writeBarrierPostRelocate(this->value, &this->value);
|
||||
#endif
|
||||
}
|
||||
|
||||
void relocate(JSRuntime *rt) {
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
T::writeBarrierPostRemove(this->value, &this->value);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -922,6 +932,11 @@ class ReadBarrieredValue
|
|||
inline JSObject &toObject() const;
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone);
|
||||
#endif
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* gc_Barrier_h */
|
||||
|
|
|
@ -99,6 +99,7 @@ struct Cell
|
|||
MOZ_ALWAYS_INLINE void unmark(uint32_t color) const;
|
||||
|
||||
inline JSRuntime *runtimeFromMainThread() const;
|
||||
inline JS::shadow::Runtime *shadowRuntimeFromMainThread() const;
|
||||
inline JS::Zone *tenuredZone() const;
|
||||
inline bool tenuredIsInsideZone(JS::Zone *zone) const;
|
||||
|
||||
|
@ -963,6 +964,12 @@ Cell::runtimeFromMainThread() const
|
|||
return rt;
|
||||
}
|
||||
|
||||
inline JS::shadow::Runtime *
|
||||
Cell::shadowRuntimeFromMainThread() const
|
||||
{
|
||||
return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromMainThread());
|
||||
}
|
||||
|
||||
inline JSRuntime *
|
||||
Cell::runtimeFromAnyThread() const
|
||||
{
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#define gc_Marking_h
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
#include "jit/IonCode.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
class JSAtom;
|
||||
|
@ -30,6 +29,16 @@ class UnownedBaseShape;
|
|||
|
||||
template<class, typename> class HeapPtr;
|
||||
|
||||
namespace jit {
|
||||
class IonCode;
|
||||
class IonScript;
|
||||
class VMFunction;
|
||||
}
|
||||
|
||||
namespace types {
|
||||
class Type;
|
||||
}
|
||||
|
||||
namespace gc {
|
||||
|
||||
/*** Object Marking ***/
|
||||
|
|
|
@ -33,7 +33,7 @@ typedef JS::Rooted<Shape*> RootedShape;
|
|||
typedef JS::Rooted<types::TypeObject*> RootedTypeObject;
|
||||
typedef JS::Rooted<JSAtom*> RootedAtom;
|
||||
typedef JS::Rooted<PropertyName*> RootedPropertyName;
|
||||
typedef Rooted<js::ScriptSourceObject*> RootedScriptSource;
|
||||
typedef JS::Rooted<js::ScriptSourceObject*> RootedScriptSource;
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -46,31 +46,26 @@ assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0;var i} return f');
|
|||
assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0;var i=0} return f');
|
||||
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var im=glob.imul; function f() {} return f');
|
||||
var code = asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f');
|
||||
assertAsmLinkAlwaysFail(code, null);
|
||||
assertAsmLinkAlwaysFail(code, {});
|
||||
assertAsmLinkAlwaysFail(code, {imul:Math.imul});
|
||||
assertEq(asmLink(code, {Math:{imul:Math.imul}})(2,3), 6);
|
||||
var code = asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f');
|
||||
assertEq(asmLink(code, this)(8,4), 32);
|
||||
assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), null);
|
||||
assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), {});
|
||||
assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), {imul:Math.imul});
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), {Math:{imul:Math.imul}})(2,3), 6);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), this)(8,4), 32);
|
||||
|
||||
var code = asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f');
|
||||
assertAsmLinkAlwaysFail(code, null, null);
|
||||
assertAsmLinkAlwaysFail(code, this, null, null);
|
||||
assertAsmLinkAlwaysFail(code, this, null, null);
|
||||
assertAsmLinkAlwaysFail(code, this, null, new ArrayBuffer(1));
|
||||
assertAsmLinkFail(code, this, null, new ArrayBuffer(100));
|
||||
assertAsmLinkFail(code, this, null, new ArrayBuffer(4000));
|
||||
assertEq(asmLink(code, this, null, new ArrayBuffer(4096))(), undefined);
|
||||
var code = asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f');
|
||||
assertEq(asmLink(code, this, null, new ArrayBuffer(2*4096))(), undefined);
|
||||
assertAsmLinkAlwaysFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), null, null);
|
||||
assertAsmLinkAlwaysFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, null);
|
||||
assertAsmLinkAlwaysFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, null);
|
||||
assertAsmLinkAlwaysFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(1));
|
||||
assertAsmLinkFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(100));
|
||||
assertAsmLinkFail(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(4000));
|
||||
assertEq(asmLink(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(4096))(), undefined);
|
||||
assertEq(asmLink(asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'), this, null, new ArrayBuffer(2*4096))(), undefined);
|
||||
|
||||
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i]|0; return i|0}; return f');
|
||||
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>1]|0; return i|0}; return f');
|
||||
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>1]|0; return i|0}; return f');
|
||||
var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>2]|0; return i|0}; return f');
|
||||
assertAsmLinkAlwaysFail(code, this, null, new ArrayBuffer(4095));
|
||||
assertEq(code(this, null, new ArrayBuffer(4096))(), 0);
|
||||
assertAsmLinkAlwaysFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>2]|0; return i|0}; return f'), this, null, new ArrayBuffer(4095));
|
||||
assertEq(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>2]|0; return i|0}; return f')(this, null, new ArrayBuffer(4096))(), 0);
|
||||
|
||||
var exp = asmLink(asmCompile(USE_ASM + "return {}"));
|
||||
assertEq(Object.keys(exp).length, 0);
|
||||
|
|
|
@ -22,26 +22,22 @@ assertAsmTypeFail('global', USE_ASM + "var j=0;var i=j.i|0; function f() { retur
|
|||
assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f");
|
||||
assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f");
|
||||
assertAsmTypeFail('global', USE_ASM + 'var i=global.Infinity; function f() { i = 0.0 } return f');
|
||||
var code = asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f');
|
||||
assertAsmLinkAlwaysFail(code, undefined);
|
||||
assertAsmLinkAlwaysFail(code, null);
|
||||
assertAsmLinkFail(code, 3);
|
||||
assertAsmLinkFail(code, {});
|
||||
assertAsmLinkFail(code, {Infinity:NaN});
|
||||
assertAsmLinkFail(code, {Infinity:-Infinity});
|
||||
assertEq(asmLink(code, {Infinity:Infinity})(), Infinity);
|
||||
var code = asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f');
|
||||
assertEq(asmLink(code, this)(), Infinity);
|
||||
var code = asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f');
|
||||
assertAsmLinkAlwaysFail(code, undefined);
|
||||
assertAsmLinkAlwaysFail(code, null);
|
||||
assertAsmLinkFail(code, 3);
|
||||
assertAsmLinkFail(code, {});
|
||||
assertAsmLinkFail(code, {Infinity:Infinity});
|
||||
assertAsmLinkFail(code, {Infinity:-Infinity});
|
||||
assertEq(asmLink(code, {NaN:NaN})(), NaN);
|
||||
var code = asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f');
|
||||
assertEq(asmLink(code, this)(), NaN);
|
||||
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), undefined);
|
||||
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), null);
|
||||
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), 3);
|
||||
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {});
|
||||
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:NaN});
|
||||
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:-Infinity});
|
||||
assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:Infinity})(), Infinity);
|
||||
assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), this)(), Infinity);
|
||||
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), undefined);
|
||||
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), null);
|
||||
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), 3);
|
||||
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {});
|
||||
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {Infinity:Infinity});
|
||||
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {Infinity:-Infinity});
|
||||
assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {NaN:NaN})(), NaN);
|
||||
assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), this)(), NaN);
|
||||
|
||||
assertAsmLinkFail(asmCompile('glob','foreign','buf', USE_ASM + 'var t = new glob.Int32Array(buf); function f() {} return f'), {get Int32Array(){return Int32Array}}, null, new ArrayBuffer(4096))
|
||||
assertAsmLinkFail(asmCompile('glob','foreign','buf', USE_ASM + 'var t = new glob.Int32Array(buf); function f() {} return f'), new Proxy({}, {get:function() {return Int32Array}}), null, new ArrayBuffer(4096))
|
||||
|
@ -69,3 +65,12 @@ assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function
|
|||
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(null, {i:42})), 42);
|
||||
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(this, {i:1.4})), 1.4);
|
||||
assertEq(asmLink(asmCompile(USE_ASM + "var g=0; function f() { var i=42; while (1) { break; } g = i; return g|0 } return f"))(), 42);
|
||||
|
||||
var f1 = asmCompile('global', 'foreign', 'heap', USE_ASM + 'var i32 = new global.Int32Array(heap); function g() { return i32[4]|0 } return g');
|
||||
var global = this;
|
||||
var ab = new ArrayBuffer(4096);
|
||||
var p = new Proxy(global,
|
||||
{has:function(name) { f1(global, null, ab); return true},
|
||||
getOwnPropertyDescriptor:function(name) { return {value:Int32Array}}});
|
||||
new Int32Array(ab)[4] = 42;
|
||||
assertEq(f1(p, null, ab)(), 42);
|
||||
|
|
|
@ -442,6 +442,14 @@ assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'functi
|
|||
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8191&8191)>>3]; } return f'), this, null, buf)(),-1.0);
|
||||
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[(8192&8191)>>3] = -1.0; f64[0] = 0.0; return +f64[(8192&8191)>>3]; } return f'), this, null, buf)(),0.0);
|
||||
|
||||
// Bug 913867
|
||||
var buf = new ArrayBuffer(8192);
|
||||
new Int32Array(buf)[0] = 0x55aa5a5a;
|
||||
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(0&0)>>2]|0; } return f'), this, null, buf)(),0x55aa5a5a);
|
||||
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(4&0)>>2]|0; } return f'), this, null, buf)(),0x55aa5a5a);
|
||||
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f1() { i32[0] = 1; return 8; }; function f() { return i32[((f1()|0)&0)>>2]|0; } return f'), this, null, buf)(),1);
|
||||
assertEq(new Int32Array(buf)[0], 1);
|
||||
|
||||
|
||||
// Bug 882012
|
||||
assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + "var id=foreign.id;var doubles=new stdlib.Float64Array(heap);function g(){doubles[0]=+id(2.0);return +doubles[0];}return g"), this, {id: function(x){return x;}}, BUF_64KB)(), 2.0);
|
||||
|
|
|
@ -6,65 +6,53 @@ function testUnary(f, g) {
|
|||
assertEq(f(n), g(n));
|
||||
}
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{sin:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{sin:null}});
|
||||
testUnary(asmLink(code, {Math:{sin:Math.sin}}), Math.sin);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:Math.sin}}), Math.sin);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{cos:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{cos:null}});
|
||||
testUnary(asmLink(code, {Math:{cos:Math.cos}}), Math.cos);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f'), {Math:{cos:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f'), {Math:{cos:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f'), {Math:{cos:Math.cos}}), Math.cos);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var ta=glob.Math.tan; function f(d) { d=+d; return +ta(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{tan:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{tan:null}});
|
||||
testUnary(asmLink(code, {Math:{tan:Math.tan}}), Math.tan);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ta=glob.Math.tan; function f(d) { d=+d; return +ta(d) } return f'), {Math:{tan:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ta=glob.Math.tan; function f(d) { d=+d; return +ta(d) } return f'), {Math:{tan:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var ta=glob.Math.tan; function f(d) { d=+d; return +ta(d) } return f'), {Math:{tan:Math.tan}}), Math.tan);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var as=glob.Math.asin; function f(d) { d=+d; return +as(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{asin:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{asin:null}});
|
||||
testUnary(asmLink(code, {Math:{asin:Math.asin}}), Math.asin);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var as=glob.Math.asin; function f(d) { d=+d; return +as(d) } return f'), {Math:{asin:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var as=glob.Math.asin; function f(d) { d=+d; return +as(d) } return f'), {Math:{asin:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var as=glob.Math.asin; function f(d) { d=+d; return +as(d) } return f'), {Math:{asin:Math.asin}}), Math.asin);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var ac=glob.Math.acos; function f(d) { d=+d; return +ac(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{acos:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{acos:null}});
|
||||
testUnary(asmLink(code, {Math:{acos:Math.acos}}), Math.acos);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ac=glob.Math.acos; function f(d) { d=+d; return +ac(d) } return f'), {Math:{acos:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ac=glob.Math.acos; function f(d) { d=+d; return +ac(d) } return f'), {Math:{acos:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var ac=glob.Math.acos; function f(d) { d=+d; return +ac(d) } return f'), {Math:{acos:Math.acos}}), Math.acos);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var at=glob.Math.atan; function f(d) { d=+d; return +at(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{atan:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{atan:null}});
|
||||
testUnary(asmLink(code, {Math:{atan:Math.atan}}), Math.atan);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan; function f(d) { d=+d; return +at(d) } return f'), {Math:{atan:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan; function f(d) { d=+d; return +at(d) } return f'), {Math:{atan:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan; function f(d) { d=+d; return +at(d) } return f'), {Math:{atan:Math.atan}}), Math.atan);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var ce=glob.Math.ceil; function f(d) { d=+d; return +ce(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{ceil:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{ceil:null}});
|
||||
testUnary(asmLink(code, {Math:{ceil:Math.ceil}}), Math.ceil);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ce=glob.Math.ceil; function f(d) { d=+d; return +ce(d) } return f'), {Math:{ceil:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var ce=glob.Math.ceil; function f(d) { d=+d; return +ce(d) } return f'), {Math:{ceil:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var ce=glob.Math.ceil; function f(d) { d=+d; return +ce(d) } return f'), {Math:{ceil:Math.ceil}}), Math.ceil);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var fl=glob.Math.floor; function f(d) { d=+d; return +fl(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{floor:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{floor:null}});
|
||||
testUnary(asmLink(code, {Math:{floor:Math.floor}}), Math.floor);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var fl=glob.Math.floor; function f(d) { d=+d; return +fl(d) } return f'), {Math:{floor:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var fl=glob.Math.floor; function f(d) { d=+d; return +fl(d) } return f'), {Math:{floor:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var fl=glob.Math.floor; function f(d) { d=+d; return +fl(d) } return f'), {Math:{floor:Math.floor}}), Math.floor);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var exq=glob.Math.exp; function f(d) { d=+d; return +exq(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{exp:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{exp:null}});
|
||||
testUnary(asmLink(code, {Math:{exp:Math.exp}}), Math.exp);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var exq=glob.Math.exp; function f(d) { d=+d; return +exq(d) } return f'), {Math:{exp:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var exq=glob.Math.exp; function f(d) { d=+d; return +exq(d) } return f'), {Math:{exp:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var exq=glob.Math.exp; function f(d) { d=+d; return +exq(d) } return f'), {Math:{exp:Math.exp}}), Math.exp);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var lo=glob.Math.log; function f(d) { d=+d; return +lo(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{log:Math.sqrt}});
|
||||
assertAsmLinkFail(code, {Math:{log:null}});
|
||||
testUnary(asmLink(code, {Math:{log:Math.log}}), Math.log);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var lo=glob.Math.log; function f(d) { d=+d; return +lo(d) } return f'), {Math:{log:Math.sqrt}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var lo=glob.Math.log; function f(d) { d=+d; return +lo(d) } return f'), {Math:{log:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var lo=glob.Math.log; function f(d) { d=+d; return +lo(d) } return f'), {Math:{log:Math.log}}), Math.log);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var sq=glob.Math.sqrt; function f(d) { d=+d; return +sq(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{sqrt:Math.sin}});
|
||||
assertAsmLinkFail(code, {Math:{sqrt:null}});
|
||||
testUnary(asmLink(code, {Math:{sqrt:Math.sqrt}}), Math.sqrt);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sqrt; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sqrt:Math.sin}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sqrt; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sqrt:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sqrt; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sqrt:Math.sqrt}}), Math.sqrt);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; return +abs(d) } return f');
|
||||
assertAsmLinkFail(code, {Math:{abs:Math.sin}});
|
||||
assertAsmLinkFail(code, {Math:{abs:null}});
|
||||
testUnary(asmLink(code, {Math:{abs:Math.abs}}), Math.abs);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; return +abs(d) } return f'), {Math:{abs:Math.sin}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; return +abs(d) } return f'), {Math:{abs:null}});
|
||||
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; return +abs(d) } return f'), {Math:{abs:Math.abs}}), Math.abs);
|
||||
|
||||
var f = asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; return abs(i|0)|0 } return f'), this);
|
||||
for (n of [-Math.pow(2,31)-1, -Math.pow(2,31), -Math.pow(2,31)+1, -1, 0, 1, Math.pow(2,31)-2, Math.pow(2,31)-1, Math.pow(2,31)])
|
||||
|
@ -77,15 +65,13 @@ function testBinary(f, g) {
|
|||
assertEq(f(n,o), g(n,o));
|
||||
}
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f');
|
||||
assertAsmLinkFail(code, {Math:{pow:Math.sin}});
|
||||
assertAsmLinkFail(code, {Math:{pow:null}});
|
||||
testBinary(asmLink(code, {Math:{pow:Math.pow}}), Math.pow);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.sin}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:null}});
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.pow}}), Math.pow);
|
||||
|
||||
var code = asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f');
|
||||
assertAsmLinkFail(code, {Math:{atan2:Math.sin}});
|
||||
assertAsmLinkFail(code, {Math:{atan2:null}});
|
||||
testBinary(asmLink(code, {Math:{atan2:Math.atan2}}), Math.atan2);
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.sin}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:null}});
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.atan2}}), Math.atan2);
|
||||
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; d = sin(d); } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; var i=0; i = sin(d)|0; } return f');
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
// The frame created for evalInGlobal is never marked as a 'FUNCTION' frame.
|
||||
|
||||
(function () {
|
||||
var g = newGlobal();
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
gw.evalInGlobalWithBindings("eval('Math')",{}).return
|
||||
})();
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
// |jit-test| ion-eager
|
||||
x = [ "CNY", "TWD", "invalid" ];
|
||||
Object.freeze(x).map(function() {
|
||||
x.length = 6
|
||||
})
|
|
@ -0,0 +1,8 @@
|
|||
function x() {
|
||||
yield x
|
||||
}
|
||||
new(x)
|
||||
ParallelArray([7247], function() {
|
||||
--x
|
||||
eval("")
|
||||
})
|
|
@ -39,6 +39,7 @@ using namespace js::jit;
|
|||
|
||||
using mozilla::AddToHash;
|
||||
using mozilla::ArrayLength;
|
||||
using mozilla::CountLeadingZeroes32;
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::HashGeneric;
|
||||
using mozilla::IsNaN;
|
||||
|
@ -3117,8 +3118,10 @@ FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
|
|||
if (IsLiteralUint32(maskNode, &mask2)) {
|
||||
// Flag the access to skip the bounds check if the mask ensures that an 'out of
|
||||
// bounds' access can not occur based on the current heap length constraint.
|
||||
if (mozilla::CountLeadingZeroes32(f.m().minHeapLength() - 1) <= mozilla::CountLeadingZeroes32(mask2))
|
||||
if (mask2 == 0 ||
|
||||
CountLeadingZeroes32(f.m().minHeapLength() - 1) <= CountLeadingZeroes32(mask2)) {
|
||||
*needsBoundsCheck = NO_BOUNDS_CHECK;
|
||||
}
|
||||
*mask &= mask2;
|
||||
*indexExpr = indexNode;
|
||||
return true;
|
||||
|
@ -5978,7 +5981,8 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
|
|||
case RetType::Void:
|
||||
break;
|
||||
case RetType::Signed:
|
||||
masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert);
|
||||
masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert,
|
||||
/* -0 check */ false);
|
||||
break;
|
||||
case RetType::Double:
|
||||
masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert);
|
||||
|
|
|
@ -195,6 +195,7 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
|
|||
"once. This limitation should be removed in a future release. To "
|
||||
"work around this, compile a second module (e.g., using the "
|
||||
"Function constructor).");
|
||||
module.setIsLinked();
|
||||
|
||||
RootedValue globalVal(cx, UndefinedValue());
|
||||
if (args.length() > 0)
|
||||
|
@ -227,7 +228,7 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
|
|||
if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
|
||||
return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
|
||||
|
||||
module.patchHeapAccesses(heap, cx);
|
||||
module.initHeap(heap, cx);
|
||||
}
|
||||
|
||||
AutoObjectVector ffis(cx);
|
||||
|
@ -263,7 +264,6 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
|
|||
for (unsigned i = 0; i < module.numExits(); i++)
|
||||
module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();
|
||||
|
||||
module.setIsLinked(heap);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,13 @@
|
|||
using namespace js;
|
||||
|
||||
void
|
||||
AsmJSModule::patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx)
|
||||
AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(linked_);
|
||||
JS_ASSERT(!maybeHeap_);
|
||||
maybeHeap_ = heap;
|
||||
heapDatum() = heap->dataPointer();
|
||||
|
||||
JS_ASSERT(IsPowerOfTwo(heap->byteLength()));
|
||||
#if defined(JS_CPU_X86)
|
||||
uint8_t *heapOffset = heap->dataPointer();
|
||||
|
|
|
@ -630,7 +630,7 @@ class AsmJSModule
|
|||
const jit::AsmJSHeapAccess &heapAccess(unsigned i) const {
|
||||
return heapAccesses_[i];
|
||||
}
|
||||
void patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx);
|
||||
void initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx);
|
||||
|
||||
void requireHeapLengthToBeAtLeast(uint32_t len) {
|
||||
if (len > minHeapLength_)
|
||||
|
@ -655,11 +655,9 @@ class AsmJSModule
|
|||
return operationCallbackExit_;
|
||||
}
|
||||
|
||||
void setIsLinked(Handle<ArrayBufferObject*> maybeHeap) {
|
||||
void setIsLinked() {
|
||||
JS_ASSERT(!linked_);
|
||||
linked_ = true;
|
||||
maybeHeap_ = maybeHeap;
|
||||
heapDatum() = maybeHeap_ ? maybeHeap_->dataPointer() : NULL;
|
||||
}
|
||||
bool isLinked() const {
|
||||
return linked_;
|
||||
|
|
|
@ -1168,13 +1168,13 @@ BacktrackingAllocator::populateSafepoints()
|
|||
void
|
||||
BacktrackingAllocator::dumpRegisterGroups()
|
||||
{
|
||||
printf("Register groups:\n");
|
||||
fprintf(stderr, "Register groups:\n");
|
||||
for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
|
||||
VirtualRegisterGroup *group = vregs[i].group();
|
||||
if (group && i == group->canonicalReg()) {
|
||||
for (size_t j = 0; j < group->registers.length(); j++)
|
||||
printf(" v%u", group->registers[j]);
|
||||
printf("\n");
|
||||
fprintf(stderr, " v%u", group->registers[j]);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1183,68 +1183,70 @@ void
|
|||
BacktrackingAllocator::dumpLiveness()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Virtual Registers:\n");
|
||||
fprintf(stderr, "Virtual Registers:\n");
|
||||
|
||||
for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
|
||||
LBlock *block = graph.getBlock(blockIndex);
|
||||
MBasicBlock *mir = block->mir();
|
||||
|
||||
printf("\nBlock %lu", static_cast<unsigned long>(blockIndex));
|
||||
fprintf(stderr, "\nBlock %lu", static_cast<unsigned long>(blockIndex));
|
||||
for (size_t i = 0; i < mir->numSuccessors(); i++)
|
||||
printf(" [successor %u]", mir->getSuccessor(i)->id());
|
||||
printf("\n");
|
||||
fprintf(stderr, " [successor %u]", mir->getSuccessor(i)->id());
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
for (size_t i = 0; i < block->numPhis(); i++) {
|
||||
LPhi *phi = block->getPhi(i);
|
||||
|
||||
printf("Phi v%u <-", phi->getDef(0)->virtualRegister());
|
||||
fprintf(stderr, "[%u,%u Phi v%u <-",
|
||||
inputOf(phi).pos(), outputOf(phi).pos(),
|
||||
phi->getDef(0)->virtualRegister());
|
||||
for (size_t j = 0; j < phi->numOperands(); j++)
|
||||
printf(" v%u", phi->getOperand(j)->toUse()->virtualRegister());
|
||||
printf("\n");
|
||||
fprintf(stderr, " v%u", phi->getOperand(j)->toUse()->virtualRegister());
|
||||
fprintf(stderr, "]\n");
|
||||
}
|
||||
|
||||
for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
|
||||
LInstruction *ins = *iter;
|
||||
|
||||
printf("[%u,%u %s]", inputOf(ins).pos(), outputOf(ins).pos(), ins->opName());
|
||||
fprintf(stderr, "[%u,%u %s]", inputOf(ins).pos(), outputOf(ins).pos(), ins->opName());
|
||||
|
||||
for (size_t i = 0; i < ins->numTemps(); i++) {
|
||||
LDefinition *temp = ins->getTemp(i);
|
||||
if (!temp->isBogusTemp())
|
||||
printf(" [temp v%u]", temp->virtualRegister());
|
||||
fprintf(stderr, " [temp v%u]", temp->virtualRegister());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
LDefinition *def = ins->getDef(i);
|
||||
printf(" [def v%u]", def->virtualRegister());
|
||||
fprintf(stderr, " [def v%u]", def->virtualRegister());
|
||||
}
|
||||
|
||||
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
|
||||
if (alloc->isUse())
|
||||
printf(" [use v%u]", alloc->toUse()->virtualRegister());
|
||||
fprintf(stderr, " [use v%u]", alloc->toUse()->virtualRegister());
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\nLive Ranges:\n\n");
|
||||
fprintf(stderr, "\nLive Ranges:\n\n");
|
||||
|
||||
for (size_t i = 0; i < AnyRegister::Total; i++)
|
||||
printf("reg %s: %s\n", AnyRegister::FromCode(i).name(), IntervalString(fixedIntervals[i]));
|
||||
fprintf(stderr, "reg %s: %s\n", AnyRegister::FromCode(i).name(), IntervalString(fixedIntervals[i]));
|
||||
|
||||
for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
|
||||
printf("v%lu:", static_cast<unsigned long>(i));
|
||||
fprintf(stderr, "v%lu:", static_cast<unsigned long>(i));
|
||||
VirtualRegister &vreg = vregs[i];
|
||||
for (size_t j = 0; j < vreg.numIntervals(); j++) {
|
||||
if (j)
|
||||
printf(" *");
|
||||
printf("%s", IntervalString(vreg.getInterval(j)));
|
||||
fprintf(stderr, " *");
|
||||
fprintf(stderr, "%s", IntervalString(vreg.getInterval(j)));
|
||||
}
|
||||
printf("\n");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fprintf(stderr, "\n");
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
|
@ -1254,7 +1256,7 @@ struct BacktrackingAllocator::PrintLiveIntervalRange
|
|||
void operator()(const AllocatedRange &item)
|
||||
{
|
||||
if (item.range == item.interval->getRange(0)) {
|
||||
printf(" v%u: %s\n",
|
||||
fprintf(stderr, " v%u: %s\n",
|
||||
item.interval->hasVreg() ? item.interval->vreg() : 0,
|
||||
IntervalString(item.interval));
|
||||
}
|
||||
|
@ -1266,28 +1268,28 @@ void
|
|||
BacktrackingAllocator::dumpAllocations()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Allocations:\n");
|
||||
fprintf(stderr, "Allocations:\n");
|
||||
|
||||
for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
|
||||
printf("v%lu:", static_cast<unsigned long>(i));
|
||||
fprintf(stderr, "v%lu:", static_cast<unsigned long>(i));
|
||||
VirtualRegister &vreg = vregs[i];
|
||||
for (size_t j = 0; j < vreg.numIntervals(); j++) {
|
||||
if (j)
|
||||
printf(" *");
|
||||
fprintf(stderr, " *");
|
||||
LiveInterval *interval = vreg.getInterval(j);
|
||||
printf("%s :: %s", IntervalString(interval), interval->getAllocation()->toString());
|
||||
fprintf(stderr, "%s :: %s", IntervalString(interval), interval->getAllocation()->toString());
|
||||
}
|
||||
printf("\n");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
for (size_t i = 0; i < AnyRegister::Total; i++) {
|
||||
printf("reg %s:\n", AnyRegister::FromCode(i).name());
|
||||
fprintf(stderr, "reg %s:\n", AnyRegister::FromCode(i).name());
|
||||
registers[i].allocations.forEach(PrintLiveIntervalRange());
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fprintf(stderr, "\n");
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/IonLinker.h"
|
||||
#include "jit/IonSpewer.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/PerfSpewer.h"
|
||||
#include "jit/VMFunctions.h"
|
||||
|
||||
|
@ -5168,7 +5169,14 @@ ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
|
||||
if (type_ == ScalarTypeRepresentation::TYPE_FLOAT32 || type_ == ScalarTypeRepresentation::TYPE_FLOAT64) {
|
||||
masm.ensureDouble(value, FloatReg0, &failure);
|
||||
masm.storeToTypedFloatArray(type_, FloatReg0, dest);
|
||||
if (LIRGenerator::allowFloat32Optimizations() &&
|
||||
type_ == ScalarTypeRepresentation::TYPE_FLOAT32)
|
||||
{
|
||||
masm.convertDoubleToFloat(FloatReg0, ScratchFloatReg);
|
||||
masm.storeToTypedFloatArray(type_, ScratchFloatReg, dest);
|
||||
} else {
|
||||
masm.storeToTypedFloatArray(type_, FloatReg0, dest);
|
||||
}
|
||||
EmitReturnFromIC(masm);
|
||||
} else if (type_ == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED) {
|
||||
Label notInt32;
|
||||
|
|
|
@ -59,6 +59,20 @@ SetElemICInspector::sawDenseWrite() const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
SetElemICInspector::sawTypedArrayWrite() const
|
||||
{
|
||||
if (!icEntry_)
|
||||
return false;
|
||||
|
||||
// Check for a SetElem_TypedArray stub.
|
||||
for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isSetElem_TypedArray())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, Vector<Shape *> &shapes)
|
||||
{
|
||||
|
|
|
@ -40,6 +40,7 @@ class SetElemICInspector : public ICInspector
|
|||
bool sawOOBDenseWrite() const;
|
||||
bool sawOOBTypedArrayWrite() const;
|
||||
bool sawDenseWrite() const;
|
||||
bool sawTypedArrayWrite() const;
|
||||
};
|
||||
|
||||
class BaselineInspector
|
||||
|
|
|
@ -157,89 +157,36 @@ CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
|
|||
{
|
||||
ValueOperand operand = ToValue(lir, LValueToInt32::Input);
|
||||
Register output = ToRegister(lir->output());
|
||||
FloatRegister temp = ToFloatRegister(lir->tempFloat());
|
||||
|
||||
Register tag = masm.splitTagForTest(operand);
|
||||
|
||||
Label done, simple, isInt32, isBool, isString, notDouble;
|
||||
// Type-check switch.
|
||||
MDefinition *input;
|
||||
if (lir->mode() == LValueToInt32::NORMAL)
|
||||
input = lir->mirNormal()->input();
|
||||
else
|
||||
input = lir->mirTruncate()->input();
|
||||
masm.branchEqualTypeIfNeeded(MIRType_Int32, input, tag, &isInt32);
|
||||
masm.branchEqualTypeIfNeeded(MIRType_Boolean, input, tag, &isBool);
|
||||
// Only convert strings to int if we are in a truncation context, like
|
||||
// bitwise operations.
|
||||
if (lir->mode() == LValueToInt32::TRUNCATE)
|
||||
masm.branchEqualTypeIfNeeded(MIRType_String, input, tag, &isString);
|
||||
masm.branchTestDouble(Assembler::NotEqual, tag, ¬Double);
|
||||
|
||||
// If the value is a double, see if it fits in a 32-bit int. We need to ask
|
||||
// the platform-specific codegenerator to do this.
|
||||
FloatRegister temp = ToFloatRegister(lir->tempFloat());
|
||||
masm.unboxDouble(operand, temp);
|
||||
|
||||
Label fails, isDouble;
|
||||
masm.bind(&isDouble);
|
||||
Label fails;
|
||||
if (lir->mode() == LValueToInt32::TRUNCATE) {
|
||||
if (!emitTruncateDouble(temp, output))
|
||||
return false;
|
||||
// We can only handle strings in truncation contexts, like bitwise
|
||||
// operations.
|
||||
if (input->mightBeType(MIRType_String)) {
|
||||
Register stringReg = ToRegister(lir->temp());
|
||||
OutOfLineCode *ool = oolCallVM(StringToNumberInfo, lir, (ArgList(), stringReg),
|
||||
StoreFloatRegisterTo(temp));
|
||||
if (!ool)
|
||||
return false;
|
||||
|
||||
masm.truncateValueToInt32(operand, input, ool->entry(), ool->rejoin(), stringReg,
|
||||
temp, output, &fails);
|
||||
} else {
|
||||
masm.truncateValueToInt32(operand, input, temp, output, &fails);
|
||||
}
|
||||
} else {
|
||||
masm.convertDoubleToInt32(temp, output, &fails, lir->mirNormal()->canBeNegativeZero());
|
||||
}
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(¬Double);
|
||||
|
||||
if (lir->mode() == LValueToInt32::NORMAL) {
|
||||
// If the value is not null, it's a string, object, or undefined,
|
||||
// which we can't handle here.
|
||||
masm.branchTestNull(Assembler::NotEqual, tag, &fails);
|
||||
} else {
|
||||
// Test for object - then fallthrough to null, which will also handle
|
||||
// undefined.
|
||||
masm.branchEqualTypeIfNeeded(MIRType_Object, input, tag, &fails);
|
||||
masm.convertValueToInt32(operand, input, temp, output, &fails,
|
||||
lir->mirNormal()->canBeNegativeZero());
|
||||
}
|
||||
|
||||
if (fails.used() && !bailoutFrom(&fails, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
// The value is null - just emit 0.
|
||||
masm.mov(Imm32(0), output);
|
||||
masm.jump(&done);
|
||||
|
||||
// Unbox a string, call StringToNumber to get a double back, and jump back
|
||||
// to the snippet generated above about dealing with doubles.
|
||||
if (isString.used()) {
|
||||
masm.bind(&isString);
|
||||
Register str = masm.extractString(operand, ToRegister(lir->temp()));
|
||||
OutOfLineCode *ool = oolCallVM(StringToNumberInfo, lir, (ArgList(), str),
|
||||
StoreFloatRegisterTo(temp));
|
||||
if (!ool)
|
||||
return false;
|
||||
|
||||
masm.jump(ool->entry());
|
||||
masm.bind(ool->rejoin());
|
||||
masm.jump(&isDouble);
|
||||
}
|
||||
|
||||
// Just unbox a bool, the result is 0 or 1.
|
||||
if (isBool.used()) {
|
||||
masm.bind(&isBool);
|
||||
masm.unboxBoolean(operand, output);
|
||||
masm.jump(&done);
|
||||
}
|
||||
|
||||
// Integers can be unboxed.
|
||||
if (isInt32.used()) {
|
||||
masm.bind(&isInt32);
|
||||
masm.unboxInt32(operand, output);
|
||||
}
|
||||
|
||||
masm.bind(&done);
|
||||
|
||||
return true;
|
||||
return bailoutFrom(&fails, lir->snapshot());
|
||||
}
|
||||
|
||||
static const double DoubleZero = 0.0;
|
||||
|
@ -302,6 +249,67 @@ CodeGenerator::visitValueToDouble(LValueToDouble *lir)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitValueToFloat32(LValueToFloat32 *lir)
|
||||
{
|
||||
MToFloat32 *mir = lir->mir();
|
||||
ValueOperand operand = ToValue(lir, LValueToFloat32::Input);
|
||||
FloatRegister output = ToFloatRegister(lir->output());
|
||||
|
||||
Register tag = masm.splitTagForTest(operand);
|
||||
|
||||
Label isDouble, isInt32, isBool, isNull, isUndefined, done;
|
||||
bool hasBoolean = false, hasNull = false, hasUndefined = false;
|
||||
|
||||
masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
|
||||
if (mir->conversion() != MToFloat32::NumbersOnly) {
|
||||
masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
|
||||
hasBoolean = true;
|
||||
hasUndefined = true;
|
||||
if (mir->conversion() != MToFloat32::NonNullNonStringPrimitives) {
|
||||
masm.branchTestNull(Assembler::Equal, tag, &isNull);
|
||||
hasNull = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bailout(lir->snapshot()))
|
||||
return false;
|
||||
|
||||
if (hasNull) {
|
||||
static float DoubleZeroFloat = DoubleZero;
|
||||
masm.bind(&isNull);
|
||||
masm.loadStaticFloat32(&DoubleZeroFloat, output);
|
||||
masm.jump(&done);
|
||||
}
|
||||
|
||||
if (hasUndefined) {
|
||||
static float js_NaN_float = js_NaN;
|
||||
masm.bind(&isUndefined);
|
||||
masm.loadStaticFloat32(&js_NaN_float, output);
|
||||
masm.jump(&done);
|
||||
}
|
||||
|
||||
if (hasBoolean) {
|
||||
masm.bind(&isBool);
|
||||
masm.boolValueToFloat32(operand, output);
|
||||
masm.jump(&done);
|
||||
}
|
||||
|
||||
masm.bind(&isInt32);
|
||||
masm.int32ValueToFloat32(operand, output);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&isDouble);
|
||||
masm.unboxDouble(operand, output);
|
||||
masm.convertDoubleToFloat(output, output);
|
||||
masm.bind(&done);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
|
||||
{
|
||||
|
@ -309,6 +317,27 @@ CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble *lir)
|
||||
{
|
||||
masm.convertFloatToDouble(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32 *lir)
|
||||
{
|
||||
masm.convertDoubleToFloat(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32 *lir)
|
||||
{
|
||||
masm.convertInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitDoubleToInt32(LDoubleToInt32 *lir)
|
||||
{
|
||||
|
@ -1312,10 +1341,10 @@ CodeGenerator::visitGuardThreadLocalObject(LGuardThreadLocalObject *lir)
|
|||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitTypeBarrier(LTypeBarrier *lir)
|
||||
CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
|
||||
{
|
||||
ValueOperand operand = ToValue(lir, LTypeBarrier::Input);
|
||||
Register scratch = ToTempUnboxRegister(lir->temp());
|
||||
ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
|
||||
Register scratch = ToTempRegisterOrInvalid(lir->temp());
|
||||
|
||||
Label matched, miss;
|
||||
masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &matched, &miss);
|
||||
|
@ -1326,6 +1355,21 @@ CodeGenerator::visitTypeBarrier(LTypeBarrier *lir)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
Register scratch = ToTempRegisterOrInvalid(lir->temp());
|
||||
|
||||
Label matched, miss;
|
||||
masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, &matched, &miss);
|
||||
masm.jump(&miss);
|
||||
if (!bailoutFrom(&miss, lir->snapshot()))
|
||||
return false;
|
||||
masm.bind(&matched);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitMonitorTypes(LMonitorTypes *lir)
|
||||
{
|
||||
|
@ -6067,10 +6111,11 @@ CodeGenerator::visitSetElementCacheV(LSetElementCacheV *ins)
|
|||
Register obj = ToRegister(ins->object());
|
||||
Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
|
||||
Register temp = ToRegister(ins->temp());
|
||||
FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
|
||||
ValueOperand index = ToValue(ins, LSetElementCacheV::Index);
|
||||
ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
|
||||
|
||||
SetElementIC cache(obj, unboxIndex, temp, index, value, ins->mir()->strict());
|
||||
SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, ins->mir()->strict());
|
||||
|
||||
return addCache(ins, allocateCache(cache));
|
||||
}
|
||||
|
@ -6081,6 +6126,7 @@ CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
|
|||
Register obj = ToRegister(ins->object());
|
||||
Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
|
||||
Register temp = ToRegister(ins->temp());
|
||||
FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
|
||||
ValueOperand index = ToValue(ins, LSetElementCacheT::Index);
|
||||
ConstantOrRegister value;
|
||||
const LAllocation *tmp = ins->value();
|
||||
|
@ -6089,7 +6135,7 @@ CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
|
|||
else
|
||||
value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
|
||||
|
||||
SetElementIC cache(obj, unboxIndex, temp, index, value, ins->mir()->strict());
|
||||
SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, ins->mir()->strict());
|
||||
|
||||
return addCache(ins, allocateCache(cache));
|
||||
}
|
||||
|
@ -6693,46 +6739,24 @@ CodeGenerator::visitClampDToUint8(LClampDToUint8 *lir)
|
|||
bool
|
||||
CodeGenerator::visitClampVToUint8(LClampVToUint8 *lir)
|
||||
{
|
||||
ValueOperand input = ToValue(lir, LClampVToUint8::Input);
|
||||
ValueOperand operand = ToValue(lir, LClampVToUint8::Input);
|
||||
FloatRegister tempFloat = ToFloatRegister(lir->tempFloat());
|
||||
Register output = ToRegister(lir->output());
|
||||
MDefinition *input = lir->mir()->input();
|
||||
|
||||
Register tag = masm.splitTagForTest(input);
|
||||
Label fails;
|
||||
if (input->mightBeType(MIRType_String)) {
|
||||
OutOfLineCode *ool = oolCallVM(StringToNumberInfo, lir, (ArgList(), output),
|
||||
StoreFloatRegisterTo(tempFloat));
|
||||
masm.clampValueToUint8(operand, input, ool->entry(), ool->rejoin(), output,
|
||||
tempFloat, output, &fails);
|
||||
|
||||
Label done;
|
||||
Label isInt32, isDouble, isBoolean;
|
||||
masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean);
|
||||
|
||||
// Undefined, null and objects are always 0.
|
||||
Label isZero;
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, &isZero);
|
||||
masm.branchTestNull(Assembler::Equal, tag, &isZero);
|
||||
masm.branchTestObject(Assembler::Equal, tag, &isZero);
|
||||
|
||||
// Bailout for everything else (strings).
|
||||
if (!bailout(lir->snapshot()))
|
||||
} else {
|
||||
masm.clampValueToUint8(operand, input, tempFloat, output, &fails);
|
||||
}
|
||||
if (!bailoutFrom(&fails, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
masm.bind(&isInt32);
|
||||
masm.unboxInt32(input, output);
|
||||
masm.clampIntToUint8(output, output);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&isDouble);
|
||||
masm.unboxDouble(input, tempFloat);
|
||||
masm.clampDoubleToUint8(tempFloat, output);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&isBoolean);
|
||||
masm.unboxBoolean(input, output);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&isZero);
|
||||
masm.move32(Imm32(0), output);
|
||||
|
||||
masm.bind(&done);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7424,6 +7448,19 @@ CodeGenerator::visitAssertRangeD(LAssertRangeD *ins)
|
|||
return emitAssertRangeD(r, input, temp);
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitAssertRangeF(LAssertRangeF *ins)
|
||||
{
|
||||
FloatRegister input = ToFloatRegister(ins->input());
|
||||
FloatRegister temp = ToFloatRegister(ins->temp());
|
||||
Range *r = ins->range();
|
||||
|
||||
masm.convertFloatToDouble(input, input);
|
||||
bool success = emitAssertRangeD(r, input, temp);
|
||||
masm.convertDoubleToFloat(input, input);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitAssertRangeV(LAssertRangeV *ins)
|
||||
{
|
||||
|
|
|
@ -71,6 +71,10 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
bool visitMoveGroup(LMoveGroup *group);
|
||||
bool visitValueToInt32(LValueToInt32 *lir);
|
||||
bool visitValueToDouble(LValueToDouble *lir);
|
||||
bool visitValueToFloat32(LValueToFloat32 *lir);
|
||||
bool visitFloat32ToDouble(LFloat32ToDouble *lir);
|
||||
bool visitDoubleToFloat32(LDoubleToFloat32 *lir);
|
||||
bool visitInt32ToFloat32(LInt32ToFloat32 *lir);
|
||||
bool visitInt32ToDouble(LInt32ToDouble *lir);
|
||||
void emitOOLTestObject(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch);
|
||||
bool visitTestOAndBranch(LTestOAndBranch *lir);
|
||||
|
@ -91,7 +95,8 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
bool visitElements(LElements *lir);
|
||||
bool visitConvertElementsToDoubles(LConvertElementsToDoubles *lir);
|
||||
bool visitMaybeToDoubleElement(LMaybeToDoubleElement *lir);
|
||||
bool visitTypeBarrier(LTypeBarrier *lir);
|
||||
bool visitTypeBarrierV(LTypeBarrierV *lir);
|
||||
bool visitTypeBarrierO(LTypeBarrierO *lir);
|
||||
bool visitMonitorTypes(LMonitorTypes *lir);
|
||||
bool visitPostWriteBarrierO(LPostWriteBarrierO *lir);
|
||||
bool visitPostWriteBarrierV(LPostWriteBarrierV *lir);
|
||||
|
@ -304,6 +309,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
|
||||
bool visitAssertRangeI(LAssertRangeI *ins);
|
||||
bool visitAssertRangeD(LAssertRangeD *ins);
|
||||
bool visitAssertRangeF(LAssertRangeF *ins);
|
||||
bool visitAssertRangeV(LAssertRangeV *ins);
|
||||
|
||||
IonScriptCounts *extractUnassociatedScriptCounts() {
|
||||
|
|
|
@ -2596,7 +2596,7 @@ jit::UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc)
|
|||
void
|
||||
AutoFlushCache::updateTop(uintptr_t p, size_t len)
|
||||
{
|
||||
IonContext *ictx = GetIonContext();
|
||||
IonContext *ictx = MaybeGetIonContext();
|
||||
IonRuntime *irt = (ictx != NULL) ? ictx->runtime->ionRuntime() : NULL;
|
||||
if (!irt || !irt->flusher())
|
||||
JSC::ExecutableAllocator::cacheFlush((void*)p, len);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "jit/Ion.h"
|
||||
#include "jit/IonBuilder.h"
|
||||
#include "jit/LIR.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
|
||||
#include "jsinferinlines.h"
|
||||
|
@ -86,7 +87,7 @@ jit::EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph)
|
|||
// parameter passing might be live. Rewriting uses of these terms
|
||||
// in resume points may affect the interpreter's behavior. Rather
|
||||
// than doing a more sophisticated analysis, just ignore these.
|
||||
if (ins->isUnbox() || ins->isParameter())
|
||||
if (ins->isUnbox() || ins->isParameter() || ins->isTypeBarrier())
|
||||
continue;
|
||||
|
||||
// If the instruction's behavior has been constant folded into a
|
||||
|
@ -400,6 +401,12 @@ class TypeAnalyzer
|
|||
bool adjustInputs(MDefinition *def);
|
||||
bool insertConversions();
|
||||
|
||||
bool graphContainsFloat32();
|
||||
bool markPhiConsumers();
|
||||
bool markPhiProducers();
|
||||
bool specializeValidFloatOps();
|
||||
bool tryEmitFloatOperations();
|
||||
|
||||
public:
|
||||
TypeAnalyzer(MIRGenerator *mir, MIRGraph &graph)
|
||||
: mir(mir), graph(graph)
|
||||
|
@ -415,6 +422,7 @@ static MIRType
|
|||
GuessPhiType(MPhi *phi)
|
||||
{
|
||||
MIRType type = MIRType_None;
|
||||
bool convertibleToFloat32 = false;
|
||||
for (size_t i = 0, e = phi->numOperands(); i < e; i++) {
|
||||
MDefinition *in = phi->getOperand(i);
|
||||
if (in->isPhi()) {
|
||||
|
@ -429,6 +437,8 @@ GuessPhiType(MPhi *phi)
|
|||
}
|
||||
if (type == MIRType_None) {
|
||||
type = in->type();
|
||||
if (in->isConstant())
|
||||
convertibleToFloat32 = true;
|
||||
continue;
|
||||
}
|
||||
if (type != in->type()) {
|
||||
|
@ -436,11 +446,20 @@ GuessPhiType(MPhi *phi)
|
|||
if (in->resultTypeSet() && in->resultTypeSet()->empty())
|
||||
continue;
|
||||
|
||||
// Specialize phis with int32 and double operands as double.
|
||||
if (IsNumberType(type) && IsNumberType(in->type()))
|
||||
if (IsFloatType(type) && IsFloatType(in->type())) {
|
||||
// Specialize phis with int32 and float32 operands as float32.
|
||||
type = MIRType_Float32;
|
||||
} else if (convertibleToFloat32 && in->type() == MIRType_Float32) {
|
||||
// If we only saw constants before and encounter a Float32 value, promote previous
|
||||
// constants to Float32
|
||||
type = MIRType_Float32;
|
||||
} else if (IsNumberType(type) && IsNumberType(in->type())) {
|
||||
// Specialize phis with int32 and double operands as double.
|
||||
type = MIRType_Double;
|
||||
else
|
||||
convertibleToFloat32 &= in->isConstant();
|
||||
} else {
|
||||
return MIRType_Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return type;
|
||||
|
@ -476,6 +495,13 @@ TypeAnalyzer::propagateSpecialization(MPhi *phi)
|
|||
continue;
|
||||
}
|
||||
if (use->type() != phi->type()) {
|
||||
// Specialize phis with int32 and float operands as floats.
|
||||
if (IsFloatType(use->type()) && IsFloatType(phi->type())) {
|
||||
if (!respecialize(use, MIRType_Float32))
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Specialize phis with int32 and double operands as double.
|
||||
if (IsNumberType(use->type()) && IsNumberType(phi->type())) {
|
||||
if (!respecialize(use, MIRType_Double))
|
||||
|
@ -546,9 +572,24 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
|
|||
} else {
|
||||
MInstruction *replacement;
|
||||
|
||||
if (phiType == MIRType_Double && in->type() == MIRType_Int32) {
|
||||
if (phiType == MIRType_Double && IsFloatType(in->type())) {
|
||||
// Convert int32 operands to double.
|
||||
replacement = MToDouble::New(in);
|
||||
} else if (phiType == MIRType_Float32) {
|
||||
if (in->type() == MIRType_Int32 || in->type() == MIRType_Double) {
|
||||
replacement = MToFloat32::New(in);
|
||||
} else {
|
||||
// See comment below
|
||||
if (in->type() != MIRType_Value) {
|
||||
MBox *box = MBox::New(in);
|
||||
in->block()->insertBefore(in->block()->lastIns(), box);
|
||||
in = box;
|
||||
}
|
||||
|
||||
MUnbox *unbox = MUnbox::New(in, MIRType_Double, MUnbox::Fallible);
|
||||
in->block()->insertBefore(in->block()->lastIns(), unbox);
|
||||
replacement = MToFloat32::New(in);
|
||||
}
|
||||
} else {
|
||||
// If we know this branch will fail to convert to phiType,
|
||||
// insert a box that'll immediately fail in the fallible unbox
|
||||
|
@ -583,8 +624,7 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
|
|||
// the original box.
|
||||
phi->replaceOperand(i, in->toUnbox()->input());
|
||||
} else {
|
||||
MBox *box = MBox::New(in);
|
||||
in->block()->insertBefore(in->block()->lastIns(), box);
|
||||
MDefinition *box = BoxInputsPolicy::alwaysBoxAt(in->block()->lastIns(), in);
|
||||
phi->replaceOperand(i, box);
|
||||
}
|
||||
}
|
||||
|
@ -650,9 +690,222 @@ TypeAnalyzer::insertConversions()
|
|||
return true;
|
||||
}
|
||||
|
||||
// This function tries to emit Float32 specialized operations whenever it's possible.
|
||||
// MIR nodes are flagged as:
|
||||
// - Producers, when they can create Float32 that might need to be coerced into a Double.
|
||||
// Loads in Float32 arrays and conversions to Float32 are producers.
|
||||
// - Consumers, when they can have Float32 as inputs and validate a legal use of a Float32.
|
||||
// Stores in Float32 arrays and conversions to Float32 are consumers.
|
||||
// - Float32 commutative, when using the Float32 instruction instead of the Double instruction
|
||||
// does not result in a compound loss of precision. This is the case for +, -, /, * with 2
|
||||
// operands, for instance. However, an addition with 3 operands is not commutative anymore,
|
||||
// so an intermediate coercion is needed.
|
||||
// Except for phis, all these flags are known after Ion building, so they cannot change during
|
||||
// the process.
|
||||
//
|
||||
// The idea behind the algorithm is easy: whenever we can prove that a commutative operation
|
||||
// has only producers as inputs and consumers as uses, we can specialize the operation as a
|
||||
// float32 operation. Otherwise, we have to convert all float32 inputs to doubles. Even
|
||||
// if a lot of conversions are produced, GVN will take care of eliminating the redundant ones.
|
||||
//
|
||||
// Phis have a special status. Phis need to be flagged as producers or consumers as they can
|
||||
// be inputs or outputs of commutative instructions. Fortunately, producers and consumers
|
||||
// properties are such that we can deduce the property using all non phis inputs first (which form
|
||||
// an initial phi graph) and then propagate all properties from one phi to another using a
|
||||
// fixed point algorithm. The algorithm is ensured to terminate as each iteration has less or as
|
||||
// many flagged phis as the previous iteration (so the worst steady state case is all phis being
|
||||
// flagged as false).
|
||||
//
|
||||
// In a nutshell, the algorithm applies three passes:
|
||||
// 1 - Determine which phis are consumers. Each phi gets an initial value by making a global AND on
|
||||
// all its non-phi inputs. Then each phi propagates its value to other phis. If after propagation,
|
||||
// the flag value changed, we have to reapply the algorithm on all phi operands, as a phi is a
|
||||
// consumer if all of its uses are consumers.
|
||||
// 2 - Determine which phis are producers. It's the same algorithm, except that we have to reapply
|
||||
// the algorithm on all phi uses, as a phi is a producer if all of its operands are producers.
|
||||
// 3 - Go through all commutative operations and ensure their inputs are all producers and their
|
||||
// uses are all consumers.
|
||||
bool
|
||||
TypeAnalyzer::markPhiConsumers()
|
||||
{
|
||||
JS_ASSERT(phiWorklist_.empty());
|
||||
|
||||
// Iterate in postorder so worklist is initialized to RPO.
|
||||
for (PostorderIterator block(graph.poBegin()); block != graph.poEnd(); ++block) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Consumer Phis - Initial state"))
|
||||
return false;
|
||||
|
||||
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); ++phi) {
|
||||
JS_ASSERT(!phi->isInWorklist());
|
||||
bool canConsumeFloat32 = true;
|
||||
for (MUseDefIterator use(*phi); canConsumeFloat32 && use; use++) {
|
||||
MDefinition *usedef = use.def();
|
||||
canConsumeFloat32 &= usedef->isPhi() || usedef->canConsumeFloat32();
|
||||
}
|
||||
phi->setCanConsumeFloat32(canConsumeFloat32);
|
||||
if (canConsumeFloat32 && !addPhiToWorklist(*phi))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (!phiWorklist_.empty()) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Consumer Phis - Fixed point"))
|
||||
return false;
|
||||
|
||||
MPhi *phi = popPhi();
|
||||
JS_ASSERT(phi->canConsumeFloat32());
|
||||
|
||||
bool validConsumer = true;
|
||||
for (MUseDefIterator use(phi); use; use++) {
|
||||
MDefinition *def = use.def();
|
||||
if (def->isPhi() && !def->canConsumeFloat32()) {
|
||||
validConsumer = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (validConsumer)
|
||||
continue;
|
||||
|
||||
// Propagate invalidated phis
|
||||
phi->setCanConsumeFloat32(false);
|
||||
for (size_t i = 0, e = phi->numOperands(); i < e; ++i) {
|
||||
MDefinition *input = phi->getOperand(i);
|
||||
if (input->isPhi() && !input->isInWorklist() && input->canConsumeFloat32())
|
||||
{
|
||||
if (!addPhiToWorklist(input->toPhi()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::markPhiProducers()
|
||||
{
|
||||
JS_ASSERT(phiWorklist_.empty());
|
||||
|
||||
// Iterate in reverse postorder so worklist is initialized to PO.
|
||||
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Producer Phis - initial state"))
|
||||
return false;
|
||||
|
||||
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); ++phi) {
|
||||
JS_ASSERT(!phi->isInWorklist());
|
||||
bool canProduceFloat32 = true;
|
||||
for (size_t i = 0, e = phi->numOperands(); canProduceFloat32 && i < e; ++i) {
|
||||
MDefinition *input = phi->getOperand(i);
|
||||
canProduceFloat32 &= input->isPhi() || input->canProduceFloat32();
|
||||
}
|
||||
phi->setCanProduceFloat32(canProduceFloat32);
|
||||
if (canProduceFloat32 && !addPhiToWorklist(*phi))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (!phiWorklist_.empty()) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Producer Phis - Fixed point"))
|
||||
return false;
|
||||
|
||||
MPhi *phi = popPhi();
|
||||
JS_ASSERT(phi->canProduceFloat32());
|
||||
|
||||
bool validProducer = true;
|
||||
for (size_t i = 0, e = phi->numOperands(); i < e; ++i) {
|
||||
MDefinition *input = phi->getOperand(i);
|
||||
if (input->isPhi() && !input->canProduceFloat32()) {
|
||||
validProducer = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (validProducer)
|
||||
continue;
|
||||
|
||||
// Propagate invalidated phis
|
||||
phi->setCanProduceFloat32(false);
|
||||
for (MUseDefIterator use(phi); use; use++) {
|
||||
MDefinition *def = use.def();
|
||||
if (def->isPhi() && !def->isInWorklist() && def->canProduceFloat32())
|
||||
{
|
||||
if (!addPhiToWorklist(def->toPhi()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::specializeValidFloatOps()
|
||||
{
|
||||
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Instructions"))
|
||||
return false;
|
||||
|
||||
for (MInstructionIterator ins(block->begin()); ins != block->end(); ++ins) {
|
||||
if (!ins->isFloat32Commutative())
|
||||
continue;
|
||||
|
||||
if (ins->type() == MIRType_Float32)
|
||||
continue;
|
||||
|
||||
// This call will try to specialize the instruction iff all uses are consumers and
|
||||
// all inputs are producers.
|
||||
ins->trySpecializeFloat32();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::graphContainsFloat32()
|
||||
{
|
||||
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Graph contains Float32"))
|
||||
return false;
|
||||
|
||||
for (MDefinitionIterator def(*block); def; def++) {
|
||||
if (def->type() == MIRType_Float32)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::tryEmitFloatOperations()
|
||||
{
|
||||
// Backends that currently don't know how to generate Float32 specialized instructions
|
||||
// shouldn't run this pass and just let all instructions as specialized for Double.
|
||||
if (!LIRGenerator::allowFloat32Optimizations())
|
||||
return true;
|
||||
|
||||
// Asm.js uses the ahead of time type checks to specialize operations, no need to check
|
||||
// them again at this point.
|
||||
if (mir->compilingAsmJS())
|
||||
return true;
|
||||
|
||||
// Check ahead of time that there is at least one definition typed as Float32, otherwise we
|
||||
// don't need this pass.
|
||||
if (!graphContainsFloat32())
|
||||
return true;
|
||||
|
||||
if (!markPhiConsumers())
|
||||
return false;
|
||||
if (!markPhiProducers())
|
||||
return false;
|
||||
if (!specializeValidFloatOps())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::analyze()
|
||||
{
|
||||
if (!tryEmitFloatOperations())
|
||||
return false;
|
||||
if (!specializePhis())
|
||||
return false;
|
||||
if (!insertConversions())
|
||||
|
@ -1286,7 +1539,13 @@ TryEliminateTypeBarrierFromTest(MTypeBarrier *barrier, bool filtersNull, bool fi
|
|||
// types that have been seen in the first access but not the second.
|
||||
|
||||
// A test 'if (x.f)' filters both null and undefined.
|
||||
if (test->getOperand(0) == barrier->input() && direction == TRUE_BRANCH) {
|
||||
|
||||
// Disregard the possible unbox added before the Typebarrier for checking.
|
||||
MDefinition *input = barrier->input();
|
||||
if (input->isUnbox() && input->toUnbox()->mode() == MUnbox::TypeBarrier)
|
||||
input = input->toUnbox()->input();
|
||||
|
||||
if (test->getOperand(0) == input && direction == TRUE_BRANCH) {
|
||||
*eliminated = true;
|
||||
barrier->replaceAllUsesWith(barrier->input());
|
||||
return;
|
||||
|
@ -1300,7 +1559,7 @@ TryEliminateTypeBarrierFromTest(MTypeBarrier *barrier, bool filtersNull, bool fi
|
|||
|
||||
if (compareType != MCompare::Compare_Undefined && compareType != MCompare::Compare_Null)
|
||||
return;
|
||||
if (compare->getOperand(0) != barrier->input())
|
||||
if (compare->getOperand(0) != input)
|
||||
return;
|
||||
|
||||
JSOp op = compare->jsop();
|
||||
|
@ -1332,6 +1591,13 @@ TryEliminateTypeBarrier(MTypeBarrier *barrier, bool *eliminated)
|
|||
const types::StackTypeSet *barrierTypes = barrier->resultTypeSet();
|
||||
const types::StackTypeSet *inputTypes = barrier->input()->resultTypeSet();
|
||||
|
||||
// Disregard the possible unbox added before the Typebarrier.
|
||||
if (barrier->input()->isUnbox() &&
|
||||
barrier->input()->toUnbox()->mode() == MUnbox::TypeBarrier)
|
||||
{
|
||||
inputTypes = barrier->input()->toUnbox()->input()->resultTypeSet();
|
||||
}
|
||||
|
||||
if (!barrierTypes || !inputTypes)
|
||||
return true;
|
||||
|
||||
|
@ -1764,6 +2030,10 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
|
|||
// which will definitely be added to the created object before it has a
|
||||
// chance to escape and be accessed elsewhere.
|
||||
|
||||
if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fun->nonLazyScript()->compileAndGo)
|
||||
return true;
|
||||
|
||||
|
|
|
@ -930,46 +930,48 @@ IonBuilder::addOsrValueTypeBarrier(uint32_t slot, MInstruction **def_,
|
|||
def = barrier;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MIRType_Boolean:
|
||||
case MIRType_Int32:
|
||||
case MIRType_Double:
|
||||
case MIRType_String:
|
||||
case MIRType_Object:
|
||||
{
|
||||
MUnbox *unbox = MUnbox::New(def, type, MUnbox::Fallible);
|
||||
osrBlock->insertBefore(osrBlock->lastIns(), unbox);
|
||||
osrBlock->rewriteSlot(slot, unbox);
|
||||
def = unbox;
|
||||
break;
|
||||
}
|
||||
if (type != def->type()) {
|
||||
switch (type) {
|
||||
case MIRType_Boolean:
|
||||
case MIRType_Int32:
|
||||
case MIRType_Double:
|
||||
case MIRType_String:
|
||||
case MIRType_Object:
|
||||
{
|
||||
MUnbox *unbox = MUnbox::New(def, type, MUnbox::Fallible);
|
||||
osrBlock->insertBefore(osrBlock->lastIns(), unbox);
|
||||
osrBlock->rewriteSlot(slot, unbox);
|
||||
def = unbox;
|
||||
break;
|
||||
}
|
||||
|
||||
case MIRType_Null:
|
||||
{
|
||||
MConstant *c = MConstant::New(NullValue());
|
||||
osrBlock->insertBefore(osrBlock->lastIns(), c);
|
||||
osrBlock->rewriteSlot(slot, c);
|
||||
def = c;
|
||||
break;
|
||||
}
|
||||
case MIRType_Null:
|
||||
{
|
||||
MConstant *c = MConstant::New(NullValue());
|
||||
osrBlock->insertBefore(osrBlock->lastIns(), c);
|
||||
osrBlock->rewriteSlot(slot, c);
|
||||
def = c;
|
||||
break;
|
||||
}
|
||||
|
||||
case MIRType_Undefined:
|
||||
{
|
||||
MConstant *c = MConstant::New(UndefinedValue());
|
||||
osrBlock->insertBefore(osrBlock->lastIns(), c);
|
||||
osrBlock->rewriteSlot(slot, c);
|
||||
def = c;
|
||||
break;
|
||||
}
|
||||
case MIRType_Undefined:
|
||||
{
|
||||
MConstant *c = MConstant::New(UndefinedValue());
|
||||
osrBlock->insertBefore(osrBlock->lastIns(), c);
|
||||
osrBlock->rewriteSlot(slot, c);
|
||||
def = c;
|
||||
break;
|
||||
}
|
||||
|
||||
case MIRType_Magic:
|
||||
JS_ASSERT(lazyArguments_);
|
||||
osrBlock->rewriteSlot(slot, lazyArguments_);
|
||||
def = lazyArguments_;
|
||||
break;
|
||||
case MIRType_Magic:
|
||||
JS_ASSERT(lazyArguments_);
|
||||
osrBlock->rewriteSlot(slot, lazyArguments_);
|
||||
def = lazyArguments_;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JS_ASSERT(def == osrBlock->getSlot(slot));
|
||||
|
@ -3754,9 +3756,9 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
|
|||
if (!types->unknown()) {
|
||||
MTypeBarrier *barrier = MTypeBarrier::New(callInfo.thisArg(), cloneTypeSet(types), Bailout_Normal);
|
||||
current->add(barrier);
|
||||
MUnbox *unbox = MUnbox::New(barrier, MIRType_Object, MUnbox::Infallible);
|
||||
current->add(unbox);
|
||||
callInfo.setThis(unbox);
|
||||
callInfo.setThis(barrier);
|
||||
// object or missing
|
||||
JS_ASSERT(barrier->type() == MIRType_Object || barrier->type() == MIRType_Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4051,19 +4053,12 @@ IonBuilder::getInlineableGetPropertyCache(CallInfo &callInfo)
|
|||
}
|
||||
|
||||
// Optimize away the following common pattern:
|
||||
// MUnbox[MIRType_Object, Infallible] <- MTypeBarrier <- MGetPropertyCache
|
||||
if (funcDef->isUnbox()) {
|
||||
MUnbox *unbox = funcDef->toUnbox();
|
||||
if (unbox->mode() != MUnbox::Infallible)
|
||||
// MTypeBarrier[MIRType_Object] <- MGetPropertyCache
|
||||
if (funcDef->isTypeBarrier()) {
|
||||
MTypeBarrier *barrier = funcDef->toTypeBarrier();
|
||||
if (barrier->hasUses())
|
||||
return NULL;
|
||||
if (unbox->hasUses())
|
||||
return NULL;
|
||||
if (!unbox->input()->isTypeBarrier())
|
||||
return NULL;
|
||||
|
||||
MTypeBarrier *barrier = unbox->input()->toTypeBarrier();
|
||||
// Test if usecount() > 1
|
||||
if (!barrier->hasOneUse())
|
||||
if (barrier->type() != MIRType_Object)
|
||||
return NULL;
|
||||
if (!barrier->input()->isGetPropertyCache())
|
||||
return NULL;
|
||||
|
@ -4177,16 +4172,16 @@ IonBuilder::inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBl
|
|||
{
|
||||
// Getting here implies the following:
|
||||
// 1. The call function is an MGetPropertyCache, or an MGetPropertyCache
|
||||
// followed by an MTypeBarrier, followed by an MUnbox.
|
||||
JS_ASSERT(callInfo.fun()->isGetPropertyCache() || callInfo.fun()->isUnbox());
|
||||
// followed by an MTypeBarrier.
|
||||
JS_ASSERT(callInfo.fun()->isGetPropertyCache() || callInfo.fun()->isTypeBarrier());
|
||||
|
||||
// 2. The MGetPropertyCache has inlineable cases by guarding on the TypeObject.
|
||||
JS_ASSERT(dispatch->numCases() > 0);
|
||||
|
||||
// 3. The MGetPropertyCache (and, if applicable, MTypeBarrier and MUnbox) only
|
||||
// 3. The MGetPropertyCache (and, if applicable, MTypeBarrier) only
|
||||
// have at most a single use.
|
||||
JS_ASSERT_IF(callInfo.fun()->isGetPropertyCache(), !cache->hasUses());
|
||||
JS_ASSERT_IF(callInfo.fun()->isUnbox(), cache->hasOneUse());
|
||||
JS_ASSERT_IF(callInfo.fun()->isTypeBarrier(), cache->hasOneUse());
|
||||
|
||||
// This means that no resume points yet capture the MGetPropertyCache,
|
||||
// so everything from the MGetPropertyCache up until the call is movable.
|
||||
|
@ -4240,19 +4235,14 @@ IonBuilder::inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBl
|
|||
getPropBlock->addFromElsewhere(cache);
|
||||
getPropBlock->push(cache);
|
||||
} else {
|
||||
MUnbox *unbox = callInfo.fun()->toUnbox();
|
||||
JS_ASSERT(unbox->input()->isTypeBarrier());
|
||||
JS_ASSERT(unbox->type() == MIRType_Object);
|
||||
JS_ASSERT(unbox->mode() == MUnbox::Infallible);
|
||||
|
||||
MTypeBarrier *typeBarrier = unbox->input()->toTypeBarrier();
|
||||
JS_ASSERT(typeBarrier->input()->isGetPropertyCache());
|
||||
JS_ASSERT(typeBarrier->input()->toGetPropertyCache() == cache);
|
||||
MTypeBarrier *barrier = callInfo.fun()->toTypeBarrier();
|
||||
JS_ASSERT(barrier->type() == MIRType_Object);
|
||||
JS_ASSERT(barrier->input()->isGetPropertyCache());
|
||||
JS_ASSERT(barrier->input()->toGetPropertyCache() == cache);
|
||||
|
||||
getPropBlock->addFromElsewhere(cache);
|
||||
getPropBlock->addFromElsewhere(typeBarrier);
|
||||
getPropBlock->addFromElsewhere(unbox);
|
||||
getPropBlock->push(unbox);
|
||||
getPropBlock->addFromElsewhere(barrier);
|
||||
getPropBlock->push(barrier);
|
||||
}
|
||||
|
||||
// Construct an end block with the correct resume point.
|
||||
|
@ -6207,38 +6197,14 @@ IonBuilder::pushTypeBarrier(MInstruction *ins, types::StackTypeSet *observed, bo
|
|||
|
||||
current->pop();
|
||||
|
||||
MInstruction *barrier;
|
||||
JSValueType type = observed->getKnownTypeTag();
|
||||
MInstruction *barrier = MTypeBarrier::New(ins, cloneTypeSet(observed));
|
||||
current->add(barrier);
|
||||
|
||||
// An unbox instruction isn't enough to capture JSVAL_TYPE_OBJECT. Use a type
|
||||
// barrier followed by an infallible unbox.
|
||||
bool isObject = false;
|
||||
if (type == JSVAL_TYPE_OBJECT && !observed->hasType(types::Type::AnyObjectType())) {
|
||||
type = JSVAL_TYPE_UNKNOWN;
|
||||
isObject = true;
|
||||
}
|
||||
if (barrier->type() == MIRType_Undefined)
|
||||
return pushConstant(UndefinedValue());
|
||||
if (barrier->type() == MIRType_Null)
|
||||
return pushConstant(NullValue());
|
||||
|
||||
switch (type) {
|
||||
case JSVAL_TYPE_UNKNOWN:
|
||||
case JSVAL_TYPE_UNDEFINED:
|
||||
case JSVAL_TYPE_NULL:
|
||||
barrier = MTypeBarrier::New(ins, cloneTypeSet(observed));
|
||||
current->add(barrier);
|
||||
|
||||
if (type == JSVAL_TYPE_UNDEFINED)
|
||||
return pushConstant(UndefinedValue());
|
||||
if (type == JSVAL_TYPE_NULL)
|
||||
return pushConstant(NullValue());
|
||||
if (isObject) {
|
||||
barrier = MUnbox::New(barrier, MIRType_Object, MUnbox::Infallible);
|
||||
current->add(barrier);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MUnbox::Mode mode = ins->isEffectful() ? MUnbox::TypeBarrier : MUnbox::TypeGuard;
|
||||
barrier = MUnbox::New(ins, MIRTypeFromValueType(type), mode);
|
||||
current->add(barrier);
|
||||
}
|
||||
current->push(barrier);
|
||||
return true;
|
||||
}
|
||||
|
@ -6339,6 +6305,7 @@ jit::TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *input
|
|||
case MIRType_Boolean:
|
||||
case MIRType_Int32:
|
||||
case MIRType_Double:
|
||||
case MIRType_Float32:
|
||||
case MIRType_String:
|
||||
case MIRType_Magic:
|
||||
return types->hasType(types::Type::PrimitiveType(ValueTypeFromMIRType(input)));
|
||||
|
@ -7057,6 +7024,8 @@ IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index,
|
|||
knownType = allowDouble ? MIRType_Double : MIRType_Int32;
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
knownType = (LIRGenerator::allowFloat32Optimizations()) ? MIRType_Float32 : MIRType_Double;
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
knownType = MIRType_Double;
|
||||
break;
|
||||
|
@ -7295,9 +7264,9 @@ IonBuilder::setElemTryCache(bool *emitted, MDefinition *object,
|
|||
|
||||
// TODO: Bug 876650: remove this check:
|
||||
// Temporary disable the cache if non dense native,
|
||||
// untill the cache supports more ics
|
||||
// until the cache supports more ics
|
||||
SetElemICInspector icInspect(inspector->setElemICInspector(pc));
|
||||
if (!icInspect.sawDenseWrite())
|
||||
if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite())
|
||||
return true;
|
||||
|
||||
bool needsBarrier;
|
||||
|
|
|
@ -1117,16 +1117,13 @@ CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &ca
|
|||
if (!obj || !obj->isNative())
|
||||
return GetPropertyIC::CanAttachNone;
|
||||
|
||||
// If the cache is idempotent or parallel, watch out for resolve hooks or
|
||||
// non-native objects on the proto chain. We check this before calling
|
||||
// lookupProperty, to make sure no effectful lookup hooks or resolve hooks
|
||||
// are called.
|
||||
if (cache.lookupNeedsIdempotentChain() && !obj->hasIdempotentProtoChain())
|
||||
// The lookup needs to be universally pure, otherwise we risk calling hooks out
|
||||
// of turn. We don't mind doing this even when purity isn't required, because we
|
||||
// only miss out on shape hashification, which is only a temporary perf cost.
|
||||
// The limits were arbitrarily set, anyways.
|
||||
if (!LookupPropertyPure(obj, NameToId(name), holder.address(), shape.address()))
|
||||
return GetPropertyIC::CanAttachNone;
|
||||
|
||||
if (!GetPropCache::doPropertyLookup(cx, obj, name, holder, shape))
|
||||
return GetPropertyIC::CanAttachError;
|
||||
|
||||
RootedScript script(cx);
|
||||
jsbytecode *pc;
|
||||
cache.getScriptedLocation(&script, &pc);
|
||||
|
@ -1209,8 +1206,6 @@ GetPropertyIC::tryAttachNative(JSContext *cx, IonScript *ion, HandleObject obj,
|
|||
|
||||
NativeGetPropCacheability type =
|
||||
CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape);
|
||||
if (type == CanAttachError)
|
||||
return false;
|
||||
if (type == CanAttachNone)
|
||||
return true;
|
||||
|
||||
|
@ -1424,8 +1419,6 @@ GetPropertyIC::tryAttachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, Handle
|
|||
NativeGetPropCacheability canCache =
|
||||
CanAttachNativeGetProp(cx, *this, checkObj, name, &holder, &shape);
|
||||
|
||||
if (canCache == CanAttachError)
|
||||
return false;
|
||||
if (canCache == CanAttachNone)
|
||||
return true;
|
||||
|
||||
|
@ -1898,7 +1891,6 @@ GetPropertyParIC::update(ForkJoinSlice *slice, size_t cacheIndex,
|
|||
|
||||
GetPropertyIC::NativeGetPropCacheability canCache =
|
||||
CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape);
|
||||
JS_ASSERT(canCache != GetPropertyIC::CanAttachError);
|
||||
|
||||
if (canCache == GetPropertyIC::CanAttachReadSlot) {
|
||||
if (!cache.attachReadSlot(cx, ion, obj, holder, shape))
|
||||
|
@ -1946,8 +1938,8 @@ IonCache::destroy()
|
|||
}
|
||||
|
||||
bool
|
||||
SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion,
|
||||
HandleObject obj, HandleShape shape)
|
||||
SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj,
|
||||
HandleShape shape, bool checkTypeset)
|
||||
{
|
||||
JS_ASSERT(obj->isNative());
|
||||
|
||||
|
@ -1961,7 +1953,7 @@ SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion,
|
|||
|
||||
// Guard that the incoming value is in the type set for the property
|
||||
// if a type barrier is required.
|
||||
if (needsTypeBarrier() && !value().constant()) {
|
||||
if (needsTypeBarrier()) {
|
||||
// We can't do anything that would change the HeapTypeSet, so
|
||||
// just guard that it's already there.
|
||||
|
||||
|
@ -1971,29 +1963,28 @@ SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion,
|
|||
Address(object(), JSObject::offsetOfType()),
|
||||
ImmGCPtr(type), &failures);
|
||||
|
||||
if (!type->unknownProperties()) {
|
||||
if (checkTypeset) {
|
||||
TypedOrValueRegister valReg = value().reg();
|
||||
RootedId id(cx, types::IdToTypeId(AtomToId(name())));
|
||||
types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, id);
|
||||
JS_ASSERT(propTypes);
|
||||
JS_ASSERT(!propTypes->unknown());
|
||||
|
||||
if (!propTypes->unknown()) {
|
||||
Label barrierSuccess;
|
||||
Label barrierFailure;
|
||||
Label barrierSuccess;
|
||||
Label barrierFailure;
|
||||
|
||||
Register scratchReg = object();
|
||||
masm.push(scratchReg);
|
||||
Register scratchReg = object();
|
||||
masm.push(scratchReg);
|
||||
|
||||
masm.guardTypeSet(valReg, propTypes, scratchReg,
|
||||
&barrierSuccess, &barrierFailure);
|
||||
masm.guardTypeSet(valReg, propTypes, scratchReg,
|
||||
&barrierSuccess, &barrierFailure);
|
||||
|
||||
masm.bind(&barrierFailure);
|
||||
masm.pop(object());
|
||||
masm.jump(&failures);
|
||||
masm.bind(&barrierFailure);
|
||||
masm.pop(object());
|
||||
masm.jump(&failures);
|
||||
|
||||
masm.bind(&barrierSuccess);
|
||||
masm.pop(object());
|
||||
}
|
||||
masm.bind(&barrierSuccess);
|
||||
masm.pop(object());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2059,6 +2050,13 @@ IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder,
|
|||
if (shape->hasSetterValue())
|
||||
return false;
|
||||
|
||||
// Despite the vehement claims of Shape.h that writable() is only
|
||||
// relevant for data descriptors, some PropertyOp setters care
|
||||
// desperately about its value. The flag should be always true, apart
|
||||
// from these rare instances.
|
||||
if (!shape->writable())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2598,7 +2596,7 @@ SetPropertyIC::attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj,
|
|||
|
||||
static bool
|
||||
IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject obj,
|
||||
HandleId id, MutableHandleShape pshape)
|
||||
HandleId id, MutableHandleShape pshape, bool *checkTypeset)
|
||||
{
|
||||
if (!obj->isNative())
|
||||
return false;
|
||||
|
@ -2617,20 +2615,39 @@ IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject
|
|||
if (!shape->writable())
|
||||
return false;
|
||||
|
||||
bool shouldCheck = false;
|
||||
types::TypeObject *type = obj->getType(cx);
|
||||
if (cache.needsTypeBarrier() && !type->unknownProperties()) {
|
||||
RootedId typeId(cx, types::IdToTypeId(id));
|
||||
types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, typeId);
|
||||
if (!propTypes)
|
||||
return false;
|
||||
if (cache.value().constant() && !propTypes->unknown()) {
|
||||
// If the input is a constant, then don't bother if the barrier will always fail.
|
||||
if (!propTypes->hasType(types::GetValueType(cache.value().value())))
|
||||
return false;
|
||||
if (!propTypes->unknown()) {
|
||||
shouldCheck = true;
|
||||
ConstantOrRegister val = cache.value();
|
||||
if (val.constant()) {
|
||||
// If the input is a constant, then don't bother if the barrier will always fail.
|
||||
if (!propTypes->hasType(types::GetValueType(cache.value().value())))
|
||||
return false;
|
||||
shouldCheck = false;
|
||||
} else {
|
||||
TypedOrValueRegister reg = val.reg();
|
||||
// We can do the same trick as above for primitive types of specialized registers.
|
||||
// TIs handling of objects is complicated enough to warrant a runtime
|
||||
// check, as we can't statically handle the case where the typeset
|
||||
// contains the specific object, but doesn't have ANYOBJECT set.
|
||||
if (reg.hasTyped() && reg.type() != MIRType_Object) {
|
||||
JSValueType valType = ValueTypeFromMIRType(reg.type());
|
||||
if (!propTypes->hasType(types::Type::PrimitiveType(valType)))
|
||||
return false;
|
||||
shouldCheck = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pshape.set(shape);
|
||||
*checkTypeset = shouldCheck;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2737,8 +2754,9 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
|
|||
}
|
||||
}
|
||||
RootedShape shape(cx);
|
||||
if (!addedSetterStub && IsPropertySetInlineable(cx, cache, obj, id, &shape)) {
|
||||
if (!cache.attachNativeExisting(cx, ion, obj, shape))
|
||||
bool checkTypeset;
|
||||
if (!addedSetterStub && IsPropertySetInlineable(cx, cache, obj, id, &shape, &checkTypeset)) {
|
||||
if (!cache.attachNativeExisting(cx, ion, obj, shape, checkTypeset))
|
||||
return false;
|
||||
addedSetterStub = true;
|
||||
} else if (!addedSetterStub) {
|
||||
|
@ -2807,9 +2825,6 @@ GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
|
|||
GetPropertyIC::NativeGetPropCacheability canCache =
|
||||
CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape);
|
||||
|
||||
if (canCache == GetPropertyIC::CanAttachError)
|
||||
return false;
|
||||
|
||||
if (canCache != GetPropertyIC::CanAttachReadSlot) {
|
||||
IonSpew(IonSpew_InlineCaches, "GETELEM uncacheable property");
|
||||
return true;
|
||||
|
@ -2928,9 +2943,9 @@ GetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval,
|
|||
}
|
||||
|
||||
static void
|
||||
GenerateTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
TypedArrayObject *tarr, const Value &idval, Register object,
|
||||
ConstantOrRegister index, TypedOrValueRegister output)
|
||||
GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
TypedArrayObject *tarr, const Value &idval, Register object,
|
||||
ConstantOrRegister index, TypedOrValueRegister output)
|
||||
{
|
||||
JS_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
|
||||
|
||||
|
@ -3036,7 +3051,7 @@ GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayO
|
|||
{
|
||||
MacroAssembler masm(cx);
|
||||
RepatchStubAppender attacher(*this);
|
||||
GenerateTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
|
||||
GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
|
||||
}
|
||||
|
||||
|
@ -3350,6 +3365,105 @@ SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, c
|
|||
return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
|
||||
}
|
||||
|
||||
static bool
|
||||
GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
TypedArrayObject *tarr, Register object,
|
||||
ValueOperand indexVal, ConstantOrRegister value,
|
||||
Register tempUnbox, Register temp, FloatRegister tempFloat)
|
||||
{
|
||||
Label failures, done, popObjectAndFail;
|
||||
|
||||
// Guard on the shape.
|
||||
Shape *shape = tarr->lastProperty();
|
||||
if (!shape)
|
||||
return false;
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
|
||||
|
||||
// Ensure the index is an int32.
|
||||
masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures);
|
||||
Register index = masm.extractInt32(indexVal, tempUnbox);
|
||||
|
||||
// Guard on the length.
|
||||
Address length(object, TypedArrayObject::lengthOffset());
|
||||
masm.unboxInt32(length, temp);
|
||||
masm.branch32(Assembler::BelowOrEqual, temp, index, &done);
|
||||
|
||||
// Load the elements vector.
|
||||
Register elements = temp;
|
||||
masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elements);
|
||||
|
||||
// Set the value.
|
||||
int arrayType = tarr->type();
|
||||
int width = TypedArrayObject::slotWidth(arrayType);
|
||||
BaseIndex target(elements, index, ScaleFromElemWidth(width));
|
||||
|
||||
if (arrayType == ScalarTypeRepresentation::TYPE_FLOAT32 ||
|
||||
arrayType == ScalarTypeRepresentation::TYPE_FLOAT64)
|
||||
{
|
||||
if (!masm.convertConstantOrRegisterToDouble(cx, value, tempFloat, &failures))
|
||||
return false;
|
||||
masm.storeToTypedFloatArray(arrayType, tempFloat, target);
|
||||
} else {
|
||||
// On x86 we only have 6 registers available to use, so reuse the object
|
||||
// register to compute the intermediate value to store and restore it
|
||||
// afterwards.
|
||||
masm.push(object);
|
||||
|
||||
if (arrayType == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED) {
|
||||
if (!masm.clampConstantOrRegisterToUint8(cx, value, tempFloat, object,
|
||||
&popObjectAndFail))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!masm.truncateConstantOrRegisterToInt32(cx, value, tempFloat, object,
|
||||
&popObjectAndFail))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
masm.storeToTypedIntArray(arrayType, object, target);
|
||||
|
||||
masm.pop(object);
|
||||
}
|
||||
|
||||
// Out-of-bound writes jump here as they are no-ops.
|
||||
masm.bind(&done);
|
||||
attacher.jumpRejoin(masm);
|
||||
|
||||
if (popObjectAndFail.used()) {
|
||||
masm.bind(&popObjectAndFail);
|
||||
masm.pop(object);
|
||||
}
|
||||
|
||||
masm.bind(&failures);
|
||||
attacher.jumpNextStub(masm);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval, const Value &value)
|
||||
{
|
||||
// Don't bother attaching stubs for assigning strings and objects.
|
||||
return (obj->is<TypedArrayObject>() && idval.isInt32() &&
|
||||
!value.isString() && !value.isObject());
|
||||
}
|
||||
|
||||
bool
|
||||
SetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
RepatchStubAppender attacher(*this);
|
||||
if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
|
||||
object(), index(), value(),
|
||||
tempToUnboxIndex(), temp(), tempFloat()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
|
||||
}
|
||||
|
||||
bool
|
||||
SetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
|
||||
HandleValue idval, HandleValue value)
|
||||
|
@ -3357,9 +3471,18 @@ SetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
|
|||
IonScript *ion = GetTopIonJSScript(cx)->ionScript();
|
||||
SetElementIC &cache = ion->getCache(cacheIndex).toSetElement();
|
||||
|
||||
if (cache.canAttachStub() && !cache.hasDenseStub() && IsElementSetInlineable(obj, idval)) {
|
||||
if (!cache.attachDenseElement(cx, ion, obj, idval))
|
||||
return false;
|
||||
bool attachedStub = false;
|
||||
if (cache.canAttachStub()) {
|
||||
if (!cache.hasDenseStub() && IsElementSetInlineable(obj, idval)) {
|
||||
if (!cache.attachDenseElement(cx, ion, obj, idval))
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
if (!attachedStub && canAttachTypedArrayElement(obj, idval, value)) {
|
||||
TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
|
||||
if (!cache.attachTypedArrayElement(cx, ion, tarr))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!SetObjectElement(cx, obj, idval, value, cache.strict()))
|
||||
|
@ -3411,7 +3534,7 @@ GetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
|
|||
{
|
||||
MacroAssembler masm(cx);
|
||||
DispatchStubPrepender attacher(*this);
|
||||
GenerateTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
|
||||
GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
|
||||
}
|
||||
|
||||
|
@ -3458,7 +3581,6 @@ GetElementParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject ob
|
|||
|
||||
GetPropertyIC::NativeGetPropCacheability canCache =
|
||||
CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape);
|
||||
JS_ASSERT(canCache != GetPropertyIC::CanAttachError);
|
||||
|
||||
if (canCache == GetPropertyIC::CanAttachReadSlot)
|
||||
{
|
||||
|
|
|
@ -594,7 +594,6 @@ class GetPropertyIC : public RepatchIonCache
|
|||
}
|
||||
|
||||
enum NativeGetPropCacheability {
|
||||
CanAttachError,
|
||||
CanAttachNone,
|
||||
CanAttachReadSlot,
|
||||
CanAttachArrayLength,
|
||||
|
@ -603,13 +602,6 @@ class GetPropertyIC : public RepatchIonCache
|
|||
|
||||
// Helpers for CanAttachNativeGetProp
|
||||
typedef JSContext * Context;
|
||||
static bool doPropertyLookup(Context cx, HandleObject obj, HandlePropertyName name,
|
||||
MutableHandleObject holder, MutableHandleShape shape) {
|
||||
return JSObject::lookupProperty(cx, obj, name, holder, shape);
|
||||
}
|
||||
bool lookupNeedsIdempotentChain() const {
|
||||
return idempotent();
|
||||
}
|
||||
bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const;
|
||||
bool allowArrayLength(Context cx, HandleObject obj) const;
|
||||
|
||||
|
@ -687,7 +679,8 @@ class SetPropertyIC : public RepatchIonCache
|
|||
return hasGenericProxyStub_;
|
||||
}
|
||||
|
||||
bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape);
|
||||
bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj,
|
||||
HandleShape shape, bool checkTypeset);
|
||||
bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj,
|
||||
HandleObject holder, HandleShape shape, void *returnAddr);
|
||||
bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldshape,
|
||||
|
@ -761,13 +754,8 @@ class GetElementIC : public RepatchIonCache
|
|||
|
||||
// Helpers for CanAttachNativeGetProp
|
||||
typedef JSContext * Context;
|
||||
static bool doPropertyLookup(Context cx, HandleObject obj, HandlePropertyName name,
|
||||
MutableHandleObject holder, MutableHandleShape shape) {
|
||||
return JSObject::lookupProperty(cx, obj, name, holder, shape);
|
||||
}
|
||||
bool allowGetters() const { return false; }
|
||||
bool allowArrayLength(Context, HandleObject) const { return false; }
|
||||
bool lookupNeedsIdempotentChain() const { return false; }
|
||||
bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const {
|
||||
return monitoredResult();
|
||||
}
|
||||
|
@ -805,6 +793,7 @@ class SetElementIC : public RepatchIonCache
|
|||
Register object_;
|
||||
Register tempToUnboxIndex_;
|
||||
Register temp_;
|
||||
FloatRegister tempFloat_;
|
||||
ValueOperand index_;
|
||||
ConstantOrRegister value_;
|
||||
bool strict_;
|
||||
|
@ -813,11 +802,12 @@ class SetElementIC : public RepatchIonCache
|
|||
|
||||
public:
|
||||
SetElementIC(Register object, Register tempToUnboxIndex, Register temp,
|
||||
ValueOperand index, ConstantOrRegister value,
|
||||
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
|
||||
bool strict)
|
||||
: object_(object),
|
||||
tempToUnboxIndex_(tempToUnboxIndex),
|
||||
temp_(temp),
|
||||
tempFloat_(tempFloat),
|
||||
index_(index),
|
||||
value_(value),
|
||||
strict_(strict),
|
||||
|
@ -838,6 +828,9 @@ class SetElementIC : public RepatchIonCache
|
|||
Register temp() const {
|
||||
return temp_;
|
||||
}
|
||||
FloatRegister tempFloat() const {
|
||||
return tempFloat_;
|
||||
}
|
||||
ValueOperand index() const {
|
||||
return index_;
|
||||
}
|
||||
|
@ -856,11 +849,14 @@ class SetElementIC : public RepatchIonCache
|
|||
hasDenseStub_ = true;
|
||||
}
|
||||
|
||||
static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval, const Value &value);
|
||||
|
||||
bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval);
|
||||
bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr);
|
||||
|
||||
static bool
|
||||
update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
|
||||
HandleValue value);
|
||||
HandleValue value);
|
||||
};
|
||||
|
||||
class BindNameIC : public RepatchIonCache
|
||||
|
@ -1045,11 +1041,6 @@ class GetPropertyParIC : public ParallelIonCache
|
|||
|
||||
// CanAttachNativeGetProp Helpers
|
||||
typedef LockedJSContext & Context;
|
||||
static bool doPropertyLookup(Context cx, HandleObject obj, HandlePropertyName name,
|
||||
MutableHandleObject holder, MutableHandleShape shape) {
|
||||
return LookupPropertyPure(obj, NameToId(name), holder.address(), shape.address());
|
||||
}
|
||||
bool lookupNeedsIdempotentChain() const { return true; }
|
||||
bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; }
|
||||
bool allowGetters() const { return false; }
|
||||
bool allowArrayLength(Context, HandleObject) const { return true; }
|
||||
|
@ -1105,11 +1096,6 @@ class GetElementParIC : public ParallelIonCache
|
|||
|
||||
// CanAttachNativeGetProp Helpers
|
||||
typedef LockedJSContext & Context;
|
||||
static bool doPropertyLookup(Context cx, HandleObject obj, HandlePropertyName name,
|
||||
MutableHandleObject holder, MutableHandleShape shape) {
|
||||
return LookupPropertyPure(obj, NameToId(name), holder.address(), shape.address());
|
||||
}
|
||||
bool lookupNeedsIdempotentChain() const { return true; }
|
||||
bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; }
|
||||
bool allowGetters() const { return false; }
|
||||
bool allowArrayLength(Context, HandleObject) const { return false; }
|
||||
|
|
|
@ -1241,6 +1241,22 @@ SnapshotIterator::slotValue(const Slot &slot)
|
|||
case SnapshotReader::DOUBLE_REG:
|
||||
return DoubleValue(machine_.read(slot.floatReg()));
|
||||
|
||||
case SnapshotReader::FLOAT32_REG:
|
||||
{
|
||||
double asDouble = machine_.read(slot.floatReg());
|
||||
// The register contains the encoding of a float32. We just read
|
||||
// the bits without making any conversion.
|
||||
float asFloat = *(float*) &asDouble;
|
||||
return DoubleValue(asFloat);
|
||||
}
|
||||
|
||||
case SnapshotReader::FLOAT32_STACK:
|
||||
{
|
||||
double asDouble = ReadFrameDoubleSlot(fp_, slot.stackSlot());
|
||||
float asFloat = *(float*) &asDouble; // no conversion, see comment above.
|
||||
return DoubleValue(asFloat);
|
||||
}
|
||||
|
||||
case SnapshotReader::TYPED_REG:
|
||||
return FromTypedPayload(slot.knownType(), machine_.read(slot.reg()));
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "jit/BaselineIC.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/BaselineRegisters.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/MIR.h"
|
||||
#include "vm/ForkJoin.h"
|
||||
|
||||
|
@ -96,13 +97,32 @@ MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types,
|
|||
JS_ASSERT(scratch != InvalidReg);
|
||||
branchTestObject(NotEqual, tag, miss);
|
||||
Register obj = extractObject(address, scratch);
|
||||
guardObjectType(obj, types, scratch, matched, miss);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned count = types->getObjectCount();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (JSObject *object = types->getSingleObject(i))
|
||||
branchPtr(Equal, obj, ImmGCPtr(object), matched);
|
||||
}
|
||||
template <typename TypeSet> void
|
||||
MacroAssembler::guardObjectType(Register obj, const TypeSet *types,
|
||||
Register scratch, Label *matched, Label *miss)
|
||||
{
|
||||
JS_ASSERT(!types->unknown());
|
||||
JS_ASSERT(!types->hasType(types::Type::AnyObjectType()));
|
||||
JS_ASSERT(types->getObjectCount());
|
||||
JS_ASSERT(scratch != InvalidReg);
|
||||
|
||||
// Note: Some platforms give the same register for obj and scratch.
|
||||
// Make sure when writing to scratch, the obj register isn't used anymore!
|
||||
|
||||
bool hasTypeObjects = false;
|
||||
unsigned count = types->getObjectCount();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (JSObject *object = types->getSingleObject(i))
|
||||
branchPtr(Equal, obj, ImmGCPtr(object), matched);
|
||||
else if (types->getTypeObject(i))
|
||||
hasTypeObjects = true;
|
||||
}
|
||||
|
||||
if (hasTypeObjects) {
|
||||
loadPtr(Address(obj, JSObject::offsetOfType()), scratch);
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
|
@ -142,6 +162,13 @@ template void MacroAssembler::guardTypeSet(const Address &address, const TypeWra
|
|||
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const TypeWrapper *types,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
|
||||
template void MacroAssembler::guardObjectType(Register obj, const types::StackTypeSet *types,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
template void MacroAssembler::guardObjectType(Register obj, const types::TypeSet *types,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
template void MacroAssembler::guardObjectType(Register obj, const TypeWrapper *types,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
|
||||
template void MacroAssembler::guardType(const Address &address, types::Type type,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
template void MacroAssembler::guardType(const ValueOperand &value, types::Type type,
|
||||
|
@ -278,6 +305,41 @@ MacroAssembler::moveNurseryPtr(const ImmMaybeNurseryPtr &ptr, const Register &re
|
|||
movePtr(ptr, reg);
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
static void
|
||||
StoreToTypedFloatArray(MacroAssembler &masm, int arrayType, const S &value, const T &dest) {
|
||||
switch (arrayType) {
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
if (LIRGenerator::allowFloat32Optimizations()) {
|
||||
masm.storeFloat(value, dest);
|
||||
} else {
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
// See the comment in ToDoubleForTypedArray.
|
||||
masm.canonicalizeDouble(value);
|
||||
#endif
|
||||
masm.convertDoubleToFloat(value, ScratchFloatReg);
|
||||
masm.storeFloat(ScratchFloatReg, dest);
|
||||
}
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
// See the comment in ToDoubleForTypedArray.
|
||||
masm.canonicalizeDouble(value);
|
||||
#endif
|
||||
masm.storeDouble(value, dest);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, const BaseIndex &dest) {
|
||||
StoreToTypedFloatArray(*this, arrayType, value, dest);
|
||||
}
|
||||
void MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, const Address &dest) {
|
||||
StoreToTypedFloatArray(*this, arrayType, value, dest);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssembler::loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp,
|
||||
|
@ -311,11 +373,16 @@ MacroAssembler::loadFromTypedArray(int arrayType, const T &src, AnyRegister dest
|
|||
}
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
if (arrayType == ScalarTypeRepresentation::TYPE_FLOAT32)
|
||||
if (LIRGenerator::allowFloat32Optimizations()) {
|
||||
loadFloat(src, dest.fpu());
|
||||
canonicalizeFloat(dest.fpu());
|
||||
} else {
|
||||
loadFloatAsDouble(src, dest.fpu());
|
||||
else
|
||||
loadDouble(src, dest.fpu());
|
||||
canonicalizeDouble(dest.fpu());
|
||||
}
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
loadDouble(src, dest.fpu());
|
||||
canonicalizeDouble(dest.fpu());
|
||||
break;
|
||||
default:
|
||||
|
@ -369,6 +436,11 @@ MacroAssembler::loadFromTypedArray(int arrayType, const T &src, const ValueOpera
|
|||
}
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), NULL);
|
||||
if (LIRGenerator::allowFloat32Optimizations())
|
||||
convertFloatToDouble(ScratchFloatReg, ScratchFloatReg);
|
||||
boxDouble(ScratchFloatReg, dest);
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), NULL);
|
||||
boxDouble(ScratchFloatReg, dest);
|
||||
|
@ -1304,6 +1376,7 @@ MacroAssembler::convertInt32ValueToDouble(const Address &address, Register scrat
|
|||
}
|
||||
|
||||
static const double DoubleZero = 0.0;
|
||||
static const double DoubleOne = 1.0;
|
||||
|
||||
void
|
||||
MacroAssembler::convertValueToDouble(ValueOperand value, FloatRegister output, Label *fail)
|
||||
|
@ -1339,39 +1412,45 @@ MacroAssembler::convertValueToDouble(ValueOperand value, FloatRegister output, L
|
|||
bind(&done);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::convertValueToInt32(ValueOperand value, FloatRegister temp,
|
||||
Register output, Label *fail)
|
||||
bool
|
||||
MacroAssembler::convertValueToDouble(JSContext *cx, const Value &v, FloatRegister output, Label *fail)
|
||||
{
|
||||
Register tag = splitTagForTest(value);
|
||||
if (v.isNumber() || v.isString()) {
|
||||
double d;
|
||||
if (v.isNumber())
|
||||
d = v.toNumber();
|
||||
else if (!StringToNumber(cx, v.toString(), &d))
|
||||
return false;
|
||||
|
||||
Label done, simple, isInt32, isBool, isDouble;
|
||||
if (d == js_NaN)
|
||||
loadStaticDouble(&js_NaN, output);
|
||||
else
|
||||
loadConstantDouble(d, output);
|
||||
|
||||
branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
branchTestBoolean(Assembler::Equal, tag, &isBool);
|
||||
branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
branchTestNull(Assembler::NotEqual, tag, fail);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The value is null - just emit 0.
|
||||
mov(Imm32(0), output);
|
||||
jump(&done);
|
||||
if (v.isBoolean()) {
|
||||
if (v.toBoolean())
|
||||
loadStaticDouble(&DoubleOne, output);
|
||||
else
|
||||
loadStaticDouble(&DoubleZero, output);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try converting double into integer
|
||||
bind(&isDouble);
|
||||
unboxDouble(value, temp);
|
||||
convertDoubleToInt32(temp, output, fail, /* -0 check */ false);
|
||||
jump(&done);
|
||||
if (v.isNull()) {
|
||||
loadStaticDouble(&DoubleZero, output);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Just unbox a bool, the result is 0 or 1.
|
||||
bind(&isBool);
|
||||
unboxBoolean(value, output);
|
||||
jump(&done);
|
||||
if (v.isUndefined()) {
|
||||
loadStaticDouble(&js_NaN, output);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Integers can be unboxed.
|
||||
bind(&isInt32);
|
||||
unboxInt32(value, output);
|
||||
|
||||
bind(&done);
|
||||
JS_ASSERT(v.isObject());
|
||||
jump(fail);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1413,6 +1492,251 @@ MacroAssembler::popRooted(VMFunction::RootType rootType, Register cellReg,
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MacroAssembler::convertConstantOrRegisterToDouble(JSContext *cx, ConstantOrRegister src,
|
||||
FloatRegister output, Label *fail)
|
||||
{
|
||||
if (src.constant())
|
||||
return convertValueToDouble(cx, src.value(), output, fail);
|
||||
|
||||
convertTypedOrValueToDouble(src.reg(), output, fail);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::convertTypedOrValueToDouble(TypedOrValueRegister src, FloatRegister output,
|
||||
Label *fail)
|
||||
{
|
||||
if (src.hasValue()) {
|
||||
convertValueToDouble(src.valueReg(), output, fail);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (src.type()) {
|
||||
case MIRType_Null:
|
||||
loadStaticDouble(&DoubleZero, output);
|
||||
break;
|
||||
case MIRType_Boolean:
|
||||
case MIRType_Int32:
|
||||
convertInt32ToDouble(src.typedReg().gpr(), output);
|
||||
break;
|
||||
case MIRType_Double:
|
||||
if (src.typedReg().fpu() != output)
|
||||
moveDouble(src.typedReg().fpu(), output);
|
||||
break;
|
||||
case MIRType_Object:
|
||||
case MIRType_String:
|
||||
jump(fail);
|
||||
break;
|
||||
case MIRType_Undefined:
|
||||
loadStaticDouble(&js_NaN, output);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Bad MIRType");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::convertDoubleToInt(FloatRegister src, Register output, Label *fail,
|
||||
IntConversionBehavior behavior)
|
||||
{
|
||||
switch (behavior) {
|
||||
case IntConversion_Normal:
|
||||
case IntConversion_NegativeZeroCheck:
|
||||
convertDoubleToInt32(src, output, fail, behavior == IntConversion_NegativeZeroCheck);
|
||||
break;
|
||||
case IntConversion_Truncate:
|
||||
branchTruncateDouble(src, output, fail);
|
||||
break;
|
||||
case IntConversion_ClampToUint8:
|
||||
clampDoubleToUint8(src, output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::convertValueToInt(ValueOperand value, MDefinition *maybeInput,
|
||||
Label *handleStringEntry, Label *handleStringRejoin,
|
||||
Register stringReg, FloatRegister temp, Register output,
|
||||
Label *fail, IntConversionBehavior behavior)
|
||||
{
|
||||
Register tag = splitTagForTest(value);
|
||||
bool handleStrings = (behavior == IntConversion_Truncate ||
|
||||
behavior == IntConversion_ClampToUint8) &&
|
||||
handleStringEntry &&
|
||||
handleStringRejoin;
|
||||
bool zeroObjects = behavior == IntConversion_ClampToUint8;
|
||||
|
||||
Label done, isInt32, isBool, isDouble, isNull, isString;
|
||||
|
||||
branchEqualTypeIfNeeded(MIRType_Int32, maybeInput, tag, &isInt32);
|
||||
branchEqualTypeIfNeeded(MIRType_Boolean, maybeInput, tag, &isBool);
|
||||
branchEqualTypeIfNeeded(MIRType_Double, maybeInput, tag, &isDouble);
|
||||
|
||||
// If we are not truncating, we fail for anything that's not
|
||||
// null. Otherwise we might be able to handle strings and objects.
|
||||
switch (behavior) {
|
||||
case IntConversion_Normal:
|
||||
case IntConversion_NegativeZeroCheck:
|
||||
branchTestNull(Assembler::NotEqual, tag, fail);
|
||||
break;
|
||||
|
||||
case IntConversion_Truncate:
|
||||
case IntConversion_ClampToUint8:
|
||||
branchEqualTypeIfNeeded(MIRType_Null, maybeInput, tag, &isNull);
|
||||
if (handleStrings)
|
||||
branchEqualTypeIfNeeded(MIRType_String, maybeInput, tag, &isString);
|
||||
if (zeroObjects)
|
||||
branchEqualTypeIfNeeded(MIRType_Object, maybeInput, tag, &isNull);
|
||||
branchTestUndefined(Assembler::NotEqual, tag, fail);
|
||||
break;
|
||||
}
|
||||
|
||||
// The value is null or undefined in truncation contexts - just emit 0.
|
||||
if (isNull.used())
|
||||
bind(&isNull);
|
||||
mov(Imm32(0), output);
|
||||
jump(&done);
|
||||
|
||||
// Try converting a string into a double, then jump to the double case.
|
||||
if (handleStrings) {
|
||||
bind(&isString);
|
||||
unboxString(value, stringReg);
|
||||
jump(handleStringEntry);
|
||||
}
|
||||
|
||||
// Try converting double into integer.
|
||||
if (isDouble.used() || handleStrings) {
|
||||
if (isDouble.used()) {
|
||||
bind(&isDouble);
|
||||
unboxDouble(value, temp);
|
||||
}
|
||||
|
||||
if (handleStrings)
|
||||
bind(handleStringRejoin);
|
||||
|
||||
convertDoubleToInt(temp, output, fail, behavior);
|
||||
jump(&done);
|
||||
}
|
||||
|
||||
// Just unbox a bool, the result is 0 or 1.
|
||||
if (isBool.used()) {
|
||||
bind(&isBool);
|
||||
unboxBoolean(value, output);
|
||||
jump(&done);
|
||||
}
|
||||
|
||||
// Integers can be unboxed.
|
||||
if (isInt32.used()) {
|
||||
bind(&isInt32);
|
||||
unboxInt32(value, output);
|
||||
if (behavior == IntConversion_ClampToUint8)
|
||||
clampIntToUint8(output, output);
|
||||
}
|
||||
|
||||
bind(&done);
|
||||
}
|
||||
|
||||
bool
|
||||
MacroAssembler::convertValueToInt(JSContext *cx, const Value &v, Register output, Label *fail,
|
||||
IntConversionBehavior behavior)
|
||||
{
|
||||
bool handleStrings = (behavior == IntConversion_Truncate ||
|
||||
behavior == IntConversion_ClampToUint8);
|
||||
bool zeroObjects = behavior == IntConversion_ClampToUint8;
|
||||
|
||||
if (v.isNumber() || (handleStrings && v.isString())) {
|
||||
double d;
|
||||
if (v.isNumber())
|
||||
d = v.toNumber();
|
||||
else if (!StringToNumber(cx, v.toString(), &d))
|
||||
return false;
|
||||
|
||||
switch (behavior) {
|
||||
case IntConversion_Normal:
|
||||
case IntConversion_NegativeZeroCheck: {
|
||||
// -0 is checked anyways if we have a constant value.
|
||||
int i;
|
||||
if (mozilla::DoubleIsInt32(d, &i))
|
||||
move32(Imm32(i), output);
|
||||
else
|
||||
jump(fail);
|
||||
break;
|
||||
}
|
||||
case IntConversion_Truncate:
|
||||
move32(Imm32(ToInt32(d)), output);
|
||||
break;
|
||||
case IntConversion_ClampToUint8:
|
||||
move32(Imm32(ClampDoubleToUint8(d)), output);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (v.isBoolean()) {
|
||||
move32(Imm32(v.toBoolean() ? 1 : 0), output);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (v.isNull() || v.isUndefined()) {
|
||||
move32(Imm32(0), output);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ASSERT(v.isObject());
|
||||
|
||||
if (zeroObjects)
|
||||
move32(Imm32(0), output);
|
||||
else
|
||||
jump(fail);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MacroAssembler::convertConstantOrRegisterToInt(JSContext *cx, ConstantOrRegister src,
|
||||
FloatRegister temp, Register output,
|
||||
Label *fail, IntConversionBehavior behavior)
|
||||
{
|
||||
if (src.constant())
|
||||
return convertValueToInt(cx, src.value(), output, fail, behavior);
|
||||
|
||||
convertTypedOrValueToInt(src.reg(), temp, output, fail, behavior);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp,
|
||||
Register output, Label *fail,
|
||||
IntConversionBehavior behavior)
|
||||
{
|
||||
if (src.hasValue()) {
|
||||
convertValueToInt(src.valueReg(), temp, output, fail, behavior);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (src.type()) {
|
||||
case MIRType_Undefined:
|
||||
case MIRType_Null:
|
||||
move32(Imm32(0), output);
|
||||
break;
|
||||
case MIRType_Boolean:
|
||||
case MIRType_Int32:
|
||||
if (src.typedReg().gpr() != output)
|
||||
move32(src.typedReg().gpr(), output);
|
||||
break;
|
||||
case MIRType_Double:
|
||||
convertDoubleToInt(src.typedReg().fpu(), output, fail, behavior);
|
||||
break;
|
||||
case MIRType_String:
|
||||
case MIRType_Object:
|
||||
jump(fail);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Bad MIRType");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::finish()
|
||||
{
|
||||
|
@ -1470,10 +1794,10 @@ MacroAssembler::branchIfNotInterpretedConstructor(Register fun, Register scratch
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition *def, const Register &tag,
|
||||
MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, const Register &tag,
|
||||
Label *label)
|
||||
{
|
||||
if (def->mightBeType(type)) {
|
||||
if (!maybeDef || maybeDef->mightBeType(type)) {
|
||||
switch (type) {
|
||||
case MIRType_Null:
|
||||
branchTestNull(Equal, tag, label);
|
||||
|
|
|
@ -162,6 +162,9 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
template <typename Source, typename TypeSet>
|
||||
void guardTypeSet(const Source &address, const TypeSet *types, Register scratch,
|
||||
Label *matched, Label *miss);
|
||||
template <typename TypeSet>
|
||||
void guardObjectType(Register obj, const TypeSet *types,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
template <typename Source>
|
||||
void guardType(const Source &address, types::Type type, Register scratch,
|
||||
Label *matched, Label *miss);
|
||||
|
@ -427,7 +430,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
void Push(TypedOrValueRegister v) {
|
||||
if (v.hasValue())
|
||||
Push(v.valueReg());
|
||||
else if (v.type() == MIRType_Double)
|
||||
else if (IsFloatingPointType(v.type()))
|
||||
Push(v.typedReg().fpu());
|
||||
else
|
||||
Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
|
||||
|
@ -558,6 +561,14 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
bind(¬NaN);
|
||||
}
|
||||
|
||||
void canonicalizeFloat(FloatRegister reg) {
|
||||
static float js_NaN_float = js_NaN;
|
||||
Label notNaN;
|
||||
branchFloat(DoubleOrdered, reg, reg, ¬NaN);
|
||||
loadStaticFloat32(&js_NaN_float, reg);
|
||||
bind(¬NaN);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp, Label *fail);
|
||||
|
||||
|
@ -586,24 +597,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void storeToTypedFloatArray(int arrayType, FloatRegister value, const T &dest) {
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
// See the comment in ToDoubleForTypedArray.
|
||||
canonicalizeDouble(value);
|
||||
#endif
|
||||
switch (arrayType) {
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
convertDoubleToFloat(value, ScratchFloatReg);
|
||||
storeFloat(ScratchFloatReg, dest);
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
storeDouble(value, dest);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const BaseIndex &dest);
|
||||
void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const Address &dest);
|
||||
|
||||
Register extractString(const Address &address, Register scratch) {
|
||||
return extractObject(address, scratch);
|
||||
|
@ -625,6 +620,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
Register extractObject(const TypedOrValueRegister ®, Register scratch) {
|
||||
if (reg.hasValue())
|
||||
return extractObject(reg.valueReg(), scratch);
|
||||
JS_ASSERT(reg.type() == MIRType_Object);
|
||||
return reg.typedReg().gpr();
|
||||
}
|
||||
|
||||
|
@ -634,7 +630,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
|
||||
using MacroAssemblerSpecific::ensureDouble;
|
||||
|
||||
void ensureDouble(const Address &source, FloatRegister dest, Label *failure) {
|
||||
template <typename S>
|
||||
void ensureDouble(const S &source, FloatRegister dest, Label *failure) {
|
||||
Label isDouble, done;
|
||||
branchTestDouble(Assembler::Equal, source, &isDouble);
|
||||
branchTestInt32(Assembler::NotEqual, source, failure);
|
||||
|
@ -650,7 +647,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
|
||||
// Emit type case branch on tag matching if the type tag in the definition
|
||||
// might actually be that type.
|
||||
void branchEqualTypeIfNeeded(MIRType type, MDefinition *def, const Register &tag,
|
||||
void branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, const Register &tag,
|
||||
Label *label);
|
||||
|
||||
// Inline allocation.
|
||||
|
@ -1001,7 +998,151 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
|
||||
void convertInt32ValueToDouble(const Address &address, Register scratch, Label *done);
|
||||
void convertValueToDouble(ValueOperand value, FloatRegister output, Label *fail);
|
||||
void convertValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label *fail);
|
||||
bool convertValueToDouble(JSContext *cx, const Value &v, FloatRegister output, Label *fail);
|
||||
bool convertConstantOrRegisterToDouble(JSContext *cx, ConstantOrRegister src,
|
||||
FloatRegister output, Label *fail);
|
||||
void convertTypedOrValueToDouble(TypedOrValueRegister src, FloatRegister output, Label *fail);
|
||||
|
||||
enum IntConversionBehavior {
|
||||
IntConversion_Normal,
|
||||
IntConversion_NegativeZeroCheck,
|
||||
IntConversion_Truncate,
|
||||
IntConversion_ClampToUint8,
|
||||
};
|
||||
|
||||
//
|
||||
// Functions for converting values to int.
|
||||
//
|
||||
void convertDoubleToInt(FloatRegister src, Register output, Label *fail,
|
||||
IntConversionBehavior behavior);
|
||||
|
||||
// Strings may be handled by providing labels to jump to when the behavior
|
||||
// is truncation or clamping. The subroutine, usually an OOL call, is
|
||||
// passed the unboxed string in |stringReg| and should convert it to a
|
||||
// double store into |temp|.
|
||||
void convertValueToInt(ValueOperand value, MDefinition *input,
|
||||
Label *handleStringEntry, Label *handleStringRejoin,
|
||||
Register stringReg, FloatRegister temp, Register output,
|
||||
Label *fail, IntConversionBehavior behavior);
|
||||
void convertValueToInt(ValueOperand value, FloatRegister temp, Register output, Label *fail,
|
||||
IntConversionBehavior behavior)
|
||||
{
|
||||
convertValueToInt(value, NULL, NULL, NULL, InvalidReg, temp, output, fail, behavior);
|
||||
}
|
||||
bool convertValueToInt(JSContext *cx, const Value &v, Register output, Label *fail,
|
||||
IntConversionBehavior behavior);
|
||||
bool convertConstantOrRegisterToInt(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
|
||||
Register output, Label *fail, IntConversionBehavior behavior);
|
||||
void convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp, Register output,
|
||||
Label *fail, IntConversionBehavior behavior);
|
||||
|
||||
//
|
||||
// Convenience functions for converting values to int32.
|
||||
//
|
||||
void convertValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label *fail,
|
||||
bool negativeZeroCheck)
|
||||
{
|
||||
convertValueToInt(value, temp, output, fail, negativeZeroCheck
|
||||
? IntConversion_NegativeZeroCheck
|
||||
: IntConversion_Normal);
|
||||
}
|
||||
void convertValueToInt32(ValueOperand value, MDefinition *input,
|
||||
FloatRegister temp, Register output, Label *fail,
|
||||
bool negativeZeroCheck)
|
||||
{
|
||||
convertValueToInt(value, input, NULL, NULL, InvalidReg, temp, output, fail,
|
||||
negativeZeroCheck
|
||||
? IntConversion_NegativeZeroCheck
|
||||
: IntConversion_Normal);
|
||||
}
|
||||
bool convertValueToInt32(JSContext *cx, const Value &v, Register output, Label *fail,
|
||||
bool negativeZeroCheck)
|
||||
{
|
||||
return convertValueToInt(cx, v, output, fail, negativeZeroCheck
|
||||
? IntConversion_NegativeZeroCheck
|
||||
: IntConversion_Normal);
|
||||
}
|
||||
bool convertConstantOrRegisterToInt32(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
|
||||
Register output, Label *fail, bool negativeZeroCheck)
|
||||
{
|
||||
return convertConstantOrRegisterToInt(cx, src, temp, output, fail, negativeZeroCheck
|
||||
? IntConversion_NegativeZeroCheck
|
||||
: IntConversion_Normal);
|
||||
}
|
||||
void convertTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
|
||||
Label *fail, bool negativeZeroCheck)
|
||||
{
|
||||
convertTypedOrValueToInt(src, temp, output, fail, negativeZeroCheck
|
||||
? IntConversion_NegativeZeroCheck
|
||||
: IntConversion_Normal);
|
||||
}
|
||||
|
||||
//
|
||||
// Convenience functions for truncating values to int32.
|
||||
//
|
||||
void truncateValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label *fail) {
|
||||
convertValueToInt(value, temp, output, fail, IntConversion_Truncate);
|
||||
}
|
||||
void truncateValueToInt32(ValueOperand value, MDefinition *input,
|
||||
Label *handleStringEntry, Label *handleStringRejoin,
|
||||
Register stringReg, FloatRegister temp, Register output,
|
||||
Label *fail)
|
||||
{
|
||||
convertValueToInt(value, input, handleStringEntry, handleStringRejoin,
|
||||
stringReg, temp, output, fail, IntConversion_Truncate);
|
||||
}
|
||||
void truncateValueToInt32(ValueOperand value, MDefinition *input,
|
||||
FloatRegister temp, Register output, Label *fail)
|
||||
{
|
||||
convertValueToInt(value, input, NULL, NULL, InvalidReg, temp, output, fail,
|
||||
IntConversion_Truncate);
|
||||
}
|
||||
bool truncateValueToInt32(JSContext *cx, const Value &v, Register output, Label *fail) {
|
||||
return convertValueToInt(cx, v, output, fail, IntConversion_Truncate);
|
||||
}
|
||||
bool truncateConstantOrRegisterToInt32(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
|
||||
Register output, Label *fail)
|
||||
{
|
||||
return convertConstantOrRegisterToInt(cx, src, temp, output, fail, IntConversion_Truncate);
|
||||
}
|
||||
void truncateTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
|
||||
Label *fail)
|
||||
{
|
||||
convertTypedOrValueToInt(src, temp, output, fail, IntConversion_Truncate);
|
||||
}
|
||||
|
||||
// Convenience functions for clamping values to uint8.
|
||||
void clampValueToUint8(ValueOperand value, FloatRegister temp, Register output, Label *fail) {
|
||||
convertValueToInt(value, temp, output, fail, IntConversion_ClampToUint8);
|
||||
}
|
||||
void clampValueToUint8(ValueOperand value, MDefinition *input,
|
||||
Label *handleStringEntry, Label *handleStringRejoin,
|
||||
Register stringReg, FloatRegister temp, Register output,
|
||||
Label *fail)
|
||||
{
|
||||
convertValueToInt(value, input, handleStringEntry, handleStringRejoin,
|
||||
stringReg, temp, output, fail, IntConversion_ClampToUint8);
|
||||
}
|
||||
void clampValueToUint8(ValueOperand value, MDefinition *input,
|
||||
FloatRegister temp, Register output, Label *fail)
|
||||
{
|
||||
convertValueToInt(value, input, NULL, NULL, InvalidReg, temp, output, fail,
|
||||
IntConversion_ClampToUint8);
|
||||
}
|
||||
bool clampValueToUint8(JSContext *cx, const Value &v, Register output, Label *fail) {
|
||||
return convertValueToInt(cx, v, output, fail, IntConversion_ClampToUint8);
|
||||
}
|
||||
bool clampConstantOrRegisterToUint8(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
|
||||
Register output, Label *fail)
|
||||
{
|
||||
return convertConstantOrRegisterToInt(cx, src, temp, output, fail,
|
||||
IntConversion_ClampToUint8);
|
||||
}
|
||||
void clampTypedOrValueToUint8(TypedOrValueRegister src, FloatRegister temp, Register output,
|
||||
Label *fail)
|
||||
{
|
||||
convertTypedOrValueToInt(src, temp, output, fail, IntConversion_ClampToUint8);
|
||||
}
|
||||
};
|
||||
|
||||
static inline Assembler::DoubleCondition
|
||||
|
|
|
@ -83,6 +83,7 @@ enum MIRType
|
|||
MIRType_Boolean,
|
||||
MIRType_Int32,
|
||||
MIRType_Double,
|
||||
MIRType_Float32,
|
||||
MIRType_String,
|
||||
MIRType_Object,
|
||||
MIRType_Magic,
|
||||
|
@ -134,6 +135,7 @@ ValueTypeFromMIRType(MIRType type)
|
|||
return JSVAL_TYPE_BOOLEAN;
|
||||
case MIRType_Int32:
|
||||
return JSVAL_TYPE_INT32;
|
||||
case MIRType_Float32: // Fall through, there's no JSVAL for Float32
|
||||
case MIRType_Double:
|
||||
return JSVAL_TYPE_DOUBLE;
|
||||
case MIRType_String:
|
||||
|
@ -166,6 +168,8 @@ StringFromMIRType(MIRType type)
|
|||
return "Int32";
|
||||
case MIRType_Double:
|
||||
return "Double";
|
||||
case MIRType_Float32:
|
||||
return "Float32";
|
||||
case MIRType_String:
|
||||
return "String";
|
||||
case MIRType_Object:
|
||||
|
@ -192,7 +196,19 @@ StringFromMIRType(MIRType type)
|
|||
static inline bool
|
||||
IsNumberType(MIRType type)
|
||||
{
|
||||
return type == MIRType_Int32 || type == MIRType_Double;
|
||||
return type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsFloatType(MIRType type)
|
||||
{
|
||||
return type == MIRType_Int32 || type == MIRType_Float32;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsFloatingPointType(MIRType type)
|
||||
{
|
||||
return type == MIRType_Double || type == MIRType_Float32;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
|
|
|
@ -248,7 +248,7 @@ Loop::requiresHoistedUse(const MDefinition *ins) const
|
|||
// hoisting on their own, in general. Floating-point constants typically
|
||||
// are worth hoisting, unless they'll end up being spilled (eg. due to a
|
||||
// call).
|
||||
if (ins->isConstant() && (ins->type() != MIRType_Double || containsPossibleCall_))
|
||||
if (ins->isConstant() && (IsFloatingPointType(ins->type()) || containsPossibleCall_))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
|
@ -188,6 +188,22 @@ class LDouble : public LInstructionHelper<1, 0, 0>
|
|||
}
|
||||
};
|
||||
|
||||
// Constant float32.
|
||||
class LFloat32 : public LInstructionHelper<1, 0, 0>
|
||||
{
|
||||
float f_;
|
||||
public:
|
||||
LIR_HEADER(Float32);
|
||||
|
||||
LFloat32(float f)
|
||||
: f_(f)
|
||||
{ }
|
||||
|
||||
float getFloat() const {
|
||||
return f_;
|
||||
}
|
||||
};
|
||||
|
||||
// A constant Value.
|
||||
class LValue : public LInstructionHelper<BOX_PIECES, 0, 0>
|
||||
{
|
||||
|
@ -2102,6 +2118,16 @@ class LNegD : public LInstructionHelper<1, 1, 0>
|
|||
}
|
||||
};
|
||||
|
||||
// Negative of a float32.
|
||||
class LNegF : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(NegF)
|
||||
LNegF(const LAllocation &num) {
|
||||
setOperand(0, num);
|
||||
}
|
||||
};
|
||||
|
||||
// Absolute value of an integer.
|
||||
class LAbsI : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
|
@ -2302,6 +2328,23 @@ class LMathD : public LBinaryMath<0>
|
|||
}
|
||||
};
|
||||
|
||||
// Performs an add, sub, mul, or div on two double values.
|
||||
class LMathF: public LBinaryMath<0>
|
||||
{
|
||||
JSOp jsop_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(MathF)
|
||||
|
||||
LMathF(JSOp jsop)
|
||||
: jsop_(jsop)
|
||||
{ }
|
||||
|
||||
JSOp jsop() const {
|
||||
return jsop_;
|
||||
}
|
||||
};
|
||||
|
||||
class LModD : public LBinaryMath<1>
|
||||
{
|
||||
public:
|
||||
|
@ -2458,6 +2501,39 @@ class LInt32ToDouble : public LInstructionHelper<1, 1, 0>
|
|||
}
|
||||
};
|
||||
|
||||
// Convert a 32-bit float to a double.
|
||||
class LFloat32ToDouble : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Float32ToDouble)
|
||||
|
||||
LFloat32ToDouble(const LAllocation &input) {
|
||||
setOperand(0, input);
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a double to a 32-bit float.
|
||||
class LDoubleToFloat32 : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(DoubleToFloat32)
|
||||
|
||||
LDoubleToFloat32(const LAllocation &input) {
|
||||
setOperand(0, input);
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a 32-bit integer to a float32.
|
||||
class LInt32ToFloat32 : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Int32ToFloat32)
|
||||
|
||||
LInt32ToFloat32(const LAllocation &input) {
|
||||
setOperand(0, input);
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a value to a double.
|
||||
class LValueToDouble : public LInstructionHelper<1, BOX_PIECES, 0>
|
||||
{
|
||||
|
@ -2470,6 +2546,18 @@ class LValueToDouble : public LInstructionHelper<1, BOX_PIECES, 0>
|
|||
}
|
||||
};
|
||||
|
||||
// Convert a value to a float32.
|
||||
class LValueToFloat32 : public LInstructionHelper<1, BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(ValueToFloat32)
|
||||
static const size_t Input = 0;
|
||||
|
||||
MToFloat32 *mir() {
|
||||
return mir_->toToFloat32();
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a value to an int32.
|
||||
// Input: components of a Value
|
||||
// Output: 32-bit integer
|
||||
|
@ -3559,6 +3647,9 @@ class LClampVToUint8 : public LInstructionHelper<1, BOX_PIECES, 1>
|
|||
const LDefinition *tempFloat() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const MClampToUint8 *mir() const {
|
||||
return mir_->toClampToUint8();
|
||||
}
|
||||
};
|
||||
|
||||
// Load a boxed value from an object's fixed slot.
|
||||
|
@ -4168,7 +4259,7 @@ class LSetPropertyCacheT : public LInstructionHelper<0, 2, 1>
|
|||
}
|
||||
};
|
||||
|
||||
class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 2>
|
||||
class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 3>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SetElementCacheV);
|
||||
|
@ -4177,11 +4268,12 @@ class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 2>
|
|||
static const size_t Value = 1 + BOX_PIECES;
|
||||
|
||||
LSetElementCacheV(const LAllocation &object, const LDefinition &tempToUnboxIndex,
|
||||
const LDefinition &temp)
|
||||
const LDefinition &temp, const LDefinition &tempFloat)
|
||||
{
|
||||
setOperand(0, object);
|
||||
setTemp(0, tempToUnboxIndex);
|
||||
setTemp(1, temp);
|
||||
setTemp(2, tempFloat);
|
||||
}
|
||||
const MSetElementCache *mir() const {
|
||||
return mir_->toSetElementCache();
|
||||
|
@ -4196,9 +4288,12 @@ class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 2>
|
|||
const LDefinition *temp() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition *tempFloat() {
|
||||
return getTemp(2);
|
||||
}
|
||||
};
|
||||
|
||||
class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 2>
|
||||
class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 3>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SetElementCacheT);
|
||||
|
@ -4206,11 +4301,13 @@ class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 2>
|
|||
static const size_t Index = 2;
|
||||
|
||||
LSetElementCacheT(const LAllocation &object, const LAllocation &value,
|
||||
const LDefinition &tempToUnboxIndex, const LDefinition &temp) {
|
||||
const LDefinition &tempToUnboxIndex,
|
||||
const LDefinition &temp, const LDefinition &tempFloat) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, value);
|
||||
setTemp(0, tempToUnboxIndex);
|
||||
setTemp(1, temp);
|
||||
setTemp(2, tempFloat);
|
||||
}
|
||||
const MSetElementCache *mir() const {
|
||||
return mir_->toSetElementCache();
|
||||
|
@ -4228,6 +4325,9 @@ class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 2>
|
|||
const LDefinition *temp() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition *tempFloat() {
|
||||
return getTemp(2);
|
||||
}
|
||||
};
|
||||
|
||||
class LCallIteratorStart : public LCallInstructionHelper<1, 1, 0>
|
||||
|
@ -4449,12 +4549,12 @@ class LGuardThreadLocalObject : public LCallInstructionHelper<0, 2, 1>
|
|||
};
|
||||
|
||||
// Guard that a value is in a TypeSet.
|
||||
class LTypeBarrier : public LInstructionHelper<0, BOX_PIECES, 1>
|
||||
class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(TypeBarrier)
|
||||
LIR_HEADER(TypeBarrierV)
|
||||
|
||||
LTypeBarrier(const LDefinition &temp) {
|
||||
LTypeBarrierV(const LDefinition &temp) {
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
|
@ -4468,6 +4568,27 @@ class LTypeBarrier : public LInstructionHelper<0, BOX_PIECES, 1>
|
|||
}
|
||||
};
|
||||
|
||||
// Guard that a object is in a TypeSet.
|
||||
class LTypeBarrierO : public LInstructionHelper<0, 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(TypeBarrierO)
|
||||
|
||||
LTypeBarrierO(const LAllocation &obj, const LDefinition &temp) {
|
||||
setOperand(0, obj);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
const MTypeBarrier *mir() const {
|
||||
return mir_->toTypeBarrier();
|
||||
}
|
||||
const LAllocation *object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Guard that a value is in a TypeSet.
|
||||
class LMonitorTypes : public LInstructionHelper<0, BOX_PIECES, 1>
|
||||
{
|
||||
|
@ -4983,6 +5104,32 @@ class LAssertRangeD : public LInstructionHelper<0, 1, 1>
|
|||
}
|
||||
};
|
||||
|
||||
class LAssertRangeF : public LInstructionHelper<0, 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(AssertRangeF)
|
||||
|
||||
LAssertRangeF(const LAllocation &input, const LDefinition &temp) {
|
||||
setOperand(0, input);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LAllocation *input() {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
|
||||
MAssertRange *mir() {
|
||||
return mir_->toAssertRange();
|
||||
}
|
||||
Range *range() {
|
||||
return mir()->range();
|
||||
}
|
||||
};
|
||||
|
||||
class LAssertRangeV : public LInstructionHelper<0, BOX_PIECES, 3>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -540,6 +540,7 @@ class LDefinition
|
|||
case MIRType_Object:
|
||||
return LDefinition::OBJECT;
|
||||
case MIRType_Double:
|
||||
case MIRType_Float32:
|
||||
return LDefinition::DOUBLE;
|
||||
#if defined(JS_PUNBOX64)
|
||||
case MIRType_Value:
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
_(Integer) \
|
||||
_(Pointer) \
|
||||
_(Double) \
|
||||
_(Float32) \
|
||||
_(Value) \
|
||||
_(Parameter) \
|
||||
_(Callee) \
|
||||
|
@ -92,6 +93,7 @@
|
|||
_(MinMaxD) \
|
||||
_(NegI) \
|
||||
_(NegD) \
|
||||
_(NegF) \
|
||||
_(AbsI) \
|
||||
_(AbsD) \
|
||||
_(SqrtD) \
|
||||
|
@ -108,6 +110,7 @@
|
|||
_(SubI) \
|
||||
_(MulI) \
|
||||
_(MathD) \
|
||||
_(MathF) \
|
||||
_(ModD) \
|
||||
_(BinaryV) \
|
||||
_(Concat) \
|
||||
|
@ -115,8 +118,12 @@
|
|||
_(CharCodeAt) \
|
||||
_(FromCharCode) \
|
||||
_(Int32ToDouble) \
|
||||
_(Float32ToDouble) \
|
||||
_(DoubleToFloat32) \
|
||||
_(Int32ToFloat32) \
|
||||
_(ValueToDouble) \
|
||||
_(ValueToInt32) \
|
||||
_(ValueToFloat32) \
|
||||
_(DoubleToInt32) \
|
||||
_(TruncateDToInt32) \
|
||||
_(IntToString) \
|
||||
|
@ -143,7 +150,8 @@
|
|||
_(GuardObjectType) \
|
||||
_(GuardClass) \
|
||||
_(GuardThreadLocalObject) \
|
||||
_(TypeBarrier) \
|
||||
_(TypeBarrierV) \
|
||||
_(TypeBarrierO) \
|
||||
_(MonitorTypes) \
|
||||
_(PostWriteBarrierO) \
|
||||
_(PostWriteBarrierV) \
|
||||
|
@ -248,6 +256,7 @@
|
|||
_(CheckInterruptPar) \
|
||||
_(AssertRangeI) \
|
||||
_(AssertRangeD) \
|
||||
_(AssertRangeF) \
|
||||
_(AssertRangeV)
|
||||
|
||||
#if defined(JS_CPU_X86)
|
||||
|
|
|
@ -36,14 +36,17 @@ class Requirement
|
|||
Requirement(Kind kind)
|
||||
: kind_(kind)
|
||||
{
|
||||
// These have dedicated constructors;
|
||||
// These have dedicated constructors.
|
||||
JS_ASSERT(kind != FIXED && kind != SAME_AS_OTHER);
|
||||
}
|
||||
|
||||
Requirement(Kind kind, CodePosition at)
|
||||
: kind_(kind),
|
||||
position_(at)
|
||||
{ }
|
||||
{
|
||||
// These have dedicated constructors.
|
||||
JS_ASSERT(kind != FIXED && kind != SAME_AS_OTHER);
|
||||
}
|
||||
|
||||
Requirement(LAllocation fixed)
|
||||
: kind_(FIXED),
|
||||
|
|
|
@ -1241,6 +1241,12 @@ LIRGenerator::visitAdd(MAdd *ins)
|
|||
return lowerForFPU(new LMathD(JSOP_ADD), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
return lowerForFPU(new LMathF(JSOP_ADD), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
return lowerBinaryV(JSOP_ADD, ins);
|
||||
}
|
||||
|
||||
|
@ -1269,6 +1275,10 @@ LIRGenerator::visitSub(MSub *ins)
|
|||
JS_ASSERT(lhs->type() == MIRType_Double);
|
||||
return lowerForFPU(new LMathD(JSOP_SUB), ins, lhs, rhs);
|
||||
}
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
return lowerForFPU(new LMathF(JSOP_SUB), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
return lowerBinaryV(JSOP_SUB, ins);
|
||||
}
|
||||
|
@ -1295,6 +1305,18 @@ LIRGenerator::visitMul(MMul *ins)
|
|||
|
||||
return lowerForFPU(new LMathD(JSOP_MUL), ins, lhs, rhs);
|
||||
}
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
|
||||
// We apply the same optimizations as for doubles
|
||||
if (lhs->isConstant() && lhs->toConstant()->value() == JS::Float32Value(-1.0f))
|
||||
return defineReuseInput(new LNegF(useRegisterAtStart(rhs)), ins, 0);
|
||||
if (rhs->isConstant() && rhs->toConstant()->value() == JS::Float32Value(-1.0f))
|
||||
return defineReuseInput(new LNegF(useRegisterAtStart(lhs)), ins, 0);
|
||||
|
||||
return lowerForFPU(new LMathF(JSOP_MUL), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
return lowerBinaryV(JSOP_MUL, ins);
|
||||
}
|
||||
|
@ -1314,6 +1336,10 @@ LIRGenerator::visitDiv(MDiv *ins)
|
|||
JS_ASSERT(lhs->type() == MIRType_Double);
|
||||
return lowerForFPU(new LMathD(JSOP_DIV), ins, lhs, rhs);
|
||||
}
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
return lowerForFPU(new LMathF(JSOP_DIV), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
return lowerBinaryV(JSOP_DIV, ins);
|
||||
}
|
||||
|
@ -1506,6 +1532,12 @@ LIRGenerator::visitToDouble(MToDouble *convert)
|
|||
return define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Float32:
|
||||
{
|
||||
LFloat32ToDouble *lir = new LFloat32ToDouble(useRegister(opd));
|
||||
return define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Double:
|
||||
return redefine(convert, opd);
|
||||
|
||||
|
@ -1516,6 +1548,56 @@ LIRGenerator::visitToDouble(MToDouble *convert)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitToFloat32(MToFloat32 *convert)
|
||||
{
|
||||
MDefinition *opd = convert->input();
|
||||
mozilla::DebugOnly<MToFloat32::ConversionKind> conversion = convert->conversion();
|
||||
|
||||
switch (opd->type()) {
|
||||
case MIRType_Value:
|
||||
{
|
||||
LValueToFloat32 *lir = new LValueToFloat32();
|
||||
if (!useBox(lir, LValueToFloat32::Input, opd))
|
||||
return false;
|
||||
return assignSnapshot(lir) && define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Null:
|
||||
JS_ASSERT(conversion != MToFloat32::NonStringPrimitives);
|
||||
return lowerConstantFloat32(0, convert);
|
||||
|
||||
case MIRType_Undefined:
|
||||
JS_ASSERT(conversion != MToFloat32::NumbersOnly);
|
||||
return lowerConstantFloat32(js_NaN, convert);
|
||||
|
||||
case MIRType_Boolean:
|
||||
JS_ASSERT(conversion != MToFloat32::NumbersOnly);
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case MIRType_Int32:
|
||||
{
|
||||
LInt32ToFloat32 *lir = new LInt32ToFloat32(useRegister(opd));
|
||||
return define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Double:
|
||||
{
|
||||
LDoubleToFloat32 *lir = new LDoubleToFloat32(useRegister(opd));
|
||||
return define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Float32:
|
||||
return redefine(convert, opd);
|
||||
|
||||
default:
|
||||
// Objects might be effectful.
|
||||
// Strings are complicated - we don't handle them yet.
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitToInt32(MToInt32 *convert)
|
||||
{
|
||||
|
@ -1822,6 +1904,9 @@ LIRGenerator::visitStoreSlot(MStoreSlot *ins)
|
|||
case MIRType_Double:
|
||||
return add(new LStoreSlotT(useRegister(ins->slots()), useRegister(ins->value())), ins);
|
||||
|
||||
case MIRType_Float32:
|
||||
MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be stored in a slot.");
|
||||
|
||||
default:
|
||||
return add(new LStoreSlotT(useRegister(ins->slots()), useRegisterOrConstant(ins->value())),
|
||||
ins);
|
||||
|
@ -1838,14 +1923,47 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
|
|||
|
||||
const types::StackTypeSet *types = ins->resultTypeSet();
|
||||
bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
|
||||
LDefinition tmp = needTemp ? temp() : tempToUnbox();
|
||||
|
||||
LTypeBarrier *barrier = new LTypeBarrier(tmp);
|
||||
if (!useBox(barrier, LTypeBarrier::Input, ins->input()))
|
||||
return false;
|
||||
if (!assignSnapshot(barrier, ins->bailoutKind()))
|
||||
return false;
|
||||
return redefine(ins, ins->input()) && add(barrier, ins);
|
||||
MIRType inputType = ins->getOperand(0)->type();
|
||||
MIRType outputType = ins->type();
|
||||
|
||||
JS_ASSERT(inputType == outputType);
|
||||
JS_ASSERT_IF(ins->alwaysBails(), outputType == MIRType_Value);
|
||||
|
||||
// Handle typebarrier that will always bail.
|
||||
// (Emit LBail for visibility).
|
||||
if (ins->alwaysBails()) {
|
||||
JS_ASSERT(outputType == MIRType_Value);
|
||||
|
||||
LBail *bail = new LBail();
|
||||
if (!assignSnapshot(bail, ins->bailoutKind()))
|
||||
return false;
|
||||
return redefine(ins, ins->input()) && add(bail, ins);
|
||||
}
|
||||
|
||||
// Handle typebarrier with Value as input.
|
||||
if (inputType == MIRType_Value) {
|
||||
LDefinition tmp = needTemp ? temp() : tempToUnbox();
|
||||
LTypeBarrierV *barrier = new LTypeBarrierV(tmp);
|
||||
if (!useBox(barrier, LTypeBarrierV::Input, ins->input()))
|
||||
return false;
|
||||
if (!assignSnapshot(barrier, ins->bailoutKind()))
|
||||
return false;
|
||||
return redefine(ins, ins->input()) && add(barrier, ins);
|
||||
}
|
||||
|
||||
// Handle typebarrier with specific TypeObject/SingleObjects.
|
||||
if (inputType == MIRType_Object && !types->hasType(types::Type::AnyObjectType()))
|
||||
{
|
||||
LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp();
|
||||
LTypeBarrierO *barrier = new LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
|
||||
if (!assignSnapshot(barrier, ins->bailoutKind()))
|
||||
return false;
|
||||
return redefine(ins, ins->getOperand(0)) && add(barrier, ins);
|
||||
}
|
||||
|
||||
// Handle remaining cases: No-op, unbox did everything.
|
||||
return redefine(ins, ins->getOperand(0));
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2219,7 +2337,7 @@ LIRGenerator::visitLoadTypedArrayElement(MLoadTypedArrayElement *ins)
|
|||
|
||||
// We need a temp register for Uint32Array with known double result.
|
||||
LDefinition tempDef = LDefinition::BogusTemp();
|
||||
if (ins->arrayType() == ScalarTypeRepresentation::TYPE_UINT32 && ins->type() == MIRType_Double)
|
||||
if (ins->arrayType() == ScalarTypeRepresentation::TYPE_UINT32 && IsFloatingPointType(ins->type()))
|
||||
tempDef = temp();
|
||||
|
||||
LLoadTypedArrayElement *lir = new LLoadTypedArrayElement(elements, index, tempDef);
|
||||
|
@ -2248,7 +2366,7 @@ LIRGenerator::visitClampToUint8(MClampToUint8 *ins)
|
|||
LClampVToUint8 *lir = new LClampVToUint8(tempFloat());
|
||||
if (!useBox(lir, LClampVToUint8::Input, in))
|
||||
return false;
|
||||
return assignSnapshot(lir) && define(lir, ins);
|
||||
return assignSnapshot(lir) && define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -2284,6 +2402,58 @@ LIRGenerator::visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic *ins
|
|||
return define(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
|
||||
{
|
||||
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
|
||||
JS_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
|
||||
if (ins->isFloatArray()) {
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
|
||||
} else {
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Int32);
|
||||
}
|
||||
|
||||
LUse elements = useRegister(ins->elements());
|
||||
LAllocation index = useRegisterOrConstant(ins->index());
|
||||
LAllocation value;
|
||||
|
||||
// For byte arrays, the value has to be in a byte register on x86.
|
||||
if (ins->isByteArray())
|
||||
value = useByteOpRegisterOrNonDoubleConstant(ins->value());
|
||||
else
|
||||
value = useRegisterOrNonDoubleConstant(ins->value());
|
||||
return add(new LStoreTypedArrayElement(elements, index, value), ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins)
|
||||
{
|
||||
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
|
||||
JS_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
JS_ASSERT(ins->length()->type() == MIRType_Int32);
|
||||
|
||||
if (ins->isFloatArray()) {
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
|
||||
} else {
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Int32);
|
||||
}
|
||||
|
||||
LUse elements = useRegister(ins->elements());
|
||||
LAllocation length = useAnyOrConstant(ins->length());
|
||||
LAllocation index = useRegisterOrConstant(ins->index());
|
||||
LAllocation value;
|
||||
|
||||
// For byte arrays, the value has to be in a byte register on x86.
|
||||
if (ins->isByteArray())
|
||||
value = useByteOpRegisterOrNonDoubleConstant(ins->value());
|
||||
else
|
||||
value = useRegisterOrNonDoubleConstant(ins->value());
|
||||
return add(new LStoreTypedArrayElementHole(elements, length, index, value), ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot *ins)
|
||||
{
|
||||
|
@ -2467,6 +2637,10 @@ LIRGenerator::visitAssertRange(MAssertRange *ins)
|
|||
lir = new LAssertRangeD(useRegister(input), tempFloat());
|
||||
break;
|
||||
|
||||
case MIRType_Float32:
|
||||
lir = new LAssertRangeF(useRegister(input), tempFloat());
|
||||
break;
|
||||
|
||||
case MIRType_Value:
|
||||
lir = new LAssertRangeV(tempToUnbox(), tempFloat(), tempFloat());
|
||||
if (!useBox(lir, LAssertRangeV::Input, input))
|
||||
|
@ -2566,9 +2740,14 @@ LIRGenerator::visitSetElementCache(MSetElementCache *ins)
|
|||
JS_ASSERT(ins->object()->type() == MIRType_Object);
|
||||
JS_ASSERT(ins->index()->type() == MIRType_Value);
|
||||
|
||||
// Due to lack of registers on x86, we reuse the object register as a
|
||||
// temporary. This register may be used in a 1-byte store, which on x86
|
||||
// again has constraints; thus the use of |useByteOpRegister| over
|
||||
// |useRegister| below.
|
||||
LInstruction *lir;
|
||||
if (ins->value()->type() == MIRType_Value) {
|
||||
lir = new LSetElementCacheV(useRegister(ins->object()), tempToUnbox(), temp());
|
||||
lir = new LSetElementCacheV(useByteOpRegister(ins->object()), tempToUnbox(),
|
||||
temp(), tempFloat());
|
||||
|
||||
if (!useBox(lir, LSetElementCacheV::Index, ins->index()))
|
||||
return false;
|
||||
|
@ -2576,9 +2755,9 @@ LIRGenerator::visitSetElementCache(MSetElementCache *ins)
|
|||
return false;
|
||||
} else {
|
||||
lir = new LSetElementCacheT(
|
||||
useRegister(ins->object()),
|
||||
useByteOpRegister(ins->object()),
|
||||
useRegisterOrConstant(ins->value()),
|
||||
tempToUnbox(), temp());
|
||||
tempToUnbox(), temp(), tempFloat());
|
||||
|
||||
if (!useBox(lir, LSetElementCacheT::Index, ins->index()))
|
||||
return false;
|
||||
|
|
|
@ -153,6 +153,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
bool visitOsrValue(MOsrValue *value);
|
||||
bool visitOsrScopeChain(MOsrScopeChain *object);
|
||||
bool visitToDouble(MToDouble *convert);
|
||||
bool visitToFloat32(MToFloat32 *convert);
|
||||
bool visitToInt32(MToInt32 *convert);
|
||||
bool visitTruncateToInt32(MTruncateToInt32 *truncate);
|
||||
bool visitToString(MToString *convert);
|
||||
|
@ -195,6 +196,8 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
bool visitLoadTypedArrayElement(MLoadTypedArrayElement *ins);
|
||||
bool visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole *ins);
|
||||
bool visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic *ins);
|
||||
bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
|
||||
bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
|
||||
bool visitClampToUint8(MClampToUint8 *ins);
|
||||
bool visitLoadFixedSlot(MLoadFixedSlot *ins);
|
||||
bool visitStoreFixedSlot(MStoreFixedSlot *ins);
|
||||
|
|
|
@ -792,9 +792,9 @@ IonBuilder::inlineMathImul(CallInfo &callInfo)
|
|||
if (returnType != MIRType_Int32)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (!IsNumberType(callInfo.getArg(0)->type()))
|
||||
if (!IsNumberType(callInfo.getArg(0)->type()) || callInfo.getArg(0)->type() == MIRType_Float32)
|
||||
return InliningStatus_NotInlined;
|
||||
if (!IsNumberType(callInfo.getArg(1)->type()))
|
||||
if (!IsNumberType(callInfo.getArg(1)->type()) || callInfo.getArg(1)->type() == MIRType_Float32)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.unwrapArgs();
|
||||
|
|
|
@ -452,6 +452,12 @@ MConstant::printOpcode(FILE *fp) const
|
|||
case MIRType_Double:
|
||||
fprintf(fp, "%f", value().toDouble());
|
||||
break;
|
||||
case MIRType_Float32:
|
||||
{
|
||||
float val = value().toDouble();
|
||||
fprintf(fp, "%f", val);
|
||||
break;
|
||||
}
|
||||
case MIRType_Object:
|
||||
if (value().toObject().is<JSFunction>()) {
|
||||
JSFunction *fun = &value().toObject().as<JSFunction>();
|
||||
|
@ -483,6 +489,28 @@ MConstant::printOpcode(FILE *fp) const
|
|||
}
|
||||
}
|
||||
|
||||
// Needs a static function to avoid overzealous optimizations by certain compilers (MSVC).
|
||||
static bool
|
||||
IsFloat32Representable(double x)
|
||||
{
|
||||
float asFloat = static_cast<float>(x);
|
||||
double floatAsDouble = static_cast<double>(asFloat);
|
||||
return floatAsDouble == x;
|
||||
}
|
||||
|
||||
bool
|
||||
MConstant::canProduceFloat32() const
|
||||
{
|
||||
if (!IsNumberType(type()))
|
||||
return false;
|
||||
|
||||
if (type() == MIRType_Int32)
|
||||
return IsFloat32Representable(static_cast<double>(value_.toInt32()));
|
||||
if (type() == MIRType_Double)
|
||||
return IsFloat32Representable(value_.toDouble());
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MControlInstruction::printOpcode(FILE *fp) const
|
||||
{
|
||||
|
@ -599,6 +627,21 @@ MGoto::New(MBasicBlock *target)
|
|||
return new MGoto(target);
|
||||
}
|
||||
|
||||
static void
|
||||
PrintBailoutKind(FILE *fp, BailoutKind bailoutKind)
|
||||
{
|
||||
switch(bailoutKind) {
|
||||
case Bailout_Normal: fprintf(fp, "(normal)"); break;
|
||||
case Bailout_ArgumentCheck: fprintf(fp, "(args)"); break;
|
||||
case Bailout_TypeBarrier: fprintf(fp, "(typebarrier)"); break;
|
||||
case Bailout_Monitor: fprintf(fp, "(monitor)"); break;
|
||||
case Bailout_BoundsCheck: fprintf(fp, "(boundscheck)"); break;
|
||||
case Bailout_ShapeGuard: fprintf(fp, "(shapeguard)"); break;
|
||||
case Bailout_CachedShapeGuard: fprintf(fp, "(cached shapeguard)"); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MUnbox::printOpcode(FILE *fp) const
|
||||
{
|
||||
|
@ -620,11 +663,27 @@ MUnbox::printOpcode(FILE *fp) const
|
|||
case Fallible: fprintf(fp, " (fallible)"); break;
|
||||
case Infallible: fprintf(fp, " (infallible)"); break;
|
||||
case TypeBarrier: fprintf(fp, " (typebarrier)"); break;
|
||||
case TypeGuard: fprintf(fp, " (typeguard)"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (mode() == Infallible)
|
||||
return;
|
||||
|
||||
fprintf(fp, " ");
|
||||
PrintBailoutKind(fp, bailoutKind());
|
||||
}
|
||||
|
||||
void
|
||||
MTypeBarrier::printOpcode(FILE *fp) const
|
||||
{
|
||||
PrintOpcodeName(fp, op());
|
||||
fprintf(fp, " ");
|
||||
getOperand(0)->printName(fp);
|
||||
fprintf(fp, " ");
|
||||
|
||||
PrintBailoutKind(fp, bailoutKind());
|
||||
}
|
||||
|
||||
void
|
||||
MPhi::removeOperand(size_t index)
|
||||
{
|
||||
|
@ -715,7 +774,9 @@ jit::MergeTypes(MIRType *ptype, types::StackTypeSet **ptypeSet,
|
|||
if (newTypeSet && newTypeSet->empty())
|
||||
return;
|
||||
if (newType != *ptype) {
|
||||
if (IsNumberType(newType) && IsNumberType(*ptype)) {
|
||||
if (IsFloatType(newType) && IsFloatType(*ptype)) {
|
||||
*ptype = MIRType_Float32;
|
||||
} else if (IsNumberType(newType) && IsNumberType(*ptype)) {
|
||||
*ptype = MIRType_Double;
|
||||
} else if (*ptype != MIRType_Value) {
|
||||
if (!*ptypeSet)
|
||||
|
@ -1139,6 +1200,43 @@ MBinaryArithInstruction::foldsTo(bool useValueNumbers)
|
|||
return this;
|
||||
}
|
||||
|
||||
template<size_t Op> static void
|
||||
ConvertDefinitionToDouble(MDefinition *def, MInstruction *consumer)
|
||||
{
|
||||
MInstruction *replace = MToDouble::New(def);
|
||||
consumer->replaceOperand(Op, replace);
|
||||
consumer->block()->insertBefore(consumer, replace);
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckUsesAreFloat32Consumers(MInstruction *ins)
|
||||
{
|
||||
bool allConsumerUses = true;
|
||||
for (MUseDefIterator use(ins); allConsumerUses && use; use++)
|
||||
allConsumerUses &= use.def()->canConsumeFloat32();
|
||||
return allConsumerUses;
|
||||
}
|
||||
|
||||
void
|
||||
MBinaryArithInstruction::trySpecializeFloat32()
|
||||
{
|
||||
MDefinition *left = lhs();
|
||||
MDefinition *right = rhs();
|
||||
|
||||
if (!left->canProduceFloat32() || !right->canProduceFloat32()
|
||||
|| !CheckUsesAreFloat32Consumers(this))
|
||||
{
|
||||
if (left->type() == MIRType_Float32)
|
||||
ConvertDefinitionToDouble<0>(left, this);
|
||||
if (right->type() == MIRType_Float32)
|
||||
ConvertDefinitionToDouble<1>(right, this);
|
||||
return;
|
||||
}
|
||||
|
||||
specialization_ = MIRType_Float32;
|
||||
setResultType(MIRType_Float32);
|
||||
}
|
||||
|
||||
bool
|
||||
MAbs::fallible() const
|
||||
{
|
||||
|
@ -1377,7 +1475,9 @@ MBinaryArithInstruction::infer(BaselineInspector *inspector,
|
|||
// Don't specialize for neither-integer-nor-double results.
|
||||
if (lhs == MIRType_Int32 && rhs == MIRType_Int32)
|
||||
setResultType(MIRType_Int32);
|
||||
else if (lhs == MIRType_Double || rhs == MIRType_Double)
|
||||
// Double operations are prioritary over float32 operations (i.e. if any operand needs
|
||||
// a double as an input, convert all operands to doubles)
|
||||
else if (IsFloatingPointType(lhs) || IsFloatingPointType(rhs))
|
||||
setResultType(MIRType_Double);
|
||||
else
|
||||
return inferFallback(inspector, pc);
|
||||
|
@ -1402,7 +1502,7 @@ MBinaryArithInstruction::infer(BaselineInspector *inspector,
|
|||
|
||||
// Don't specialize values when result isn't double
|
||||
if (lhs == MIRType_Value || rhs == MIRType_Value) {
|
||||
if (rval != MIRType_Double) {
|
||||
if (!IsFloatingPointType(rval)) {
|
||||
specialization_ = MIRType_None;
|
||||
return;
|
||||
}
|
||||
|
@ -1957,6 +2057,28 @@ MToDouble::foldsTo(bool useValueNumbers)
|
|||
return this;
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
MToFloat32::foldsTo(bool useValueNumbers)
|
||||
{
|
||||
if (input()->type() == MIRType_Float32)
|
||||
return input();
|
||||
|
||||
// If x is a Float32, Float32(Double(x)) == x
|
||||
if (input()->isToDouble() && input()->toToDouble()->input()->type() == MIRType_Float32)
|
||||
return input()->toToDouble()->input();
|
||||
|
||||
if (input()->isConstant()) {
|
||||
const Value &v = input()->toConstant()->value();
|
||||
if (v.isNumber()) {
|
||||
float out = v.toNumber();
|
||||
MConstant *c = MConstant::New(DoubleValue(out));
|
||||
c->setResultType(MIRType_Float32);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
MToString::foldsTo(bool useValueNumbers)
|
||||
{
|
||||
|
@ -2890,6 +3012,11 @@ jit::PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinit
|
|||
if (!object || object->unknownProperties())
|
||||
continue;
|
||||
|
||||
// TI doesn't track TypedArray objects and should never insert a type
|
||||
// barrier for them.
|
||||
if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX)
|
||||
continue;
|
||||
|
||||
types::HeapTypeSet *property = object->getProperty(cx, id, false);
|
||||
if (!property) {
|
||||
success = false;
|
||||
|
@ -2929,6 +3056,8 @@ jit::PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinit
|
|||
|
||||
if (!object || object->unknownProperties())
|
||||
continue;
|
||||
if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX)
|
||||
continue;
|
||||
|
||||
types::HeapTypeSet *property = object->getProperty(cx, id, false);
|
||||
if (!property) {
|
||||
|
|
196
js/src/jit/MIR.h
196
js/src/jit/MIR.h
|
@ -453,6 +453,13 @@ class MDefinition : public MNode
|
|||
return !resultTypeSet() || resultTypeSet()->mightBeType(ValueTypeFromMIRType(type));
|
||||
}
|
||||
|
||||
// Float32 specialization operations (see big comment in IonAnalysis before the Float32
|
||||
// specialization algorithm).
|
||||
virtual bool isFloat32Commutative() const { return false; }
|
||||
virtual bool canProduceFloat32() const { return false; }
|
||||
virtual bool canConsumeFloat32() const { return false; }
|
||||
virtual void trySpecializeFloat32() {}
|
||||
|
||||
// Returns the beginning of this definition's use chain.
|
||||
MUseIterator usesBegin() const {
|
||||
return uses_.begin();
|
||||
|
@ -948,8 +955,21 @@ class MConstant : public MNullaryInstruction
|
|||
return AliasSet::None();
|
||||
}
|
||||
|
||||
bool updateForReplacement(MDefinition *def) {
|
||||
MConstant *c = def->toConstant();
|
||||
// During constant folding, we don't want to replace a float32
|
||||
// value by a double value.
|
||||
if (type() == MIRType_Float32)
|
||||
return c->type() == MIRType_Float32;
|
||||
if (type() == MIRType_Double)
|
||||
return c->type() != MIRType_Float32;
|
||||
return true;
|
||||
}
|
||||
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
|
||||
bool canProduceFloat32() const;
|
||||
};
|
||||
|
||||
class MParameter : public MNullaryInstruction
|
||||
|
@ -1723,6 +1743,10 @@ class MCall
|
|||
return getOperand(NumNonArgumentOperands + index);
|
||||
}
|
||||
|
||||
void replaceArg(uint32_t index, MDefinition *def) {
|
||||
replaceOperand(NumNonArgumentOperands + index, def);
|
||||
}
|
||||
|
||||
void rootTargetScript(JSFunction *target) {
|
||||
targetScript_.setRoot(target->nonLazyScript());
|
||||
}
|
||||
|
@ -2129,14 +2153,14 @@ class MUnbox : public MUnaryInstruction, public BoxInputsPolicy
|
|||
enum Mode {
|
||||
Fallible, // Check the type, and deoptimize if unexpected.
|
||||
Infallible, // Type guard is not necessary.
|
||||
TypeBarrier, // Guard on the type, and act like a TypeBarrier on failure.
|
||||
TypeGuard // Guard on the type, and deoptimize otherwise.
|
||||
TypeBarrier // Guard on the type, and act like a TypeBarrier on failure.
|
||||
};
|
||||
|
||||
private:
|
||||
Mode mode_;
|
||||
BailoutKind bailoutKind_;
|
||||
|
||||
MUnbox(MDefinition *ins, MIRType type, Mode mode)
|
||||
MUnbox(MDefinition *ins, MIRType type, Mode mode, BailoutKind kind)
|
||||
: MUnaryInstruction(ins),
|
||||
mode_(mode)
|
||||
{
|
||||
|
@ -2151,17 +2175,24 @@ class MUnbox : public MUnaryInstruction, public BoxInputsPolicy
|
|||
setResultTypeSet(ins->resultTypeSet());
|
||||
setMovable();
|
||||
|
||||
if (mode_ == TypeBarrier || mode_ == TypeGuard)
|
||||
if (mode_ == TypeBarrier)
|
||||
setGuard();
|
||||
if (mode_ == TypeGuard)
|
||||
mode_ = Fallible;
|
||||
}
|
||||
|
||||
bailoutKind_ = kind;
|
||||
}
|
||||
public:
|
||||
INSTRUCTION_HEADER(Unbox)
|
||||
static MUnbox *New(MDefinition *ins, MIRType type, Mode mode)
|
||||
{
|
||||
return new MUnbox(ins, type, mode);
|
||||
BailoutKind kind = Bailout_Normal;
|
||||
if (mode == TypeBarrier && ins->isEffectful())
|
||||
kind = Bailout_TypeBarrier;
|
||||
return new MUnbox(ins, type, mode, kind);
|
||||
}
|
||||
|
||||
static MUnbox *New(MDefinition *ins, MIRType type, Mode mode, BailoutKind kind)
|
||||
{
|
||||
return new MUnbox(ins, type, mode, kind);
|
||||
}
|
||||
|
||||
TypePolicy *typePolicy() {
|
||||
|
@ -2174,9 +2205,7 @@ class MUnbox : public MUnaryInstruction, public BoxInputsPolicy
|
|||
BailoutKind bailoutKind() const {
|
||||
// If infallible, no bailout should be generated.
|
||||
JS_ASSERT(fallible());
|
||||
return mode() == Fallible
|
||||
? Bailout_Normal
|
||||
: Bailout_TypeBarrier;
|
||||
return bailoutKind_;
|
||||
}
|
||||
bool fallible() const {
|
||||
return mode() != Infallible;
|
||||
|
@ -2636,6 +2665,65 @@ class MToDouble
|
|||
bool isOperandTruncated(size_t index) const;
|
||||
};
|
||||
|
||||
// Converts a primitive (either typed or untyped) to a float32. If the input is
|
||||
// not primitive at runtime, a bailout occurs.
|
||||
class MToFloat32
|
||||
: public MUnaryInstruction,
|
||||
public ToDoublePolicy
|
||||
{
|
||||
public:
|
||||
// Types of values which can be converted.
|
||||
enum ConversionKind {
|
||||
NonStringPrimitives,
|
||||
NonNullNonStringPrimitives,
|
||||
NumbersOnly
|
||||
};
|
||||
|
||||
protected:
|
||||
ConversionKind conversion_;
|
||||
|
||||
MToFloat32(MDefinition *def, ConversionKind conversion)
|
||||
: MUnaryInstruction(def), conversion_(conversion)
|
||||
{
|
||||
setResultType(MIRType_Float32);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ToFloat32)
|
||||
static MToFloat32 *New(MDefinition *def, ConversionKind conversion = NonStringPrimitives) {
|
||||
return new MToFloat32(def, conversion);
|
||||
}
|
||||
static MToFloat32 *NewAsmJS(MDefinition *def) {
|
||||
return new MToFloat32(def, NonStringPrimitives);
|
||||
}
|
||||
|
||||
ConversionKind conversion() const {
|
||||
return conversion_;
|
||||
}
|
||||
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual MDefinition *foldsTo(bool useValueNumbers);
|
||||
bool congruentTo(MDefinition *ins) const {
|
||||
if (!ins->isToFloat32() || ins->toToFloat32()->conversion() != conversion())
|
||||
return false;
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
|
||||
bool canConsumeFloat32() const { return true; }
|
||||
bool canProduceFloat32() const { return true; }
|
||||
};
|
||||
|
||||
// Converts a uint32 to a double (coming from asm.js).
|
||||
class MAsmJSUnsignedToDouble
|
||||
: public MUnaryInstruction
|
||||
|
@ -3101,6 +3189,8 @@ class MBinaryArithInstruction
|
|||
setResultType(MIRType_Int32);
|
||||
}
|
||||
|
||||
virtual void trySpecializeFloat32();
|
||||
|
||||
bool congruentTo(MDefinition *ins) const {
|
||||
return MBinaryInstruction::congruentTo(ins);
|
||||
}
|
||||
|
@ -3476,6 +3566,8 @@ class MAdd : public MBinaryArithInstruction
|
|||
return add;
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const { return true; }
|
||||
|
||||
double getIdentity() {
|
||||
return 0;
|
||||
}
|
||||
|
@ -3512,6 +3604,8 @@ class MSub : public MBinaryArithInstruction
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const { return true; }
|
||||
|
||||
bool fallible();
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
|
@ -3584,6 +3678,8 @@ class MMul : public MBinaryArithInstruction
|
|||
return canBeNegativeZero_ || canOverflow();
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const { return true; }
|
||||
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
|
@ -3652,6 +3748,8 @@ class MDiv : public MBinaryArithInstruction
|
|||
return unsigned_;
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const { return true; }
|
||||
|
||||
bool fallible();
|
||||
bool truncate();
|
||||
};
|
||||
|
@ -3839,6 +3937,8 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
|||
bool hasBackedgeType_;
|
||||
bool triedToSpecialize_;
|
||||
bool isIterator_;
|
||||
bool canProduceFloat32_;
|
||||
bool canConsumeFloat32_;
|
||||
|
||||
#if DEBUG
|
||||
bool specialized_;
|
||||
|
@ -3849,7 +3949,9 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
|||
: slot_(slot),
|
||||
hasBackedgeType_(false),
|
||||
triedToSpecialize_(false),
|
||||
isIterator_(false)
|
||||
isIterator_(false),
|
||||
canProduceFloat32_(false),
|
||||
canConsumeFloat32_(false)
|
||||
#if DEBUG
|
||||
, specialized_(false)
|
||||
, capacity_(0)
|
||||
|
@ -3947,6 +4049,22 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
|||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
bool canProduceFloat32() const {
|
||||
return canProduceFloat32_;
|
||||
}
|
||||
|
||||
void setCanProduceFloat32(bool can) {
|
||||
canProduceFloat32_ = can;
|
||||
}
|
||||
|
||||
bool canConsumeFloat32() const {
|
||||
return canConsumeFloat32_;
|
||||
}
|
||||
|
||||
void setCanConsumeFloat32(bool can) {
|
||||
canConsumeFloat32_ = can;
|
||||
}
|
||||
};
|
||||
|
||||
// The goal of a Beta node is to split a def at a conditionally taken
|
||||
|
@ -4951,7 +5069,7 @@ class MStoreElementCommon
|
|||
class MStoreElement
|
||||
: public MAryInstruction<3>,
|
||||
public MStoreElementCommon,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> >
|
||||
{
|
||||
bool needsHoleCheck_;
|
||||
|
||||
|
@ -5001,7 +5119,7 @@ class MStoreElement
|
|||
class MStoreElementHole
|
||||
: public MAryInstruction<4>,
|
||||
public MStoreElementCommon,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >
|
||||
{
|
||||
MStoreElementHole(MDefinition *object, MDefinition *elements,
|
||||
MDefinition *index, MDefinition *value) {
|
||||
|
@ -5201,6 +5319,8 @@ class MLoadTypedArrayElement
|
|||
}
|
||||
|
||||
void computeRange();
|
||||
|
||||
bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
// Load a value from a typed array. Out-of-bounds accesses are handled using
|
||||
|
@ -5249,6 +5369,7 @@ class MLoadTypedArrayElementHole
|
|||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::TypedArrayElement);
|
||||
}
|
||||
bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
// Load a value fallibly or infallibly from a statically known typed array.
|
||||
|
@ -5260,7 +5381,9 @@ class MLoadTypedArrayElementStatic
|
|||
: MUnaryInstruction(ptr), typedArray_(typedArray), fallible_(true)
|
||||
{
|
||||
int type = typedArray_->type();
|
||||
if (type == ScalarTypeRepresentation::TYPE_FLOAT32 || type == ScalarTypeRepresentation::TYPE_FLOAT64)
|
||||
if (type == ScalarTypeRepresentation::TYPE_FLOAT32)
|
||||
setResultType(MIRType_Float32);
|
||||
else if (type == ScalarTypeRepresentation::TYPE_FLOAT64)
|
||||
setResultType(MIRType_Double);
|
||||
else
|
||||
setResultType(MIRType_Int32);
|
||||
|
@ -5299,6 +5422,7 @@ class MLoadTypedArrayElementStatic
|
|||
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
bool canProduceFloat32() const { return typedArray_->type() == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
class MStoreTypedArrayElement
|
||||
|
@ -5362,6 +5486,8 @@ class MStoreTypedArrayElement
|
|||
racy_ = true;
|
||||
}
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
|
||||
bool canConsumeFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
class MStoreTypedArrayElementHole
|
||||
|
@ -5425,6 +5551,8 @@ class MStoreTypedArrayElementHole
|
|||
return AliasSet::Store(AliasSet::TypedArrayElement);
|
||||
}
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
|
||||
bool canConsumeFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
// Store a value infallibly to a statically known typed array.
|
||||
|
@ -5466,6 +5594,8 @@ class MStoreTypedArrayElementStatic :
|
|||
return AliasSet::Store(AliasSet::TypedArrayElement);
|
||||
}
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
|
||||
bool canConsumeFloat32() const { return typedArray_->type() == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
// Compute an "effective address", i.e., a compound computation of the form:
|
||||
|
@ -5585,7 +5715,7 @@ class MLoadFixedSlot
|
|||
|
||||
class MStoreFixedSlot
|
||||
: public MBinaryInstruction,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >
|
||||
{
|
||||
bool needsBarrier_;
|
||||
size_t slot_;
|
||||
|
@ -6406,7 +6536,7 @@ class MForkJoinSlice
|
|||
// Store to vp[slot] (slots that are not inline in an object).
|
||||
class MStoreSlot
|
||||
: public MBinaryInstruction,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >
|
||||
{
|
||||
uint32_t slot_;
|
||||
MIRType slotType_;
|
||||
|
@ -6706,7 +6836,7 @@ class MCallSetProperty
|
|||
|
||||
class MSetPropertyCache
|
||||
: public MSetPropertyInstruction,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >
|
||||
{
|
||||
bool needsTypeBarrier_;
|
||||
|
||||
|
@ -7548,7 +7678,7 @@ class MGuardThreadLocalObject
|
|||
// that value.
|
||||
class MTypeBarrier
|
||||
: public MUnaryInstruction,
|
||||
public BoxInputsPolicy
|
||||
public TypeBarrierPolicy
|
||||
{
|
||||
BailoutKind bailoutKind_;
|
||||
|
||||
|
@ -7556,8 +7686,11 @@ class MTypeBarrier
|
|||
: MUnaryInstruction(def)
|
||||
{
|
||||
JS_ASSERT(!types->unknown());
|
||||
setResultType(MIRType_Value);
|
||||
|
||||
MIRType type = MIRTypeFromValueType(types->getKnownTypeTag());
|
||||
setResultType(type);
|
||||
setResultTypeSet(types);
|
||||
|
||||
setGuard();
|
||||
setMovable();
|
||||
bailoutKind_ = bailoutKind;
|
||||
|
@ -7567,16 +7700,16 @@ class MTypeBarrier
|
|||
INSTRUCTION_HEADER(TypeBarrier)
|
||||
|
||||
static MTypeBarrier *New(MDefinition *def, types::StackTypeSet *types) {
|
||||
BailoutKind bailoutKind = def->isEffectful()
|
||||
? Bailout_TypeBarrier
|
||||
: Bailout_Normal;
|
||||
return new MTypeBarrier(def, types, bailoutKind);
|
||||
BailoutKind kind = def->isEffectful() ? Bailout_TypeBarrier : Bailout_Normal;
|
||||
return new MTypeBarrier(def, types, kind);
|
||||
}
|
||||
static MTypeBarrier *New(MDefinition *def, types::StackTypeSet *types,
|
||||
BailoutKind bailoutKind) {
|
||||
return new MTypeBarrier(def, types, bailoutKind);
|
||||
BailoutKind kind) {
|
||||
return new MTypeBarrier(def, types, kind);
|
||||
}
|
||||
|
||||
void printOpcode(FILE *fp) const;
|
||||
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
}
|
||||
|
@ -7593,6 +7726,17 @@ class MTypeBarrier
|
|||
virtual bool neverHoist() const {
|
||||
return resultTypeSet()->empty();
|
||||
}
|
||||
|
||||
bool alwaysBails() const {
|
||||
// If mirtype of input doesn't agree with mirtype of barrier,
|
||||
// we will definitely bail.
|
||||
MIRType type = MIRTypeFromValueType(resultTypeSet()->getKnownTypeTag());
|
||||
if (type == MIRType_Value)
|
||||
return false;
|
||||
if (input()->type() == MIRType_Value)
|
||||
return false;
|
||||
return input()->type() != type;
|
||||
}
|
||||
};
|
||||
|
||||
// Like MTypeBarrier, guard that the value is in the given type set. This is
|
||||
|
|
|
@ -76,6 +76,7 @@ namespace jit {
|
|||
_(GuardString) \
|
||||
_(AssertRange) \
|
||||
_(ToDouble) \
|
||||
_(ToFloat32) \
|
||||
_(ToInt32) \
|
||||
_(TruncateToInt32) \
|
||||
_(ToString) \
|
||||
|
|
|
@ -163,6 +163,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
|||
SAFE_OP(Unbox)
|
||||
SAFE_OP(GuardObject)
|
||||
SAFE_OP(ToDouble)
|
||||
SAFE_OP(ToFloat32)
|
||||
SAFE_OP(ToInt32)
|
||||
SAFE_OP(TruncateToInt32)
|
||||
SAFE_OP(MaybeToDoubleElement)
|
||||
|
|
|
@ -1105,6 +1105,12 @@ MToDouble::computeRange()
|
|||
setRange(new Range(getOperand(0)));
|
||||
}
|
||||
|
||||
void
|
||||
MToFloat32::computeRange()
|
||||
{
|
||||
setRange(new Range(getOperand(0)));
|
||||
}
|
||||
|
||||
void
|
||||
MTruncateToInt32::computeRange()
|
||||
{
|
||||
|
@ -1829,6 +1835,20 @@ MToDouble::truncate()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MToFloat32::truncate()
|
||||
{
|
||||
JS_ASSERT(type() == MIRType_Float32);
|
||||
|
||||
// We use the return type to flag that this MToFloat32 sould be replaced by a
|
||||
// MTruncateToInt32 when modifying the graph.
|
||||
setResultType(MIRType_Int32);
|
||||
if (range())
|
||||
range()->wrapAroundToInt32();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MLoadTypedArrayElementStatic::truncate()
|
||||
{
|
||||
|
@ -1880,6 +1900,14 @@ MToDouble::isOperandTruncated(size_t index) const
|
|||
return type() == MIRType_Int32;
|
||||
}
|
||||
|
||||
bool
|
||||
MToFloat32::isOperandTruncated(size_t index) const
|
||||
{
|
||||
// The return type is used to flag that we are replacing this Float32 by a
|
||||
// Truncate of its operand if needed.
|
||||
return type() == MIRType_Int32;
|
||||
}
|
||||
|
||||
bool
|
||||
MStoreTypedArrayElement::isOperandTruncated(size_t index) const
|
||||
{
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче