Merge last PGO-green inbound changeset to m-c.

--HG--
rename : dom/bindings/BindingUtils.h => dom/bindings/OwningNonNull.h
This commit is contained in:
Ms2ger 2013-09-10 10:13:12 +02:00
Родитель 21eb5aa22d 437889b715
Коммит d9d7605e4f
192 изменённых файлов: 5326 добавлений и 2153 удалений

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

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

11
gfx/src/AppUnits.h Normal file
Просмотреть файл

@ -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, &notDouble);
// 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(&notDouble);
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(&notNaN);
}
void canonicalizeFloat(FloatRegister reg) {
static float js_NaN_float = js_NaN;
Label notNaN;
branchFloat(DoubleOrdered, reg, reg, &notNaN);
loadStaticFloat32(&js_NaN_float, reg);
bind(&notNaN);
}
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 &reg, 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) {

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

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

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