diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index 1273a783fc54..1534aefbdaee 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -1294,8 +1294,12 @@ AccessibleWrap::GetChildIDFor(Accessible* aAccessible) // so that the 3rd party application can call back and get the IAccessible // the event occurred on. + if (!aAccessible) { + return 0; + } + #ifdef _WIN64 - if (!aAccessible || (!aAccessible->Document() && !aAccessible->IsProxy())) + if (!aAccessible->Document() && !aAccessible->IsProxy()) return 0; uint32_t* id = & static_cast(aAccessible)->mID; diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index aa598ca8b734..2e0235be4634 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,12 +15,12 @@ - + - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index ddb9398ae845..179b7c5f47d8 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,12 +15,12 @@ - + - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index ec79cc5af39e..a0b01b05178a 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,12 +19,12 @@ - + - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 132bfa44dda4..873749c81ee2 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,9 +17,9 @@ - + - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 7b3da3b54ab6..0c405a5bee7f 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,11 +15,11 @@ - + - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index 086700fe6211..4c4ab956265d 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,11 +15,11 @@ - + - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index ec79cc5af39e..a0b01b05178a 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,12 +19,12 @@ - + - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 6d9909874f4f..e4ec23499566 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,12 +15,12 @@ - + - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index f2d4246dc16e..679035d4ec1d 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "f345f6a015709beeb2ca3955cab077fcaa959d3b", + "git_revision": "1bc0b19527777ffee494962b48db4be857b07d64", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "83e7fd1f9ab848025ef06f1283ad95fe5005f26c", + "revision": "d8e220a9a7bc3083b1cd9bc94f5a524e5b114384", "repo_path": "integration/gaia-central" } diff --git a/b2g/config/nexus-4-kk/sources.xml b/b2g/config/nexus-4-kk/sources.xml index 640e189a75ba..7b6affcd6f8f 100644 --- a/b2g/config/nexus-4-kk/sources.xml +++ b/b2g/config/nexus-4-kk/sources.xml @@ -15,12 +15,12 @@ - + - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index e067b5cec909..2243b2f3e8de 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -18,9 +18,9 @@ - + - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 4de7c45611f0..9703fa7a3fb0 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,12 +15,12 @@ - + - + diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp index 7b8f89ddf474..23e2f3fef2e5 100644 --- a/caps/BasePrincipal.cpp +++ b/caps/BasePrincipal.cpp @@ -395,7 +395,7 @@ BasePrincipal::GetUnknownAppId(bool* aUnknownAppId) } already_AddRefed -BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAttrs) +BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs) { // If the URI is supposed to inherit the security context of whoever loads it, // we shouldn't make a codebase principal for it. diff --git a/caps/BasePrincipal.h b/caps/BasePrincipal.h index 6dfd74c7854b..e65d04058ef7 100644 --- a/caps/BasePrincipal.h +++ b/caps/BasePrincipal.h @@ -159,7 +159,7 @@ public: virtual bool IsCodebasePrincipal() const { return false; }; static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast(aPrin); } - static already_AddRefed CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAttrs); + static already_AddRefed CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs); const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; } uint32_t AppId() const { return mOriginAttributes.mAppId; } diff --git a/configure.in b/configure.in index f0d2a7407b6e..c8e3bd7d1071 100644 --- a/configure.in +++ b/configure.in @@ -4780,6 +4780,21 @@ then fi AC_SUBST(MOZ_ENABLE_DBUS) +dnl ======================================================== +dnl = speech-dispatcher support +dnl ======================================================== + +if test "$MOZ_ENABLE_GTK" -o "$MOZ_ENABLE_QT" +then + MOZ_SYNTH_SPEECHD=1 + + MOZ_ARG_DISABLE_BOOL(synth-speechd, + [ --disable-synth-speechd Disable speech-dispatcher support ], + MOZ_SYNTH_SPEECHD=, + MOZ_SYNTH_SPEECHD=1) +fi +AC_SUBST(MOZ_SYNTH_SPEECHD) + dnl ======================================================== dnl = Enable Android History instead of Places dnl ======================================================== diff --git a/dom/base/StructuredCloneHelper.cpp b/dom/base/StructuredCloneHelper.cpp index 7975e42e1bdc..07b7e9c84a94 100644 --- a/dom/base/StructuredCloneHelper.cpp +++ b/dom/base/StructuredCloneHelper.cpp @@ -425,14 +425,15 @@ StructuredCloneHelper::ReadFullySerializableObjects(JSContext* aCx, } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) { info = mozilla::ipc::NullPrincipalInfo(); } else { - uint32_t suffixLength, specLength; - if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) { + uint32_t appId = aIndex; + + uint32_t isInBrowserElement, specLength; + if (!JS_ReadUint32Pair(aReader, &isInBrowserElement, &specLength)) { return nullptr; } - nsAutoCString suffix; - suffix.SetLength(suffixLength); - if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) { + uint32_t signedPkgLength, dummy; + if (!JS_ReadUint32Pair(aReader, &signedPkgLength, &dummy)) { return nullptr; } @@ -442,9 +443,14 @@ StructuredCloneHelper::ReadFullySerializableObjects(JSContext* aCx, return nullptr; } - OriginAttributes attrs; - attrs.PopulateFromSuffix(suffix); - info = mozilla::ipc::ContentPrincipalInfo(attrs, spec); + nsAutoCString signedPkg; + signedPkg.SetLength(signedPkgLength); + if (!JS_ReadBytes(aReader, signedPkg.BeginWriting(), signedPkgLength)) { + return nullptr; + } + + info = mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement, + spec, signedPkg); } nsresult rv; @@ -571,12 +577,13 @@ StructuredCloneHelper::WriteFullySerializableObjects(JSContext* aCx, MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo); const mozilla::ipc::ContentPrincipalInfo& cInfo = info; - nsAutoCString suffix; - cInfo.attrs().CreateSuffix(suffix); - return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) && - JS_WriteUint32Pair(aWriter, suffix.Length(), cInfo.spec().Length()) && - JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) && - JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length()); + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, + cInfo.appId()) && + JS_WriteUint32Pair(aWriter, cInfo.isInBrowserElement(), + cInfo.spec().Length()) && + JS_WriteUint32Pair(aWriter, cInfo.signedPkg().Length(), 0) && + JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length()) && + JS_WriteBytes(aWriter, cInfo.signedPkg().get(), cInfo.signedPkg().Length()); } } diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 886169c32cdf..ee904a3e3eaf 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -8161,6 +8161,26 @@ nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal, // About URIs are allowed to access storage, even if they don't have chrome // privileges. If this is not desired, than the consumer will have to // implement their own restriction functionality. + // + // This is due to backwards-compatibility and the state of storage access before + // the introducton of nsContentUtils::InternalStorageAllowedForPrincipal: + // + // BEFORE: + // localStorage, caches: allowed in 3rd-party iframes always + // IndexedDB: allowed in 3rd-party iframes only if 3rd party URI is an about: + // URI within a specific whitelist + // + // AFTER: + // localStorage, caches: allowed in 3rd-party iframes by default. Preference + // can be set to disable in 3rd-party, which will not disallow in about: URIs. + // IndexedDB: allowed in 3rd-party iframes by default. Preference can be set to + // disable in 3rd-party, which will disallow in about: URIs, unless they are + // within a specific whitelist. + // + // This means that behavior for storage with internal about: URIs should not be + // affected, which is desireable due to the lack of automated testing for about: + // URIs with these preferences set, and the importance of the correct functioning + // of these URIs even with custom preferences. nsCOMPtr uri; nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); if (NS_SUCCEEDED(rv) && uri) { diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 3af6e003723e..4c4086440b4f 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -1806,25 +1806,21 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript( if (!JS::Compile(cx, options, srcBuf, &script)) { return; } - } else { - // We're going to run these against some non-global scope. - if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) { - return; - } + // We're going to run these against some non-global scope. + } else if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) { + return; } + MOZ_ASSERT(script); aScriptp.set(script); nsAutoCString scheme; uri->GetScheme(scheme); // We don't cache data: scripts! if (aShouldCache && !scheme.EqualsLiteral("data")) { - nsMessageManagerScriptHolder* holder; - // Root the object also for caching. - if (script) { - holder = new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope); - } + nsMessageManagerScriptHolder* holder = + new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope); sCachedScripts->Put(aURL, holder); } } diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 7120bfca3457..ad1e992e5faa 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -114,6 +114,7 @@ nsScriptLoadRequestList::Contains(nsScriptLoadRequest* aElem) nsScriptLoader::nsScriptLoader(nsIDocument *aDocument) : mDocument(aDocument), mBlockerCount(0), + mNumberOfProcessors(0), mEnabled(true), mDeferEnabled(false), mDocumentParsingDone(false), @@ -1503,6 +1504,18 @@ nsScriptLoader::ContinueParserAsync(nsScriptLoadRequest* aParserBlockingRequest) aParserBlockingRequest->mElement->ContinueParserAsync(); } +uint32_t +nsScriptLoader::NumberOfProcessors() +{ + if (mNumberOfProcessors > 0) + return mNumberOfProcessors; + + int32_t numProcs = PR_GetNumberOfProcessors(); + if (numProcs > 0) + mNumberOfProcessors = numProcs; + return mNumberOfProcessors; +} + nsresult nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsIStreamLoader* aLoader, @@ -1589,7 +1602,7 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, aRequest->mProgress = nsScriptLoadRequest::Progress_DoneLoading; // If this is currently blocking the parser, attempt to compile it off-main-thread. - if (aRequest == mParserBlockingRequest && (PR_GetNumberOfProcessors() > 1)) { + if (aRequest == mParserBlockingRequest && (NumberOfProcessors() > 1)) { nsresult rv = AttemptAsyncScriptCompile(aRequest); if (rv == NS_OK) { NS_ASSERTION(aRequest->mProgress == nsScriptLoadRequest::Progress_Compiling, diff --git a/dom/base/nsScriptLoader.h b/dom/base/nsScriptLoader.h index b3be883f5cd9..5601e8bf54c0 100644 --- a/dom/base/nsScriptLoader.h +++ b/dom/base/nsScriptLoader.h @@ -487,6 +487,7 @@ private: JS::Handle aScopeChain, JS::CompileOptions *aOptions); + uint32_t NumberOfProcessors(); nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsIStreamLoader* aLoader, nsresult aStatus, @@ -530,6 +531,7 @@ private: // XXXbz do we want to cycle-collect these or something? Not sure. nsTArray< nsRefPtr > mPendingChildLoaders; uint32_t mBlockerCount; + uint32_t mNumberOfProcessors; bool mEnabled; bool mDeferEnabled; bool mDocumentParsingDone; diff --git a/dom/cache/CacheStorage.cpp b/dom/cache/CacheStorage.cpp index d57297f4c386..ae828d7b50dc 100644 --- a/dom/cache/CacheStorage.cpp +++ b/dom/cache/CacheStorage.cpp @@ -81,7 +81,7 @@ IsTrusted(const PrincipalInfo& aPrincipalInfo, bool aTestingPrefEnabled) // worker. We require exact knowledge of this information before allowing // the caller to touch the disk using the Cache API. if (NS_WARN_IF(aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo || - aPrincipalInfo.get_ContentPrincipalInfo().attrs().mAppId == + aPrincipalInfo.get_ContentPrincipalInfo().appId() == nsIScriptSecurityManager::UNKNOWN_APP_ID)) { return false; } diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index a7c2ed0b3f44..c2772cbf2448 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -1740,8 +1740,10 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId, serializedInfo.Append(cInfo.spec()); + MOZ_ASSERT(cInfo.appId() != nsIScriptSecurityManager::UNKNOWN_APP_ID); + OriginAttributes attrs(cInfo.appId(), cInfo.isInBrowserElement()); nsAutoCString suffix; - cInfo.attrs().CreateSuffix(suffix); + attrs.CreateSuffix(suffix); serializedInfo.Append(suffix); } @@ -1911,8 +1913,9 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId, return NS_ERROR_FAILURE; } + nsCString signedPkg = NS_ConvertUTF16toUTF8(attrs.mSignedPkg); aSavedResponseOut->mValue.principalInfo() = - mozilla::ipc::ContentPrincipalInfo(attrs, originNoSuffix); + mozilla::ipc::ContentPrincipalInfo(attrs.mAppId, attrs.mInBrowser, originNoSuffix, signedPkg); } int32_t redirected; diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 4980b44e6d3d..d1cd7ae86d39 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -330,14 +330,6 @@ parent: sync IsParentWindowMainWidgetVisible() returns (bool visible); - /** - * Returns the offset of this tab from the top level window - * origin in device pixels. - * - * aPoint offset values in device pixels. - */ - prio(high) sync GetTabOffset() returns (LayoutDeviceIntPoint aPoint); - /** * Gets the DPI of the screen corresponding to this browser. */ diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 8f423c54b1de..d1132e9b1e17 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2266,13 +2266,6 @@ TabParent::RecvEnableDisableCommands(const nsString& aAction, return true; } -bool -TabParent::RecvGetTabOffset(LayoutDeviceIntPoint* aPoint) -{ - *aPoint = GetChildProcessOffset(); - return true; -} - NS_IMETHODIMP TabParent::GetChildProcessOffset(int32_t* aOutCssX, int32_t* aOutCssY) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 80d67b5269e1..6ef8ca79b481 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -219,7 +219,6 @@ public: virtual bool RecvIsParentWindowMainWidgetVisible(bool* aIsVisible) override; virtual bool RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip) override; virtual bool RecvHideTooltip() override; - virtual bool RecvGetTabOffset(LayoutDeviceIntPoint* aPoint) override; virtual bool RecvGetDPI(float* aValue) override; virtual bool RecvGetDefaultScale(double* aValue) override; virtual bool RecvGetMaxTouchPoints(uint32_t* aTouchPoints) override; diff --git a/dom/media/tests/mochitest/ipc/mochitest.ini b/dom/media/tests/mochitest/ipc/mochitest.ini index 976343a26833..21a6d91a4230 100644 --- a/dom/media/tests/mochitest/ipc/mochitest.ini +++ b/dom/media/tests/mochitest/ipc/mochitest.ini @@ -6,4 +6,4 @@ support-files = skip-if = e10s [test_ipc.html] -skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || os == 'win' #bug 910661 # b2g(nested ipc not working) b2g-debug(debug-only failure) b2g-desktop(nested ipc not working)(win : Bug 1179826) +skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #bug 910661 # b2g(nested ipc not working) b2g-debug(debug-only failure) b2g-desktop(nested ipc not working) diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp index 8d9b4157ed2d..73f40a38c63b 100644 --- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -11,6 +11,7 @@ #include "WebMDemuxer.h" #include "WebMBufferedParser.h" #include "gfx2DGlue.h" +#include "mozilla/Endian.h" #include "mozilla/Preferences.h" #include "mozilla/SharedThreadPool.h" #include "MediaDataDemuxer.h" diff --git a/dom/media/webspeech/synth/moz.build b/dom/media/webspeech/synth/moz.build index 1d6e01d107ea..b3dce2a7cc1b 100644 --- a/dom/media/webspeech/synth/moz.build +++ b/dom/media/webspeech/synth/moz.build @@ -41,10 +41,16 @@ if CONFIG['MOZ_WEBSPEECH']: 'test/nsFakeSynthServices.cpp' ] + DIRS = [] + if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': - DIRS = ['windows'] - elif CONFIG['MOZ_SYNTH_PICO']: - DIRS = ['pico'] + DIRS += ['windows'] + + if CONFIG['MOZ_SYNTH_SPEECHD']: + DIRS += ['speechd'] + + if CONFIG['MOZ_SYNTH_PICO']: + DIRS += ['pico'] IPDL_SOURCES += [ 'ipc/PSpeechSynthesis.ipdl', diff --git a/dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp b/dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp new file mode 100644 index 000000000000..deed7c1d29b4 --- /dev/null +++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ModuleUtils.h" +#include "nsIClassInfoImpl.h" +#include "SpeechDispatcherService.h" + +using namespace mozilla::dom; + +#define SPEECHDISPATCHERSERVICE_CID \ + {0x8817b1cf, 0x5ada, 0x43bf, {0xbd, 0x73, 0x60, 0x76, 0x57, 0x70, 0x3d, 0x0d}} + +#define SPEECHDISPATCHERSERVICE_CONTRACTID "@mozilla.org/synthspeechdispatcher;1" + +// Defines SpeechDispatcherServiceConstructor +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(SpeechDispatcherService, + SpeechDispatcherService::GetInstanceForService) + +// Defines kSPEECHDISPATCHERSERVICE_CID +NS_DEFINE_NAMED_CID(SPEECHDISPATCHERSERVICE_CID); + +static const mozilla::Module::CIDEntry kCIDs[] = { + { &kSPEECHDISPATCHERSERVICE_CID, true, nullptr, SpeechDispatcherServiceConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kContracts[] = { + { SPEECHDISPATCHERSERVICE_CONTRACTID, &kSPEECHDISPATCHERSERVICE_CID }, + { nullptr } +}; + +static const mozilla::Module::CategoryEntry kCategories[] = { + { "profile-after-change", "SpeechDispatcher Speech Synth", SPEECHDISPATCHERSERVICE_CONTRACTID }, + { nullptr } +}; + +static void +UnloadSpeechDispatcherModule() +{ + SpeechDispatcherService::Shutdown(); +} + +static const mozilla::Module kModule = { + mozilla::Module::kVersion, + kCIDs, + kContracts, + kCategories, + nullptr, + nullptr, + UnloadSpeechDispatcherModule +}; + +NSMODULE_DEFN(synthspeechdispatcher) = &kModule; diff --git a/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp new file mode 100644 index 000000000000..765c17f8fc38 --- /dev/null +++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp @@ -0,0 +1,557 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SpeechDispatcherService.h" + +#include "mozilla/dom/nsSpeechTask.h" +#include "mozilla/dom/nsSynthVoiceRegistry.h" +#include "mozilla/Preferences.h" +#include "nsEscape.h" +#include "nsISupports.h" +#include "nsPrintfCString.h" +#include "nsReadableUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "prlink.h" + +#define URI_PREFIX "urn:moz-tts:sapi:" + +// Some structures for libspeechd +typedef enum { + SPD_EVENT_BEGIN, + SPD_EVENT_END, + SPD_EVENT_INDEX_MARK, + SPD_EVENT_CANCEL, + SPD_EVENT_PAUSE, + SPD_EVENT_RESUME +} SPDNotificationType; + +typedef enum { + SPD_BEGIN = 1, + SPD_END = 2, + SPD_INDEX_MARKS = 4, + SPD_CANCEL = 8, + SPD_PAUSE = 16, + SPD_RESUME = 32, + + SPD_ALL = 0x3f +} SPDNotification; + +typedef enum { + SPD_MODE_SINGLE = 0, + SPD_MODE_THREADED = 1 +} SPDConnectionMode; + +typedef void (*SPDCallback) (size_t msg_id, size_t client_id, + SPDNotificationType state); + +typedef void (*SPDCallbackIM) (size_t msg_id, size_t client_id, + SPDNotificationType state, char* index_mark); + +struct SPDConnection +{ + SPDCallback callback_begin; + SPDCallback callback_end; + SPDCallback callback_cancel; + SPDCallback callback_pause; + SPDCallback callback_resume; + SPDCallbackIM callback_im; + + /* partial, more private fields in structure */ +}; + +struct SPDVoice +{ + char* name; + char* language; + char* variant; +}; + +typedef enum { + SPD_IMPORTANT = 1, + SPD_MESSAGE = 2, + SPD_TEXT = 3, + SPD_NOTIFICATION = 4, + SPD_PROGRESS = 5 +} SPDPriority; + +#define SPEECHD_FUNCTIONS \ + FUNC(spd_open, SPDConnection*, (const char*, const char*, const char*, SPDConnectionMode)) \ + FUNC(spd_close, void, (SPDConnection*)) \ + FUNC(spd_list_synthesis_voices, SPDVoice**, (SPDConnection*)) \ + FUNC(spd_say, int, (SPDConnection*, SPDPriority, const char*)) \ + FUNC(spd_cancel, int, (SPDConnection*)) \ + FUNC(spd_set_volume, int, (SPDConnection*, int)) \ + FUNC(spd_set_voice_rate, int, (SPDConnection*, int)) \ + FUNC(spd_set_voice_pitch, int, (SPDConnection*, int)) \ + FUNC(spd_set_synthesis_voice, int, (SPDConnection*, const char*)) \ + FUNC(spd_set_notification_on, int, (SPDConnection*, SPDNotification)) + +#define FUNC(name, type, params) \ + typedef type (*_##name##_fn) params; \ + static _##name##_fn _##name; + +SPEECHD_FUNCTIONS + +#undef FUNC + +#define spd_open _spd_open +#define spd_close _spd_close +#define spd_list_synthesis_voices _spd_list_synthesis_voices +#define spd_say _spd_say +#define spd_cancel _spd_cancel +#define spd_set_volume _spd_set_volume +#define spd_set_voice_rate _spd_set_voice_rate +#define spd_set_voice_pitch _spd_set_voice_pitch +#define spd_set_synthesis_voice _spd_set_synthesis_voice +#define spd_set_notification_on _spd_set_notification_on + +static PRLibrary* speechdLib = nullptr; + +typedef void (*nsSpeechDispatcherFunc)(); +struct nsSpeechDispatcherDynamicFunction +{ + const char* functionName; + nsSpeechDispatcherFunc* function; +}; + +namespace mozilla { +namespace dom { + +StaticRefPtr SpeechDispatcherService::sSingleton; + +class SpeechDispatcherVoice +{ +public: + + SpeechDispatcherVoice(const nsAString& aName, const nsAString& aLanguage) + : mName(aName), mLanguage(aLanguage) {} + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SpeechDispatcherVoice) + + // Voice name + nsString mName; + + // Voice language, in BCP-47 syntax + nsString mLanguage; + +private: + ~SpeechDispatcherVoice() {} +}; + + +class SpeechDispatcherCallback final : public nsISpeechTaskCallback +{ +public: + SpeechDispatcherCallback(nsISpeechTask* aTask, SpeechDispatcherService* aService) + : mTask(aTask) + , mService(aService) {} + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(SpeechDispatcherCallback, nsISpeechTaskCallback) + + NS_DECL_NSISPEECHTASKCALLBACK + + bool OnSpeechEvent(SPDNotificationType state); + +private: + ~SpeechDispatcherCallback() { } + + // This pointer is used to dispatch events + nsCOMPtr mTask; + + // By holding a strong reference to the service we guarantee that it won't be + // destroyed before this runnable. + nsRefPtr mService; + + TimeStamp mStartTime; +}; + +NS_IMPL_CYCLE_COLLECTION(SpeechDispatcherCallback, mTask); + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechDispatcherCallback) + NS_INTERFACE_MAP_ENTRY(nsISpeechTaskCallback) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTaskCallback) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechDispatcherCallback) +NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechDispatcherCallback) + +NS_IMETHODIMP +SpeechDispatcherCallback::OnPause() +{ + // XXX: Speech dispatcher does not pause immediately, but waits for the speech + // to reach an index mark so that it could resume from that offset. + // There is no support for word or sentence boundaries, so index marks would + // only occur in explicit SSML marks, and we don't support that yet. + // What in actuality happens, is that if you call spd_pause(), it will speak + // the utterance in its entirety, dispatch an end event, and then put speechd + // in a 'paused' state. Since it is after the utterance ended, we don't get + // that state change, and our speech api is in an unrecoverable state. + // So, since it is useless anyway, I am not implementing pause. + return NS_OK; +} + +NS_IMETHODIMP +SpeechDispatcherCallback::OnResume() +{ + // XXX: Unsupported, see OnPause(). + return NS_OK; +} + +NS_IMETHODIMP +SpeechDispatcherCallback::OnCancel() +{ + if (spd_cancel(mService->mSpeechdClient) < 0) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +SpeechDispatcherCallback::OnVolumeChanged(float aVolume) +{ + // XXX: This currently does not change the volume mid-utterance, but it + // doesn't do anything bad either. So we could put this here with the hopes + // that speechd supports this in the future. + if (spd_set_volume(mService->mSpeechdClient, static_cast(aVolume * 100)) < 0) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +bool +SpeechDispatcherCallback::OnSpeechEvent(SPDNotificationType state) +{ + bool remove = false; + + switch (state) { + case SPD_EVENT_BEGIN: + mStartTime = TimeStamp::Now(); + mTask->DispatchStart(); + break; + + case SPD_EVENT_PAUSE: + mTask->DispatchPause((TimeStamp::Now() - mStartTime).ToSeconds(), 0); + break; + + case SPD_EVENT_RESUME: + mTask->DispatchResume((TimeStamp::Now() - mStartTime).ToSeconds(), 0); + break; + + case SPD_EVENT_CANCEL: + case SPD_EVENT_END: + mTask->DispatchEnd((TimeStamp::Now() - mStartTime).ToSeconds(), 0); + remove = true; + break; + + case SPD_EVENT_INDEX_MARK: + // Not yet supported + break; + + default: + break; + } + + return remove; +} + +static void +speechd_cb(size_t msg_id, size_t client_id, SPDNotificationType state) +{ + SpeechDispatcherService* service = SpeechDispatcherService::GetInstance(false); + + if (service) { + NS_DispatchToMainThread( + NS_NewRunnableMethodWithArgs( + service, &SpeechDispatcherService::EventNotify, + static_cast(msg_id), state)); + } +} + + +NS_INTERFACE_MAP_BEGIN(SpeechDispatcherService) + NS_INTERFACE_MAP_ENTRY(nsISpeechService) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechService) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(SpeechDispatcherService) +NS_IMPL_RELEASE(SpeechDispatcherService) + +SpeechDispatcherService::SpeechDispatcherService() + : mInitialized(false) + , mSpeechdClient(nullptr) +{ + if (!Preferences::GetBool("media.webspeech.synth.enabled") || + Preferences::GetBool("media.webspeech.synth.test")) { + return; + } + + // While speech dispatcher has a "threaded" mode, only spd_say() is async. + // Since synchronous socket i/o could impact startup time, we do + // initialization in a separate thread. + DebugOnly rv = NS_NewNamedThread("speechd init", + getter_AddRefs(mInitThread)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + rv = mInitThread->Dispatch( + NS_NewRunnableMethod(this, &SpeechDispatcherService::Init), NS_DISPATCH_NORMAL); + MOZ_ASSERT(NS_SUCCEEDED(rv)); +} + +SpeechDispatcherService::~SpeechDispatcherService() +{ + if (mInitThread) { + mInitThread->Shutdown(); + } + + if (mSpeechdClient) { + spd_close(mSpeechdClient); + } +} + +void +SpeechDispatcherService::Init() +{ +#define FUNC(name, type, params) { #name, (nsSpeechDispatcherFunc *)&_##name }, + static const nsSpeechDispatcherDynamicFunction kSpeechDispatcherSymbols[] = { + SPEECHD_FUNCTIONS + }; +#undef FUNC + + MOZ_ASSERT(!mInitialized); + + speechdLib = PR_LoadLibrary("libspeechd.so.2"); + + if (!speechdLib) { + NS_WARNING("Failed to load speechd library"); + return; + } + + for (uint32_t i = 0; i < ArrayLength(kSpeechDispatcherSymbols); i++) { + *kSpeechDispatcherSymbols[i].function = + PR_FindFunctionSymbol(speechdLib, kSpeechDispatcherSymbols[i].functionName); + + if (!*kSpeechDispatcherSymbols[i].function) { + NS_WARNING(nsPrintfCString("Failed to find speechd symbol for'%s'", + kSpeechDispatcherSymbols[i].functionName).get()); + return; + } + } + + mSpeechdClient = spd_open("firefox", "web speech api", "who", SPD_MODE_THREADED); + + // Get all the voices from sapi and register in the SynthVoiceRegistry + SPDVoice** list = spd_list_synthesis_voices(mSpeechdClient); + + mSpeechdClient->callback_begin = speechd_cb; + mSpeechdClient->callback_end = speechd_cb; + mSpeechdClient->callback_cancel = speechd_cb; + mSpeechdClient->callback_pause = speechd_cb; + mSpeechdClient->callback_resume = speechd_cb; + + spd_set_notification_on(mSpeechdClient, SPD_BEGIN); + spd_set_notification_on(mSpeechdClient, SPD_END); + spd_set_notification_on(mSpeechdClient, SPD_CANCEL); + + if (list != NULL) { + for (int i = 0; list[i]; i++) { + nsAutoString uri; + + uri.AssignLiteral(URI_PREFIX); + nsAutoCString name; + NS_EscapeURL(list[i]->name, -1, esc_OnlyNonASCII | esc_AlwaysCopy, name); + uri.Append(NS_ConvertUTF8toUTF16(name));; + uri.AppendLiteral("?"); + + nsAutoCString lang(list[i]->language); + + if (strcmp(list[i]->variant, "none") != 0) { + // In speech dispatcher, the variant will usually be the locale subtag + // with another, non-standard suptag after it. We keep the first one + // and convert it to uppercase. + const char* v = list[i]->variant; + const char* hyphen = strchr(v, '-'); + nsDependentCSubstring variant(v, hyphen ? hyphen - v : strlen(v)); + ToUpperCase(variant); + + // eSpeak uses UK which is not a valid region subtag in BCP47. + if (variant.Equals("UK")) { + variant.AssignLiteral("GB"); + } + + lang.AppendLiteral("-"); + lang.Append(variant); + } + + uri.Append(NS_ConvertUTF8toUTF16(lang)); + + mVoices.Put(uri, new SpeechDispatcherVoice( + NS_ConvertUTF8toUTF16(list[i]->name), + NS_ConvertUTF8toUTF16(lang))); + } + } + + NS_DispatchToMainThread(NS_NewRunnableMethod(this, &SpeechDispatcherService::RegisterVoices)); + + //mInitialized = true; +} + +struct VoiceTraverserData +{ + SpeechDispatcherService* mService; + nsSynthVoiceRegistry* mRegistry; +}; + +// private methods + +static PLDHashOperator +AddVoiceTraverser(const nsAString& aUri, + nsRefPtr& aVoice, + void* aUserArg) +{ + VoiceTraverserData* data = static_cast(aUserArg); + + // This service can only speak one utterance at a time, se we set + // aQueuesUtterances to true in order to track global state and schedule + // access to this service. + DebugOnly rv = data->mRegistry->AddVoice(data->mService, aUri, + aVoice->mName, aVoice->mLanguage, + aVoice->mName.EqualsLiteral("default"), true); + + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to add voice"); + + return PL_DHASH_NEXT; +} + +void +SpeechDispatcherService::RegisterVoices() +{ + VoiceTraverserData data = { this, nsSynthVoiceRegistry::GetInstance() }; + mVoices.Enumerate(AddVoiceTraverser, &data); + + mInitThread->Shutdown(); + mInitThread = nullptr; + + mInitialized = true; +} + +// TODO: Support SSML +NS_IMETHODIMP +SpeechDispatcherService::Speak(const nsAString& aText, const nsAString& aUri, + float aVolume, float aRate, float aPitch, + nsISpeechTask* aTask) +{ + if (NS_WARN_IF(!mInitialized)) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsRefPtr callback = + new SpeechDispatcherCallback(aTask, this); + + bool found = false; + SpeechDispatcherVoice* voice = mVoices.GetWeak(aUri, &found); + + if(NS_WARN_IF(!(found))) { + return NS_ERROR_NOT_AVAILABLE; + } + + spd_set_synthesis_voice(mSpeechdClient, + NS_ConvertUTF16toUTF8(voice->mName).get()); + + // We provide a volume of 0.0 to 1.0, speech-dispatcher expects 0 - 100. + spd_set_volume(mSpeechdClient, static_cast(aVolume * 100)); + + // We provide a rate of 0.1 to 10 with 1 being default. + // speech-dispatcher expects -100 to 100 with 0 being default. + int rate = 0; + + if (aRate > 1) { + rate = static_cast((aRate - 1) * 10); + } else if (aRate <= 1) { + rate = static_cast((aRate - 1) * (100/0.9)); + } + + spd_set_voice_rate(mSpeechdClient, rate); + + // We provide a pitch of 0 to 2 with 1 being the default. + // speech-dispatcher expects -100 to 100 with 0 being default. + spd_set_voice_pitch(mSpeechdClient, static_cast((aPitch - 1) * 100)); + + // The last three parameters don't matter for an indirect service + nsresult rv = aTask->Setup(callback, 0, 0, 0); + + if (NS_FAILED(rv)) { + return rv; + } + + int msg_id = spd_say(mSpeechdClient, SPD_MESSAGE, NS_ConvertUTF16toUTF8(aText).get()); + + if (msg_id < 0) { + return NS_ERROR_FAILURE; + } + + mCallbacks.Put(msg_id, callback); + + return NS_OK; +} + +NS_IMETHODIMP +SpeechDispatcherService::GetServiceType(SpeechServiceType* aServiceType) +{ + *aServiceType = nsISpeechService::SERVICETYPE_INDIRECT_AUDIO; + return NS_OK; +} + +SpeechDispatcherService* +SpeechDispatcherService::GetInstance(bool create) +{ + if (XRE_GetProcessType() != GeckoProcessType_Default) { + MOZ_ASSERT(false, + "SpeechDispatcherService can only be started on main gecko process"); + return nullptr; + } + + if (!sSingleton && create) { + sSingleton = new SpeechDispatcherService(); + } + + return sSingleton; +} + +already_AddRefed +SpeechDispatcherService::GetInstanceForService() +{ + MOZ_ASSERT(NS_IsMainThread()); + nsRefPtr sapiService = GetInstance(); + return sapiService.forget(); +} + +void +SpeechDispatcherService::EventNotify(uint32_t aMsgId, uint32_t aState) +{ + SpeechDispatcherCallback* callback = mCallbacks.GetWeak(aMsgId); + + if (callback) { + if (callback->OnSpeechEvent((SPDNotificationType)aState)) { + mCallbacks.Remove(aMsgId); + } + } +} + +void +SpeechDispatcherService::Shutdown() +{ + if (!sSingleton) { + return; + } + + sSingleton = nullptr; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/webspeech/synth/speechd/SpeechDispatcherService.h b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.h new file mode 100644 index 000000000000..0bfa97fe16ea --- /dev/null +++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SpeechDispatcherService_h +#define mozilla_dom_SpeechDispatcherService_h + +#include "mozilla/StaticPtr.h" +#include "nsAutoPtr.h" +#include "nsISpeechService.h" +#include "nsIThread.h" +#include "nsRefPtrHashtable.h" +#include "nsTArray.h" + +struct SPDConnection; + +namespace mozilla { +namespace dom { + +class SpeechDispatcherCallback; +class SpeechDispatcherVoice; + +class SpeechDispatcherService final : public nsISpeechService +{ + friend class SpeechDispatcherCallback; +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISPEECHSERVICE + + SpeechDispatcherService(); + void Init(); + + void EventNotify(uint32_t aMsgId, uint32_t aState); + + static SpeechDispatcherService* GetInstance(bool create = true); + static already_AddRefed GetInstanceForService(); + + static void Shutdown(); + + static StaticRefPtr sSingleton; + +private: + virtual ~SpeechDispatcherService(); + + void RegisterVoices(); + + bool mInitialized; + + SPDConnection* mSpeechdClient; + + nsRefPtrHashtable mCallbacks; + + nsCOMPtr mInitThread; + + nsRefPtrHashtable mVoices; +}; + +} // namespace dom +} // namespace mozilla +#endif diff --git a/dom/media/webspeech/synth/speechd/moz.build b/dom/media/webspeech/synth/speechd/moz.build new file mode 100644 index 000000000000..3d8c8c015c05 --- /dev/null +++ b/dom/media/webspeech/synth/speechd/moz.build @@ -0,0 +1,13 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +UNIFIED_SOURCES += [ + 'SpeechDispatcherModule.cpp', + 'SpeechDispatcherService.cpp' +] +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' diff --git a/dom/network/TCPServerSocket.cpp b/dom/network/TCPServerSocket.cpp index 7aefd7c9fbd6..ecf724689799 100644 --- a/dom/network/TCPServerSocket.cpp +++ b/dom/network/TCPServerSocket.cpp @@ -123,7 +123,7 @@ void TCPServerSocket::FireEvent(const nsAString& aType, TCPSocket* aSocket) { AutoJSAPI api; - api.Init(GetOwner()); + api.Init(GetOwnerGlobal()); TCPServerSocketEventInit init; init.mBubbles = false; @@ -144,7 +144,7 @@ TCPServerSocket::FireEvent(const nsAString& aType, TCPSocket* aSocket) NS_IMETHODIMP TCPServerSocket::OnSocketAccepted(nsIServerSocket* aServer, nsISocketTransport* aTransport) { - nsCOMPtr global = do_QueryInterface(GetOwner()); + nsCOMPtr global = GetOwnerGlobal(); nsRefPtr socket = TCPSocket::CreateAcceptedSocket(global, aTransport, mUseArrayBuffers); if (mServerBridgeParent) { socket->SetAppIdAndBrowser(mServerBridgeParent->GetAppId(), @@ -175,7 +175,7 @@ TCPServerSocket::OnStopListening(nsIServerSocket* aServer, nsresult aStatus) nsresult TCPServerSocket::AcceptChildSocket(TCPSocketChild* aSocketChild) { - nsCOMPtr global = do_QueryInterface(GetOwner()); + nsCOMPtr global = GetOwnerGlobal(); NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); nsRefPtr socket = TCPSocket::CreateAcceptedSocket(global, aSocketChild, mUseArrayBuffers); NS_ENSURE_TRUE(socket, NS_ERROR_FAILURE); diff --git a/dom/network/TCPSocket.cpp b/dom/network/TCPSocket.cpp index cc978c2d5f58..73e0e1b2fa79 100644 --- a/dom/network/TCPSocket.cpp +++ b/dom/network/TCPSocket.cpp @@ -493,7 +493,7 @@ TCPSocket::FireEvent(const nsAString& aType) } AutoJSAPI api; - if (NS_WARN_IF(!api.Init(GetOwner()))) { + if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) { return NS_ERROR_FAILURE; } JS::Rooted val(api.cx()); @@ -505,7 +505,7 @@ TCPSocket::FireDataArrayEvent(const nsAString& aType, const InfallibleTArray& buffer) { AutoJSAPI api; - if (NS_WARN_IF(!api.Init(GetOwner()))) { + if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) { return NS_ERROR_FAILURE; } JSContext* cx = api.cx(); @@ -523,7 +523,7 @@ TCPSocket::FireDataStringEvent(const nsAString& aType, const nsACString& aString) { AutoJSAPI api; - if (NS_WARN_IF(!api.Init(GetOwner()))) { + if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) { return NS_ERROR_FAILURE; } JSContext* cx = api.cx(); @@ -1034,7 +1034,7 @@ TCPSocket::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInput } AutoJSAPI api; - if (!api.Init(GetOwner())) { + if (!api.Init(GetOwnerGlobal())) { return NS_ERROR_FAILURE; } JSContext* cx = api.cx(); @@ -1057,7 +1057,7 @@ TCPSocket::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInput } AutoJSAPI api; - if (!api.Init(GetOwner())) { + if (!api.Init(GetOwnerGlobal())) { return NS_ERROR_FAILURE; } JSContext* cx = api.cx(); @@ -1189,3 +1189,17 @@ TCPSocket::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aD return NS_OK; } + +/* static */ +bool +TCPSocket::ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal) +{ + JS::Rooted global(aCx, aGlobal); + if (nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global))) { + return true; + } + + const char* const perms[] = { "tcp-socket", nullptr }; + return Preferences::GetBool("dom.mozTCPSocket.enabled") && + CheckAnyPermissions(aCx, global, perms); +} diff --git a/dom/network/TCPSocket.h b/dom/network/TCPSocket.h index b3c692325924..10bc181b1ef0 100644 --- a/dom/network/TCPSocket.h +++ b/dom/network/TCPSocket.h @@ -88,13 +88,10 @@ public: NS_DECL_NSIOBSERVER NS_DECL_NSITCPSOCKETCALLBACK - nsPIDOMWindow* GetParentObject() const - { - return GetOwner(); - } - virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + static bool ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal); + void GetHost(nsAString& aHost); uint32_t Port(); bool Ssl(); diff --git a/dom/network/moz.build b/dom/network/moz.build index 7ad57f99e4a0..b42c30734f84 100644 --- a/dom/network/moz.build +++ b/dom/network/moz.build @@ -9,6 +9,7 @@ DIRS += ['interfaces'] if CONFIG['MOZ_B2G_RIL']: XPCSHELL_TESTS_MANIFESTS += ['tests/unit_stats/xpcshell.ini'] +MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini'] MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] EXPORTS.mozilla.dom += [ diff --git a/dom/network/tests/chrome.ini b/dom/network/tests/chrome.ini new file mode 100644 index 000000000000..7e2485643b42 --- /dev/null +++ b/dom/network/tests/chrome.ini @@ -0,0 +1,7 @@ +[DEFAULT] +support-files = + tcpsocket_test.jsm + test_tcpsocket_client_and_server_basics.js + add_task.js + +[test_tcpsocket_jsm.html] \ No newline at end of file diff --git a/dom/network/tests/tcpsocket_test.jsm b/dom/network/tests/tcpsocket_test.jsm new file mode 100644 index 000000000000..367833af344d --- /dev/null +++ b/dom/network/tests/tcpsocket_test.jsm @@ -0,0 +1,13 @@ +this.EXPORTED_SYMBOLS = ['createSocket', 'createServer', 'enablePrefsAndPermissions']; + +this.createSocket = function(host, port, options) { + return new TCPSocket(host, port, options); +} + +this.createServer = function(port, options, backlog) { + return new TCPServerSocket(port, options, backlog); +} + +this.enablePrefsAndPermissions = function() { + return false; +} diff --git a/dom/network/tests/test_tcpsocket_client_and_server_basics.html b/dom/network/tests/test_tcpsocket_client_and_server_basics.html index bff6071f3ca5..99e8e88d8e09 100644 --- a/dom/network/tests/test_tcpsocket_client_and_server_basics.html +++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.html @@ -13,6 +13,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1084245 + diff --git a/dom/network/tests/test_tcpsocket_client_and_server_basics.js b/dom/network/tests/test_tcpsocket_client_and_server_basics.js index d3aa43b19e6f..69854276b0e1 100644 --- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js +++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js @@ -153,18 +153,20 @@ function defer() { function* test_basics() { - // Enable our use of TCPSocket - let prefDeferred = defer(); - SpecialPowers.pushPrefEnv( - { set: [ ['dom.mozTCPSocket.enabled', true] ] }, - prefDeferred.resolve); - yield prefDeferred.promise; + if (enablePrefsAndPermissions()) { + // Enable our use of TCPSocket + let prefDeferred = defer(); + SpecialPowers.pushPrefEnv( + { set: [ ['dom.mozTCPSocket.enabled', true] ] }, + prefDeferred.resolve); + yield prefDeferred.promise; - let permDeferred = defer(); - SpecialPowers.pushPermissions( - [ { type: 'tcp-socket', allow: true, context: document } ], - permDeferred.resolve); - yield permDeferred.promise; + let permDeferred = defer(); + SpecialPowers.pushPermissions( + [ { type: 'tcp-socket', allow: true, context: document } ], + permDeferred.resolve); + yield permDeferred.promise; + } // See bug 903830; in e10s mode we never get to find out the localPort if we // let it pick a free port by choosing 0. This is the same port the xpcshell @@ -172,15 +174,15 @@ function* test_basics() { let serverPort = 8085; // - Start up a listening socket. - let listeningServer = new TCPServerSocket(serverPort, - { binaryType: 'arraybuffer' }, - SERVER_BACKLOG); + let listeningServer = createServer(serverPort, + { binaryType: 'arraybuffer' }, + SERVER_BACKLOG); let connectedPromise = waitForConnection(listeningServer); // -- Open a connection to the server - let clientSocket = new TCPSocket('127.0.0.1', serverPort, - { binaryType: 'arraybuffer' }); + let clientSocket = createSocket('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); let clientQueue = listenForEventsOnSocket(clientSocket, 'client'); // (the client connects) @@ -286,8 +288,8 @@ function* test_basics() { // -- Re-establish connection connectedPromise = waitForConnection(listeningServer); - clientSocket = new TCPSocket('127.0.0.1', serverPort, - { binaryType: 'arraybuffer' }); + clientSocket = createSocket('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); clientQueue = listenForEventsOnSocket(clientSocket, 'client'); is((yield clientQueue.waitForEvent()).type, 'open', 'got open event'); @@ -313,8 +315,8 @@ function* test_basics() { // -- Re-establish connection connectedPromise = waitForConnection(listeningServer); - clientSocket = new TCPSocket('127.0.0.1', serverPort, - { binaryType: 'arraybuffer' }); + clientSocket = createSocket('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); clientQueue = listenForEventsOnSocket(clientSocket, 'client'); is((yield clientQueue.waitForEvent()).type, 'open', 'got open event'); @@ -347,8 +349,8 @@ function* test_basics() { // -- Re-establish connection connectedPromise = waitForConnection(listeningServer); - clientSocket = new TCPSocket('127.0.0.1', serverPort, - { binaryType: 'string' }); + clientSocket = createSocket('127.0.0.1', serverPort, + { binaryType: 'string' }); clientQueue = listenForEventsOnSocket(clientSocket, 'client'); is((yield clientQueue.waitForEvent()).type, 'open', 'got open event'); @@ -375,8 +377,8 @@ function* test_basics() { listeningServer.close(); // - try and connect, get an error - clientSocket = new TCPSocket('127.0.0.1', serverPort, - { binaryType: 'arraybuffer' }); + clientSocket = createSocket('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); clientQueue = listenForEventsOnSocket(clientSocket, 'client'); is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect'); is(clientSocket.readyState, 'closed', diff --git a/dom/network/tests/test_tcpsocket_jsm.html b/dom/network/tests/test_tcpsocket_jsm.html new file mode 100644 index 000000000000..508c3c956948 --- /dev/null +++ b/dom/network/tests/test_tcpsocket_jsm.html @@ -0,0 +1,25 @@ + + + + + + Test for 1207090 + + + + + + + +Mozilla Bug 1207090 +

+ +
+
+
+ + diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index 284f3cc5acd7..bf90b483f7c5 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -31,7 +31,6 @@ #include "gfxXlibSurface.h" #endif #include "gfxContext.h" -#include "gfxColor.h" #include "gfxUtils.h" #include "mozilla/gfx/2D.h" #include "Layers.h" diff --git a/dom/webidl/TCPServerSocket.webidl b/dom/webidl/TCPServerSocket.webidl index e629fa32a86c..06d6689d2d00 100644 --- a/dom/webidl/TCPServerSocket.webidl +++ b/dom/webidl/TCPServerSocket.webidl @@ -14,9 +14,8 @@ dictionary ServerSocketOptions { }; [Constructor(unsigned short port, optional ServerSocketOptions options, optional unsigned short backlog = 0), - Pref="dom.mozTCPSocket.enabled", - CheckAnyPermissions="tcp-socket", - Exposed=Window] + Func="mozilla::dom::TCPSocket::ShouldTCPSocketExist", + Exposed=(Window,System)] interface TCPServerSocket : EventTarget { /** * The port of this server socket object. diff --git a/dom/webidl/TCPServerSocketEvent.webidl b/dom/webidl/TCPServerSocketEvent.webidl index 0842d8553db7..f133ae7812f8 100644 --- a/dom/webidl/TCPServerSocketEvent.webidl +++ b/dom/webidl/TCPServerSocketEvent.webidl @@ -4,9 +4,8 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ [Constructor(DOMString type, optional TCPServerSocketEventInit eventInitDict), - Pref="dom.mozTCPSocket.enabled", - CheckAnyPermissions="tcp-socket", - Exposed=Window] + Func="mozilla::dom::TCPSocket::ShouldTCPSocketExist", + Exposed=(Window,System)] interface TCPServerSocketEvent : Event { readonly attribute TCPSocket socket; }; diff --git a/dom/webidl/TCPSocket.webidl b/dom/webidl/TCPSocket.webidl index 0f8378ea2d02..db0336979791 100644 --- a/dom/webidl/TCPSocket.webidl +++ b/dom/webidl/TCPSocket.webidl @@ -40,9 +40,8 @@ interface LegacyMozTCPSocket { }; [Constructor(DOMString host, unsigned short port, optional SocketOptions options), - Pref="dom.mozTCPSocket.enabled", - CheckAnyPermissions="tcp-socket", - Exposed=Window] + Func="mozilla::dom::TCPSocket::ShouldTCPSocketExist", + Exposed=(Window,System)] interface TCPSocket : EventTarget { /** * Upgrade an insecure connection to use TLS. Throws if the ready state is not OPEN. diff --git a/dom/webidl/TCPSocketErrorEvent.webidl b/dom/webidl/TCPSocketErrorEvent.webidl index f8aa919ca80f..a4c98da6c4aa 100644 --- a/dom/webidl/TCPSocketErrorEvent.webidl +++ b/dom/webidl/TCPSocketErrorEvent.webidl @@ -9,9 +9,9 @@ * - if there's an error connecting to the host */ -[Pref="dom.mozTCPSocket.enabled", - CheckAnyPermissions="tcp-socket", - Constructor(DOMString type, optional TCPSocketErrorEventInit eventInitDict)] +[Func="mozilla::dom::TCPSocket::ShouldTCPSocketExist", + Constructor(DOMString type, optional TCPSocketErrorEventInit eventInitDict), + Exposed=(Window,System)] interface TCPSocketErrorEvent : Event { readonly attribute DOMString name; readonly attribute DOMString message; diff --git a/dom/webidl/TCPSocketEvent.webidl b/dom/webidl/TCPSocketEvent.webidl index f7d82f5f2e78..6a3d6851df4d 100644 --- a/dom/webidl/TCPSocketEvent.webidl +++ b/dom/webidl/TCPSocketEvent.webidl @@ -10,9 +10,8 @@ */ [Constructor(DOMString type, optional TCPSocketEventInit eventInitDict), - Pref="dom.mozTCPSocket.enabled", - CheckAnyPermissions="tcp-socket", - Exposed=Window] + Func="mozilla::dom::TCPSocket::ShouldTCPSocketExist", + Exposed=(Window,System)] interface TCPSocketEvent : Event { /** * If the event is a "data" event, data will be the bytes read from the network; diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 4564ee98b0de..d4c683be6949 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -355,6 +355,8 @@ LoadRuntimeOptions(const char* aPrefName, void* /* aClosure */) // Runtime options. JS::RuntimeOptions runtimeOptions; runtimeOptions.setAsmJS(GetWorkerPref(NS_LITERAL_CSTRING("asmjs"))) + .setThrowOnAsmJSValidationFailure(GetWorkerPref( + NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure"))) .setBaseline(GetWorkerPref(NS_LITERAL_CSTRING("baselinejit"))) .setIon(GetWorkerPref(NS_LITERAL_CSTRING("ion"))) .setNativeRegExp(GetWorkerPref(NS_LITERAL_CSTRING("native_regexp"))) diff --git a/dom/workers/ServiceWorkerRegistrar.cpp b/dom/workers/ServiceWorkerRegistrar.cpp index bd82b79f343c..138349d56223 100644 --- a/dom/workers/ServiceWorkerRegistrar.cpp +++ b/dom/workers/ServiceWorkerRegistrar.cpp @@ -332,8 +332,9 @@ ServiceWorkerRegistrar::ReadData() } GET_LINE(line); + nsCString signedPkg = NS_ConvertUTF16toUTF8(attrs.mSignedPkg); entry->principal() = - mozilla::ipc::ContentPrincipalInfo(attrs, line); + mozilla::ipc::ContentPrincipalInfo(attrs.mAppId, attrs.mInBrowser, line, signedPkg); GET_LINE(entry->scope()); GET_LINE(entry->scriptSpec()); @@ -548,8 +549,9 @@ ServiceWorkerRegistrar::WriteData() const mozilla::ipc::ContentPrincipalInfo& cInfo = info.get_ContentPrincipalInfo(); + OriginAttributes attrs(cInfo.appId(), cInfo.isInBrowserElement()); nsAutoCString suffix; - cInfo.attrs().CreateSuffix(suffix); + attrs.CreateSuffix(suffix); buffer.Truncate(); buffer.Append(suffix.get()); diff --git a/dom/workers/test/gtest/TestReadWrite.cpp b/dom/workers/test/gtest/TestReadWrite.cpp index b77efb74f31c..8c1514b043a4 100644 --- a/dom/workers/test/gtest/TestReadWrite.cpp +++ b/dom/workers/test/gtest/TestReadWrite.cpp @@ -163,8 +163,9 @@ TEST(ServiceWorkerRegistrar, TestReadData) ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal(); + mozilla::OriginAttributes attrs0(cInfo0.appId(), cInfo0.isInBrowserElement()); nsAutoCString suffix0; - cInfo0.attrs().CreateSuffix(suffix0); + attrs0.CreateSuffix(suffix0); ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get()); ASSERT_STREQ("spec 0", cInfo0.spec().get()); @@ -178,8 +179,9 @@ TEST(ServiceWorkerRegistrar, TestReadData) ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal(); + mozilla::OriginAttributes attrs1(cInfo1.appId(), cInfo1.isInBrowserElement()); nsAutoCString suffix1; - cInfo1.attrs().CreateSuffix(suffix1); + attrs1.CreateSuffix(suffix1); ASSERT_STREQ("", suffix1.get()); ASSERT_STREQ("spec 1", cInfo1.spec().get()); @@ -219,7 +221,7 @@ TEST(ServiceWorkerRegistrar, TestWriteData) nsAutoCString spec; spec.AppendPrintf("spec write %d", i); - d->principal() = mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(i, i % 2), spec); + d->principal() = mozilla::ipc::ContentPrincipalInfo(i, i % 2, spec, EmptyCString()); d->scope().AppendPrintf("scope write %d", i); d->scriptSpec().AppendPrintf("scriptSpec write %d", i); d->currentWorkerURL().AppendPrintf("currentWorkerURL write %d", i); @@ -245,12 +247,8 @@ TEST(ServiceWorkerRegistrar, TestWriteData) ASSERT_EQ(data[i].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo); const mozilla::ipc::ContentPrincipalInfo& cInfo = data[i].principal(); - mozilla::OriginAttributes attrs(i, i % 2); - nsAutoCString suffix, expectSuffix; - attrs.CreateSuffix(expectSuffix); - cInfo.attrs().CreateSuffix(suffix); - - ASSERT_STREQ(expectSuffix.get(), suffix.get()); + ASSERT_EQ((uint32_t)i, cInfo.appId()); + ASSERT_EQ((uint32_t)(i % 2), (uint32_t)cInfo.isInBrowserElement()); test.AppendPrintf("spec write %d", i); ASSERT_STREQ(test.get(), cInfo.spec().get()); diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index b8761294d5a6..5a1172ecfcdb 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -39,7 +39,6 @@ #ifdef XP_MACOSX #include -#include "gfxColor.h" #endif #if defined(MOZ_WIDGET_COCOA) diff --git a/gfx/gl/GLReadTexImageHelper.cpp b/gfx/gl/GLReadTexImageHelper.cpp index 6ba1a1238bcb..d93b4914937f 100644 --- a/gfx/gl/GLReadTexImageHelper.cpp +++ b/gfx/gl/GLReadTexImageHelper.cpp @@ -7,6 +7,7 @@ #include "GLReadTexImageHelper.h" #include "gfx2DGlue.h" +#include "gfxColor.h" #include "gfxTypes.h" #include "GLContext.h" #include "OGLShaderProgram.h" diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 79c7e47074c3..ccafabbb5c1e 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -13,7 +13,6 @@ #include -#include "gfxColor.h" #include "mozilla/gfx/Matrix.h" #include "GraphicsFilter.h" #include "gfxPoint.h" @@ -293,28 +292,6 @@ struct ParamTraits {}; */ -template<> -struct ParamTraits -{ - typedef gfxRGBA paramType; - - static void Write(Message* msg, const paramType& param) - { - WriteParam(msg, param.r); - WriteParam(msg, param.g); - WriteParam(msg, param.b); - WriteParam(msg, param.a); - } - - static bool Read(const Message* msg, void** iter, paramType* result) - { - return (ReadParam(msg, iter, &result->r) && - ReadParam(msg, iter, &result->g) && - ReadParam(msg, iter, &result->b) && - ReadParam(msg, iter, &result->a)); - } -}; - template<> struct ParamTraits { @@ -915,51 +892,6 @@ struct ParamTraits } }; -struct MessageAndAttributeMap -{ - Message* msg; - const mozilla::gfx::AttributeMap& map; -}; - -static bool -WriteAttribute(mozilla::gfx::AttributeName aName, - mozilla::gfx::AttributeType aType, - void* aUserData) -{ - MessageAndAttributeMap* msgAndMap = - static_cast(aUserData); - - WriteParam(msgAndMap->msg, aType); - WriteParam(msgAndMap->msg, aName); - - switch (aType) { - -#define HANDLE_TYPE(typeName) \ - case mozilla::gfx::AttributeType::e##typeName: \ - WriteParam(msgAndMap->msg, msgAndMap->map.Get##typeName(aName)); \ - break; - - HANDLE_TYPE(Bool) - HANDLE_TYPE(Uint) - HANDLE_TYPE(Float) - HANDLE_TYPE(Size) - HANDLE_TYPE(IntSize) - HANDLE_TYPE(IntPoint) - HANDLE_TYPE(Matrix) - HANDLE_TYPE(Matrix5x4) - HANDLE_TYPE(Point3D) - HANDLE_TYPE(Color) - HANDLE_TYPE(AttributeMap) - HANDLE_TYPE(Floats) - -#undef HANDLE_TYPE - - default: - MOZ_CRASH("unhandled attribute type"); - } - return true; -} - template <> struct ParamTraits { @@ -968,8 +900,41 @@ struct ParamTraits static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.Count()); - MessageAndAttributeMap msgAndMap = { aMsg, aParam }; - aParam.EnumerateRead(WriteAttribute, &msgAndMap); + for (auto iter = aParam.ConstIter(); !iter.Done(); iter.Next()) { + mozilla::gfx::AttributeName name = + mozilla::gfx::AttributeName(iter.Key()); + mozilla::gfx::AttributeType type = + mozilla::gfx::AttributeMap::GetType(iter.UserData()); + + WriteParam(aMsg, type); + WriteParam(aMsg, name); + + switch (type) { + +#define CASE_TYPE(typeName) \ + case mozilla::gfx::AttributeType::e##typeName: \ + WriteParam(aMsg, aParam.Get##typeName(name)); \ + break; + + CASE_TYPE(Bool) + CASE_TYPE(Uint) + CASE_TYPE(Float) + CASE_TYPE(Size) + CASE_TYPE(IntSize) + CASE_TYPE(IntPoint) + CASE_TYPE(Matrix) + CASE_TYPE(Matrix5x4) + CASE_TYPE(Point3D) + CASE_TYPE(Color) + CASE_TYPE(AttributeMap) + CASE_TYPE(Floats) + +#undef CASE_TYPE + + default: + MOZ_CRASH("unhandled attribute type"); + } + } } static bool Read(const Message* aMsg, void** aIter, paramType* aResult) diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index bdd6179401d1..d3eb4c287ec2 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -13,7 +13,6 @@ #include "mozilla/gfx/Rect.h" // for RoundedIn #include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor #include "mozilla/gfx/Logging.h" // for Log -#include "gfxColor.h" #include "nsString.h" namespace IPC { diff --git a/gfx/layers/LayerScope.cpp b/gfx/layers/LayerScope.cpp index a4f86208ae4b..9c5bfd53e4ed 100644 --- a/gfx/layers/LayerScope.cpp +++ b/gfx/layers/LayerScope.cpp @@ -21,7 +21,6 @@ #include "mozilla/layers/LayerManagerComposite.h" #include "mozilla/layers/TextureHostOGL.h" -#include "gfxColor.h" #include "gfxContext.h" #include "gfxUtils.h" #include "gfxPrefs.h" @@ -599,12 +598,12 @@ protected: class DebugGLColorData final: public DebugGLData { public: DebugGLColorData(void* layerRef, - const gfxRGBA& color, + const Color& color, int width, int height) : DebugGLData(Packet::COLOR), mLayerRef(reinterpret_cast(layerRef)), - mColor(color.Packed()), + mColor(color.ToABGR()), mSize(width, height) { } @@ -902,7 +901,7 @@ public: // Sender private functions private: static void SendColor(void* aLayerRef, - const gfxRGBA& aColor, + const Color& aColor, int aWidth, int aHeight); static void SendTextureSource(GLContext* aGLContext, @@ -1004,7 +1003,7 @@ SenderHelper::SendLayer(LayerComposite* aLayer, void SenderHelper::SendColor(void* aLayerRef, - const gfxRGBA& aColor, + const Color& aColor, int aWidth, int aHeight) { @@ -1179,11 +1178,8 @@ SenderHelper::SendEffectChain(GLContext* aGLContext, case EffectTypes::SOLID_COLOR: { const EffectSolidColor* solidColorEffect = static_cast(primaryEffect); - gfxRGBA color(solidColorEffect->mColor.r, - solidColorEffect->mColor.g, - solidColorEffect->mColor.b, - solidColorEffect->mColor.a); - SendColor(aEffectChain.mLayerRef, color, aWidth, aHeight); + SendColor(aEffectChain.mLayerRef, solidColorEffect->mColor, + aWidth, aHeight); break; } case EffectTypes::COMPONENT_ALPHA: diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index 5d507608ae06..8c2a45921cf6 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -10,7 +10,6 @@ #include "ImageLayers.h" // for ImageLayer, etc #include "Layers.h" // for Layer, ContainerLayer, etc #include "Units.h" // for ParentLayerIntRect -#include "gfxColor.h" // for gfxRGBA #include "GraphicsFilter.h" // for GraphicsFilter #include "gfxRect.h" // for gfxRect #include "gfxUtils.h" // for gfxUtils diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index b3b2cfc961c9..88238216f065 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -13,7 +13,6 @@ #include "Units.h" // for LayerMargin, LayerPoint, ParentLayerIntRect #include "gfxContext.h" #include "gfxTypes.h" -#include "gfxColor.h" // for gfxRGBA #include "GraphicsFilter.h" // for GraphicsFilter #include "gfxPoint.h" // for gfxPoint #include "gfxRect.h" // for gfxRect diff --git a/gfx/layers/LayersLogging.cpp b/gfx/layers/LayersLogging.cpp index ea917b69292a..4bef36ad30af 100644 --- a/gfx/layers/LayersLogging.cpp +++ b/gfx/layers/LayersLogging.cpp @@ -7,7 +7,6 @@ #include "LayersLogging.h" #include // for uint8_t -#include "gfxColor.h" // for gfxRGBA #include "ImageTypes.h" // for ImageFormat #include "mozilla/gfx/Matrix.h" // for Matrix4x4, Matrix #include "mozilla/gfx/Point.h" // for IntSize @@ -57,17 +56,6 @@ AppendToString(std::stringstream& aStream, FrameMetrics::ViewID n, aStream << sfx; } -void -AppendToString(std::stringstream& aStream, const gfxRGBA& c, - const char* pfx, const char* sfx) -{ - aStream << pfx; - aStream << nsPrintfCString( - "rgba(%d, %d, %d, %g)", - uint8_t(c.r*255.0), uint8_t(c.g*255.0), uint8_t(c.b*255.0), c.a).get(); - aStream << sfx; -} - void AppendToString(std::stringstream& aStream, const Color& c, const char* pfx, const char* sfx) diff --git a/gfx/layers/LayersLogging.h b/gfx/layers/LayersLogging.h index ebc456c023e1..25340405f1e0 100644 --- a/gfx/layers/LayersLogging.h +++ b/gfx/layers/LayersLogging.h @@ -16,8 +16,6 @@ #include "nsRegion.h" // for nsRegion, nsIntRegion #include "nscore.h" // for nsACString, etc -struct gfxRGBA; - namespace mozilla { namespace gfx { class Matrix4x4; @@ -40,10 +38,6 @@ void AppendToString(std::stringstream& aStream, FrameMetrics::ViewID n, const char* pfx="", const char* sfx=""); -void -AppendToString(std::stringstream& aStream, const gfxRGBA& c, - const char* pfx="", const char* sfx=""); - void AppendToString(std::stringstream& aStream, const gfx::Color& c, const char* pfx="", const char* sfx=""); diff --git a/gfx/layers/ReadbackLayer.h b/gfx/layers/ReadbackLayer.h index eb2daa06c33a..69a4f6e3297a 100644 --- a/gfx/layers/ReadbackLayer.h +++ b/gfx/layers/ReadbackLayer.h @@ -8,9 +8,8 @@ #include // for uint64_t #include "Layers.h" // for Layer, etc -#include "gfxColor.h" // for gfxRGBA -#include "mozilla/gfx/Rect.h" // for gfxRect -#include "mozilla/gfx/Point.h" // for gfxRect +#include "mozilla/gfx/Rect.h" // for gfxRect +#include "mozilla/gfx/Point.h" // for IntPoint #include "mozilla/mozalloc.h" // for operator delete #include "nsAutoPtr.h" // for nsAutoPtr #include "nsCOMPtr.h" // for already_AddRefed diff --git a/gfx/layers/ReadbackProcessor.cpp b/gfx/layers/ReadbackProcessor.cpp index 88755a14377e..b31775407f5d 100644 --- a/gfx/layers/ReadbackProcessor.cpp +++ b/gfx/layers/ReadbackProcessor.cpp @@ -9,7 +9,6 @@ #include "ReadbackLayer.h" // for ReadbackLayer, ReadbackSink #include "UnitTransforms.h" // for ViewAs #include "Units.h" // for ParentLayerIntRect -#include "gfxColor.h" // for gfxRGBA #include "gfxContext.h" // for gfxContext #include "gfxUtils.h" #include "gfxRect.h" // for gfxRect diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp index 87081f1d01d2..0f014bb33296 100644 --- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -17,7 +17,6 @@ #include "basic/BasicImplData.h" // for BasicImplData #include "basic/BasicLayers.h" // for BasicLayerManager, etc #include "gfxASurface.h" // for gfxASurface, etc -#include "gfxColor.h" // for gfxRGBA #include "gfxContext.h" // for gfxContext, etc #include "gfxImageSurface.h" // for gfxImageSurface #include "gfxMatrix.h" // for gfxMatrix diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index 1efa41da06af..ea87d1421530 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -5,7 +5,6 @@ #include "mozilla/layers/ContentClient.h" #include "BasicLayers.h" // for BasicLayerManager -#include "gfxColor.h" // for gfxRGBA #include "gfxContext.h" // for gfxContext, etc #include "gfxPlatform.h" // for gfxPlatform #include "gfxPrefs.h" // for gfxPrefs diff --git a/gfx/layers/composite/ColorLayerComposite.cpp b/gfx/layers/composite/ColorLayerComposite.cpp index a6daa29338fd..179e20102b8e 100644 --- a/gfx/layers/composite/ColorLayerComposite.cpp +++ b/gfx/layers/composite/ColorLayerComposite.cpp @@ -4,7 +4,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ColorLayerComposite.h" -#include "gfxColor.h" // for gfxRGBA #include "mozilla/RefPtr.h" // for RefPtr #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/gfx/Point.h" // for Point diff --git a/gfx/layers/ipc/CompositorBench.cpp b/gfx/layers/ipc/CompositorBench.cpp index 2c0f893dad2a..5b9e61c2516e 100644 --- a/gfx/layers/ipc/CompositorBench.cpp +++ b/gfx/layers/ipc/CompositorBench.cpp @@ -10,7 +10,6 @@ #include "mozilla/layers/Compositor.h" #include "mozilla/layers/Effects.h" #include "mozilla/TimeStamp.h" -#include "gfxColor.h" #include "gfxPrefs.h" #include #include "GeckoProfiler.h" @@ -96,15 +95,11 @@ public: {} void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) { - float red; float tmp; - red = modf(aStep * 0.03f, &tmp); + float red = modff(aStep * 0.03f, &tmp); EffectChain effects; - gfxRGBA color(red, 0.4f, 0.4f, 1.0f); - effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(color.r, - color.g, - color.b, - color.a)); + effects.mPrimaryEffect = + new EffectSolidColor(gfx::Color(red, 0.4f, 0.4f, 1.0f)); const gfx::Rect& rect = aScreenRect; const gfx::Rect& clipRect = aScreenRect; @@ -130,15 +125,10 @@ public: } already_AddRefed CreateEffect(size_t i) { - float red; float tmp; - red = modf(i * 0.03f, &tmp); + float red = modff(i * 0.03f, &tmp); EffectChain effects; - gfxRGBA color(red, 0.4f, 0.4f, 1.0f); - return MakeAndAddRef(gfx::Color(color.r, - color.g, - color.b, - color.a)); + return MakeAndAddRef(gfx::Color(red, 0.4f, 0.4f, 1.0f)); } }; @@ -156,15 +146,10 @@ public: } already_AddRefed CreateEffect(size_t i) { - float red; float tmp; - red = modf(i * 0.03f, &tmp); + float red = modff(i * 0.03f, &tmp); EffectChain effects; - gfxRGBA color(red, 0.4f, 0.4f, 1.0f); - return MakeAndAddRef(gfx::Color(color.r, - color.g, - color.b, - color.a)); + return MakeAndAddRef(gfx::Color(red, 0.4f, 0.4f, 1.0f)); } }; diff --git a/gfx/layers/opengl/OGLShaderProgram.cpp b/gfx/layers/opengl/OGLShaderProgram.cpp index 2bc22cf9f7c2..3a0ce9abbae3 100644 --- a/gfx/layers/opengl/OGLShaderProgram.cpp +++ b/gfx/layers/opengl/OGLShaderProgram.cpp @@ -14,8 +14,6 @@ #include "Layers.h" #include "GLContext.h" -struct gfxRGBA; - namespace mozilla { namespace layers { diff --git a/gfx/layers/opengl/OGLShaderProgram.h b/gfx/layers/opengl/OGLShaderProgram.h index 83ddac53efe7..f1d5ae1b70e7 100644 --- a/gfx/layers/opengl/OGLShaderProgram.h +++ b/gfx/layers/opengl/OGLShaderProgram.h @@ -20,8 +20,6 @@ #include -struct gfxRGBA; - namespace mozilla { namespace layers { @@ -435,10 +433,6 @@ public: SetUniform(KnownUniform::MaskTexture, aUnit); } - void SetRenderColor(const gfxRGBA& aColor) { - SetUniform(KnownUniform::RenderColor, aColor); - } - void SetRenderColor(const gfx::Color& aColor) { SetUniform(KnownUniform::RenderColor, aColor); } @@ -507,17 +501,6 @@ protected: } } - void SetUniform(KnownUniform::KnownUniformName aKnownUniform, const gfxRGBA& aColor) - { - ASSERT_THIS_PROGRAM; - NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform"); - - KnownUniform& ku(mProfile.mUniforms[aKnownUniform]); - if (ku.UpdateUniform(aColor.r, aColor.g, aColor.b, aColor.a)) { - mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v); - } - } - void SetUniform(KnownUniform::KnownUniformName aKnownUniform, const gfx::Color& aColor) { ASSERT_THIS_PROGRAM; NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform"); diff --git a/gfx/src/FilterSupport.cpp b/gfx/src/FilterSupport.cpp index 5b1a82453ded..8ff67f95720e 100644 --- a/gfx/src/FilterSupport.cpp +++ b/gfx/src/FilterSupport.cpp @@ -2045,20 +2045,13 @@ AttributeMap::~AttributeMap() { } -static PLDHashOperator -CopyAttribute(const uint32_t& aAttributeName, - Attribute* aAttribute, - void* aAttributes) -{ - typedef nsClassHashtable Map; - Map* map = static_cast(aAttributes); - map->Put(aAttributeName, new Attribute(*aAttribute)); - return PL_DHASH_NEXT; -} - AttributeMap::AttributeMap(const AttributeMap& aOther) { - aOther.mMap.EnumerateRead(CopyAttribute, &mMap); + for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) { + const uint32_t& attributeName = iter.Key(); + Attribute* attribute = iter.UserData(); + mMap.Put(attributeName, new Attribute(*attribute)); + } } AttributeMap& @@ -2066,34 +2059,15 @@ AttributeMap::operator=(const AttributeMap& aOther) { if (this != &aOther) { mMap.Clear(); - aOther.mMap.EnumerateRead(CopyAttribute, &mMap); + for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) { + const uint32_t& attributeName = iter.Key(); + Attribute* attribute = iter.UserData(); + mMap.Put(attributeName, new Attribute(*attribute)); + } } return *this; } -namespace { - struct MatchingMap { - typedef nsClassHashtable Map; - const Map& map; - bool matches; - }; -} // namespace - -static PLDHashOperator -CheckAttributeEquality(const uint32_t& aAttributeName, - Attribute* aAttribute, - void* aMatchingMap) -{ - MatchingMap& matchingMap = *static_cast(aMatchingMap); - Attribute* matchingAttribute = matchingMap.map.Get(aAttributeName); - if (!matchingAttribute || - *matchingAttribute != *aAttribute) { - matchingMap.matches = false; - return PL_DHASH_STOP; - } - return PL_DHASH_NEXT; -} - bool AttributeMap::operator==(const AttributeMap& aOther) const { @@ -2101,37 +2075,16 @@ AttributeMap::operator==(const AttributeMap& aOther) const return false; } - MatchingMap matchingMap = { mMap, true }; - aOther.mMap.EnumerateRead(CheckAttributeEquality, &matchingMap); - return matchingMap.matches; -} + for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) { + const uint32_t& attributeName = iter.Key(); + Attribute* attribute = iter.UserData(); + Attribute* matchingAttribute = mMap.Get(attributeName); + if (!matchingAttribute || *matchingAttribute != *attribute) { + return false; + } + } -namespace { - struct HandlerWithUserData - { - AttributeMap::AttributeHandleCallback handler; - void* userData; - }; -} // namespace - -static PLDHashOperator -PassAttributeToHandleCallback(const uint32_t& aAttributeName, - Attribute* aAttribute, - void* aHandlerWithUserData) -{ - HandlerWithUserData* handlerWithUserData = - static_cast(aHandlerWithUserData); - return handlerWithUserData->handler(AttributeName(aAttributeName), - aAttribute->Type(), - handlerWithUserData->userData) ? - PL_DHASH_NEXT : PL_DHASH_STOP; -} - -void -AttributeMap::EnumerateRead(AttributeMap::AttributeHandleCallback aCallback, void* aUserData) const -{ - HandlerWithUserData handlerWithUserData = { aCallback, aUserData }; - mMap.EnumerateRead(PassAttributeToHandleCallback, &handlerWithUserData); + return true; } uint32_t @@ -2140,6 +2093,18 @@ AttributeMap::Count() const return mMap.Count(); } +nsClassHashtable::Iterator +AttributeMap::ConstIter() const +{ + return mMap.ConstIter(); +} + +/* static */ AttributeType +AttributeMap::GetType(FilterAttribute* aAttribute) +{ + return aAttribute->Type(); +} + #define MAKE_ATTRIBUTE_HANDLERS_BASIC(type, typeLabel, defaultValue) \ type \ AttributeMap::Get##typeLabel(AttributeName aName) const { \ diff --git a/gfx/src/FilterSupport.h b/gfx/src/FilterSupport.h index baaf898c57d4..f601e3987993 100644 --- a/gfx/src/FilterSupport.h +++ b/gfx/src/FilterSupport.h @@ -221,10 +221,12 @@ public: AttributeMap GetAttributeMap(AttributeName aName) const; const nsTArray& GetFloats(AttributeName aName) const; - typedef bool (*AttributeHandleCallback)(AttributeName aName, AttributeType aType, void* aUserData); - void EnumerateRead(AttributeHandleCallback aCallback, void* aUserData) const; uint32_t Count() const; + nsClassHashtable::Iterator ConstIter() const; + + static AttributeType GetType(FilterAttribute* aAttribute); + private: mutable nsClassHashtable mMap; }; diff --git a/gfx/src/nsColor.h b/gfx/src/nsColor.h index b03263da21ab..7570def100c1 100644 --- a/gfx/src/nsColor.h +++ b/gfx/src/nsColor.h @@ -27,10 +27,6 @@ typedef uint32_t nscolor; #define NS_RGBA(_r,_g,_b,_a) \ ((nscolor) (((_a) << 24) | ((_b)<<16) | ((_g)<<8) | (_r))) -// Make a color out of a gfxRGBA. -#define NS_RGBA_FROM_GFXRGBA(gfxColor) \ - ((nscolor) (gfxColor.Packed())) - // Extract color components from nscolor #define NS_GET_R(_rgba) ((uint8_t) ((_rgba) & 0xff)) #define NS_GET_G(_rgba) ((uint8_t) (((_rgba) >> 8) & 0xff)) diff --git a/gfx/thebes/gfx2DGlue.h b/gfx/thebes/gfx2DGlue.h index 8b1c32a4c18c..bb5788b088bb 100644 --- a/gfx/thebes/gfx2DGlue.h +++ b/gfx/thebes/gfx2DGlue.h @@ -13,7 +13,6 @@ #include "mozilla/gfx/Matrix.h" #include "mozilla/gfx/Rect.h" #include "mozilla/gfx/2D.h" -#include "gfxColor.h" namespace mozilla { namespace gfx { @@ -34,17 +33,6 @@ inline Rect ToRect(const IntRect &aRect) return Rect(aRect.x, aRect.y, aRect.width, aRect.height); } -inline Color ToColor(const gfxRGBA &aRGBA) -{ - return Color(Float(aRGBA.r), Float(aRGBA.g), - Float(aRGBA.b), Float(aRGBA.a)); -} - -inline gfxRGBA ThebesColor(const Color &aColor) -{ - return gfxRGBA(aColor.r, aColor.g, aColor.b, aColor.a); -} - inline Matrix ToMatrix(const gfxMatrix &aMatrix) { return Matrix(Float(aMatrix._11), Float(aMatrix._12), Float(aMatrix._21), @@ -138,11 +126,6 @@ inline gfxRect ThebesRect(const RectDouble &aRect) return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height); } -inline gfxRGBA ThebesRGBA(const Color &aColor) -{ - return gfxRGBA(aColor.r, aColor.g, aColor.b, aColor.a); -} - inline gfxImageFormat SurfaceFormatToImageFormat(SurfaceFormat aFormat) { switch (aFormat) { diff --git a/gfx/thebes/gfxBlur.h b/gfx/thebes/gfxBlur.h index 792d01042e34..7f69288fe12c 100644 --- a/gfx/thebes/gfxBlur.h +++ b/gfx/thebes/gfxBlur.h @@ -15,7 +15,6 @@ class gfxContext; struct gfxRect; -struct gfxRGBA; namespace mozilla { namespace gfx { diff --git a/gfx/thebes/gfxColor.h b/gfx/thebes/gfxColor.h index cc99b2945ea8..2cc0350f419f 100644 --- a/gfx/thebes/gfxColor.h +++ b/gfx/thebes/gfxColor.h @@ -6,29 +6,9 @@ #ifndef GFX_COLOR_H #define GFX_COLOR_H -#include "gfxTypes.h" - #include "mozilla/Attributes.h" // for MOZ_ALWAYS_INLINE #include "mozilla/Endian.h" // for mozilla::NativeEndian::swapToBigEndian -#define GFX_UINT32_FROM_BPTR(pbptr,i) (((uint32_t*)(pbptr))[i]) - -/** - * GFX_0XFF_PPIXEL_FROM_BPTR(x) - * - * Avoid tortured construction of 32-bit ARGB pixel from 3 individual bytes - * of memory plus constant 0xFF. RGB bytes are already contiguous! - * Equivalent to: GFX_PACKED_PIXEL(0xff,r,g,b) - * - * Attempt to use fast byte-swapping instruction(s), e.g. bswap on x86, in - * preference to a sequence of shift/or operations. - */ -#define GFX_0XFF_PPIXEL_FROM_UINT32(x) \ - ( (mozilla::NativeEndian::swapToBigEndian(uint32_t(x)) >> 8) | (0xFFU << 24) ) - -#define GFX_0XFF_PPIXEL_FROM_BPTR(x) \ - ( GFX_0XFF_PPIXEL_FROM_UINT32(GFX_UINT32_FROM_BPTR((x),0)) ) - /** * GFX_BLOCK_RGB_TO_FRGB(from,to) * sizeof(*from) == sizeof(char) @@ -39,9 +19,9 @@ */ #define GFX_BLOCK_RGB_TO_FRGB(from,to) \ PR_BEGIN_MACRO \ - uint32_t m0 = GFX_UINT32_FROM_BPTR(from,0), \ - m1 = GFX_UINT32_FROM_BPTR(from,1), \ - m2 = GFX_UINT32_FROM_BPTR(from,2), \ + uint32_t m0 = ((uint32_t*)from)[0], \ + m1 = ((uint32_t*)from)[1], \ + m2 = ((uint32_t*)from)[2], \ rgbr = mozilla::NativeEndian::swapToBigEndian(m0), \ gbrg = mozilla::NativeEndian::swapToBigEndian(m1), \ brgb = mozilla::NativeEndian::swapToBigEndian(m2), \ @@ -100,128 +80,4 @@ gfxPackedPixel(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { } } -/** - * A color value, storing red, green, blue and alpha components. - * This class does not use premultiplied alpha. - * - * XXX should this use doubles (instead of gfxFloat), for consistency with - * cairo? - */ -struct gfxRGBA { - gfxFloat r, g, b, a; - - enum PackedColorType { - PACKED_ABGR, - PACKED_ABGR_PREMULTIPLIED, - - PACKED_ARGB, - PACKED_ARGB_PREMULTIPLIED, - - PACKED_XRGB - }; - - gfxRGBA() { } - /** - * Intialize this color using explicit red, green, blue and alpha - * values. - */ - MOZ_CONSTEXPR gfxRGBA(gfxFloat _r, gfxFloat _g, gfxFloat _b, gfxFloat _a=1.0) : r(_r), g(_g), b(_b), a(_a) {} - - /** - * Initialize this color from a packed 32-bit color. - * The color value is interpreted based on colorType; - * all values use the native platform endianness. - * - * Resulting gfxRGBA stores non-premultiplied data. - * - * @see gfxRGBA::Packed - */ - MOZ_IMPLICIT gfxRGBA(uint32_t c, PackedColorType colorType = PACKED_ABGR) { - if (colorType == PACKED_ABGR || - colorType == PACKED_ABGR_PREMULTIPLIED) - { - r = ((c >> 0) & 0xff) * (1.0 / 255.0); - g = ((c >> 8) & 0xff) * (1.0 / 255.0); - b = ((c >> 16) & 0xff) * (1.0 / 255.0); - a = ((c >> 24) & 0xff) * (1.0 / 255.0); - } else if (colorType == PACKED_ARGB || - colorType == PACKED_XRGB || - colorType == PACKED_ARGB_PREMULTIPLIED) - { - b = ((c >> 0) & 0xff) * (1.0 / 255.0); - g = ((c >> 8) & 0xff) * (1.0 / 255.0); - r = ((c >> 16) & 0xff) * (1.0 / 255.0); - a = ((c >> 24) & 0xff) * (1.0 / 255.0); - } - - if (colorType == PACKED_ABGR_PREMULTIPLIED || - colorType == PACKED_ARGB_PREMULTIPLIED) - { - if (a > 0.0) { - r /= a; - g /= a; - b /= a; - } - } else if (colorType == PACKED_XRGB) { - a = 1.0; - } - } - - bool operator==(const gfxRGBA& other) const - { - return r == other.r && g == other.g && b == other.b && a == other.a; - } - bool operator!=(const gfxRGBA& other) const - { - return !(*this == other); - } - - /** - * Returns this color value as a packed 32-bit integer. This reconstructs - * the int32_t based on the given colorType, always in the native byte order. - * - * Note: gcc 4.2.3 on at least Ubuntu (x86) does something strange with - * (uint8_t)(c * 255.0) << x, where the result is different than - * double d = c * 255.0; v = ((uint8_t) d) << x. - */ - uint32_t Packed(PackedColorType colorType = PACKED_ABGR) const { - gfxFloat rb = (r * 255.0); - gfxFloat gb = (g * 255.0); - gfxFloat bb = (b * 255.0); - gfxFloat ab = (a * 255.0); - - if (colorType == PACKED_ABGR) { - return (uint8_t(ab) << 24) | - (uint8_t(bb) << 16) | - (uint8_t(gb) << 8) | - (uint8_t(rb) << 0); - } - if (colorType == PACKED_ARGB || colorType == PACKED_XRGB) { - return (uint8_t(ab) << 24) | - (uint8_t(rb) << 16) | - (uint8_t(gb) << 8) | - (uint8_t(bb) << 0); - } - - rb *= a; - gb *= a; - bb *= a; - - if (colorType == PACKED_ABGR_PREMULTIPLIED) { - return (((uint8_t)(ab) << 24) | - ((uint8_t)(bb) << 16) | - ((uint8_t)(gb) << 8) | - ((uint8_t)(rb) << 0)); - } - if (colorType == PACKED_ARGB_PREMULTIPLIED) { - return (((uint8_t)(ab) << 24) | - ((uint8_t)(rb) << 16) | - ((uint8_t)(gb) << 8) | - ((uint8_t)(bb) << 0)); - } - - return 0; - } -}; - #endif /* _GFX_COLOR_H */ diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index 4631b06b8cca..6035c12d71e7 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -14,7 +14,6 @@ #include "gfxContext.h" -#include "gfxColor.h" #include "gfxMatrix.h" #include "gfxUtils.h" #include "gfxASurface.h" diff --git a/gfx/thebes/gfxDrawable.cpp b/gfx/thebes/gfxDrawable.cpp index 18a68bd0e69e..c7ee4af23912 100644 --- a/gfx/thebes/gfxDrawable.cpp +++ b/gfx/thebes/gfxDrawable.cpp @@ -7,7 +7,6 @@ #include "gfxASurface.h" #include "gfxContext.h" #include "gfxPlatform.h" -#include "gfxColor.h" #include "gfx2DGlue.h" #ifdef MOZ_X11 #include "cairo.h" diff --git a/gfx/thebes/gfxFontUtils.cpp b/gfx/thebes/gfxFontUtils.cpp index f6d7af2a3b0e..a0779ea3fe1f 100644 --- a/gfx/thebes/gfxFontUtils.cpp +++ b/gfx/thebes/gfxFontUtils.cpp @@ -7,7 +7,6 @@ #include "mozilla/BinarySearch.h" #include "gfxFontUtils.h" -#include "gfxColor.h" #include "nsServiceManagerUtils.h" diff --git a/gfx/thebes/gfxQtNativeRenderer.h b/gfx/thebes/gfxQtNativeRenderer.h index 35617ab42c56..23c138e11fc0 100644 --- a/gfx/thebes/gfxQtNativeRenderer.h +++ b/gfx/thebes/gfxQtNativeRenderer.h @@ -6,7 +6,6 @@ #ifndef GFXQTNATIVERENDER_H_ #define GFXQTNATIVERENDER_H_ -#include "gfxColor.h" #include "gfxContext.h" #include "gfxXlibSurface.h" #include "mozilla/gfx/Rect.h" diff --git a/gfx/thebes/gfxSVGGlyphs.cpp b/gfx/thebes/gfxSVGGlyphs.cpp index 5d3b2de1b823..1e07b0f2c2b8 100644 --- a/gfx/thebes/gfxSVGGlyphs.cpp +++ b/gfx/thebes/gfxSVGGlyphs.cpp @@ -28,7 +28,6 @@ #include "gfxFont.h" #include "nsSMILAnimationController.h" #include "gfxContext.h" -#include "gfxColor.h" #include "harfbuzz/hb.h" #define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml") diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index b301586ff104..2aea00695ebe 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -1101,9 +1101,7 @@ gfxUtils::ConvertYCbCrToRGB(const PlanarYCbCrData& aData, } } -/* static */ void gfxUtils::ClearThebesSurface(gfxASurface* aSurface, - IntRect* aRect, - const gfxRGBA& aColor) +/* static */ void gfxUtils::ClearThebesSurface(gfxASurface* aSurface) { if (aSurface->CairoStatus()) { return; @@ -1113,14 +1111,9 @@ gfxUtils::ConvertYCbCrToRGB(const PlanarYCbCrData& aData, return; } cairo_t* ctx = cairo_create(surf); - cairo_set_source_rgba(ctx, aColor.r, aColor.g, aColor.b, aColor.a); + cairo_set_source_rgba(ctx, 0.0, 0.0, 0.0, 0.0); cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE); - IntRect bounds; - if (aRect) { - bounds = *aRect; - } else { - bounds = IntRect(nsIntPoint(0, 0), aSurface->GetSize()); - } + IntRect bounds(nsIntPoint(0, 0), aSurface->GetSize()); cairo_rectangle(ctx, bounds.x, bounds.y, bounds.width, bounds.height); cairo_fill(ctx); cairo_destroy(ctx); diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index d8d62b8ade5b..c1bfcc734c0d 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -6,7 +6,6 @@ #ifndef GFX_UTILS_H #define GFX_UTILS_H -#include "gfxColor.h" #include "gfxTypes.h" #include "GraphicsFilter.h" #include "imgIContainer.h" @@ -161,9 +160,7 @@ public: /** * Clears surface to aColor (which defaults to transparent black). */ - static void ClearThebesSurface(gfxASurface* aSurface, - mozilla::gfx::IntRect* aRect = nullptr, - const gfxRGBA& aColor = gfxRGBA(0.0, 0.0, 0.0, 0.0)); + static void ClearThebesSurface(gfxASurface* aSurface); /** * Creates a copy of aSurface, but having the SurfaceFormat aFormat. diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 04b267488830..b6c6c16839c4 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -2444,6 +2444,7 @@ gfxWindowsPlatform::InitializeD2D() // Initialize D2D 1.0. VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled()); if (!mD3D10Device) { + mDWriteFactory = nullptr; mD2DStatus = FeatureStatus::Failed; return; } diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp index 1e3f38abb0d4..ef9b40bd110e 100644 --- a/ipc/glue/BackgroundUtils.cpp +++ b/ipc/glue/BackgroundUtils.cpp @@ -25,6 +25,7 @@ class OptionalLoadInfoArgs; } using mozilla::BasePrincipal; +using mozilla::OriginAttributes; using namespace mozilla::net; namespace ipc { @@ -76,10 +77,13 @@ PrincipalInfoToPrincipal(const PrincipalInfo& aPrincipalInfo, return nullptr; } - if (info.attrs().mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) { + if (info.appId() == nsIScriptSecurityManager::UNKNOWN_APP_ID) { rv = secMan->GetSimpleCodebasePrincipal(uri, getter_AddRefs(principal)); } else { - principal = BasePrincipal::CreateCodebasePrincipal(uri, info.attrs()); + // TODO: Bug 1167100 - User nsIPrincipal.originAttribute in ContentPrincipalInfo + OriginAttributes attrs(info.appId(), info.isInBrowserElement()); + attrs.mSignedPkg = NS_ConvertUTF8toUTF16(info.signedPkg()); + principal = BasePrincipal::CreateCodebasePrincipal(uri, attrs); rv = principal ? NS_OK : NS_ERROR_FAILURE; } if (NS_WARN_IF(NS_FAILED(rv))) { @@ -199,14 +203,33 @@ PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal, return rv; } + const mozilla::OriginAttributes& attr = + mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(); + nsCString signedPkg = NS_ConvertUTF16toUTF8(attr.mSignedPkg); + bool isUnknownAppId; rv = aPrincipal->GetUnknownAppId(&isUnknownAppId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - *aPrincipalInfo = ContentPrincipalInfo(BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(), - spec); + uint32_t appId; + if (isUnknownAppId) { + appId = nsIScriptSecurityManager::UNKNOWN_APP_ID; + } else { + rv = aPrincipal->GetAppId(&appId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool isInBrowserElement; + rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + *aPrincipalInfo = ContentPrincipalInfo(appId, isInBrowserElement, spec, signedPkg); return NS_OK; } diff --git a/ipc/glue/PBackgroundSharedTypes.ipdlh b/ipc/glue/PBackgroundSharedTypes.ipdlh index f6c9c36e5323..546c3cd8f6b7 100644 --- a/ipc/glue/PBackgroundSharedTypes.ipdlh +++ b/ipc/glue/PBackgroundSharedTypes.ipdlh @@ -2,7 +2,6 @@ * 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/. */ -using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h"; using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; namespace mozilla { @@ -10,8 +9,10 @@ namespace ipc { struct ContentPrincipalInfo { - OriginAttributes attrs; + uint32_t appId; + bool isInBrowserElement; nsCString spec; + nsCString signedPkg; }; struct SystemPrincipalInfo diff --git a/js/public/HashTable.h b/js/public/HashTable.h index 1a5e814ff826..16693bbed38d 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -653,8 +653,6 @@ class HashMapEntry template friend class detail::HashTableEntry; template friend class HashMap; - Key & mutableKey() { return key_; } - public: template HashMapEntry(KeyInput&& k, ValueInput&& v) @@ -670,9 +668,10 @@ class HashMapEntry typedef Key KeyType; typedef Value ValueType; - const Key & key() const { return key_; } - const Value & value() const { return value_; } - Value & value() { return value_; } + const Key& key() const { return key_; } + Key& mutableKey() { return key_; } + const Value& value() const { return value_; } + Value& value() { return value_; } private: HashMapEntry(const HashMapEntry&) = delete; @@ -741,6 +740,7 @@ class HashTableEntry } T& get() { MOZ_ASSERT(isLive()); return *mem.addr(); } + NonConstT& getMutable() { MOZ_ASSERT(isLive()); return *mem.addr(); } bool isFree() const { return keyHash == sFreeKey; } void clearLive() { MOZ_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); } @@ -980,6 +980,16 @@ class HashTable : private AllocPolicy #endif } + NonConstT& mutableFront() { + MOZ_ASSERT(!this->empty()); +#ifdef JS_DEBUG + MOZ_ASSERT(this->validEntry); + MOZ_ASSERT(this->generation == this->Range::table_->generation()); + MOZ_ASSERT(this->mutationCount == this->Range::table_->mutationCount); +#endif + return this->cur->getMutable(); + } + // Removes the |front()| element and re-inserts it into the table with // a new key at the new Lookup position. |front()| is invalid after // this operation until the next call to |popFront()|. diff --git a/js/public/HeapAPI.h b/js/public/HeapAPI.h index 2987b77356f3..016d9f1953b3 100644 --- a/js/public/HeapAPI.h +++ b/js/public/HeapAPI.h @@ -54,7 +54,8 @@ const size_t ChunkMarkBitmapOffset = 1032352; const size_t ChunkMarkBitmapBits = 129024; #endif const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*); -const size_t ChunkLocationOffset = ChunkSize - 2 * sizeof(void*) - sizeof(uint64_t); +const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t); +const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize; const size_t ArenaZoneOffset = 0; /* diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index f2bd4f7f61a5..309691cd74ac 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -584,7 +584,8 @@ struct ZoneStats macro(Other, GCHeapUsed, objectGroupsGCHeap) \ macro(Other, MallocHeap, objectGroupsMallocHeap) \ macro(Other, MallocHeap, typePool) \ - macro(Other, MallocHeap, baselineStubsOptimized) + macro(Other, MallocHeap, baselineStubsOptimized) \ + macro(Other, MallocHeap, uniqueIdMap) ZoneStats() : FOR_EACH_SIZE(ZERO_SIZE) diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index 92987a90e53f..55ffa08cbe20 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -8182,7 +8182,11 @@ CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, static bool Warn(AsmJSParser& parser, int errorNumber, const char* str) { - parser.reportNoOffset(ParseWarning, /* strict = */ false, errorNumber, str ? str : ""); + ParseReportKind reportKind = parser.options().throwOnAsmJSValidationFailureOption && + errorNumber == JSMSG_USE_ASM_TYPE_FAIL + ? ParseError + : ParseWarning; + parser.reportNoOffset(reportKind, /* strict = */ false, errorNumber, str ? str : ""); return false; } diff --git a/js/src/doc/Debugger/Debugger-API.md b/js/src/doc/Debugger/Debugger-API.md index 3703c5d45f5d..10f6ebf895fb 100644 --- a/js/src/doc/Debugger/Debugger-API.md +++ b/js/src/doc/Debugger/Debugger-API.md @@ -37,6 +37,10 @@ it ought not to introduce security holes, so in principle it could be made available to content as well; but it is hard to justify the security risks of the additional attack surface. +The `Debugger` API cannot currently observe self-hosted JavaScript. This is not +inherent in the API's design, but simply that the self-hosting infrastructure +isn't prepared for the kind of invasions the `Debugger` API can perform. + ## Debugger Instances and Shadow Objects diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 764af05b3f0f..8b24da477305 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -772,7 +772,10 @@ TokenStream::reportAsmJSError(uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args); + unsigned flags = options().throwOnAsmJSValidationFailureOption + ? JSREPORT_ERROR + : JSREPORT_WARNING; + reportCompileErrorNumberVA(offset, flags, errorNumber, args); va_end(args); } diff --git a/js/src/gc/Barrier.cpp b/js/src/gc/Barrier.cpp index c361d97f7dae..4014f4a8e6fc 100644 --- a/js/src/gc/Barrier.cpp +++ b/js/src/gc/Barrier.cpp @@ -10,7 +10,9 @@ #include "jsobj.h" #include "gc/Zone.h" +#include "js/HashTable.h" #include "js/Value.h" +#include "vm/ScopeObject.h" #include "vm/Symbol.h" #ifdef DEBUG @@ -102,3 +104,56 @@ JS::HeapValuePostBarrier(JS::Value* valuep, const Value& prev, const Value& next MOZ_ASSERT(valuep); js::InternalGCMethods::postBarrier(valuep, prev, next); } + +template +/* static */ js::HashNumber +js::MovableCellHasher::hash(const Lookup& l) +{ + if (!l) + return 0; + + // We have to access the zone from-any-thread here: a worker thread may be + // cloning a self-hosted object from the main-thread-runtime-owned self- + // hosting zone into the off-main-thread runtime. The zone's uid lock will + // protect against multiple workers doing this simultaneously. + MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || + l->zoneFromAnyThread()->isSelfHostingZone()); + + js::HashNumber hn; + if (!l->zoneFromAnyThread()->getHashCode(l, &hn)) + js::CrashAtUnhandlableOOM("failed to get a stable hash code"); + return hn; +} + +template +/* static */ bool +js::MovableCellHasher::match(const Key& k, const Lookup& l) +{ + // Return true if both are null or false if only one is null. + if (!k) + return !l; + if (!l) + return false; + + MOZ_ASSERT(k); + MOZ_ASSERT(l); + MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || + l->zoneFromAnyThread()->isSelfHostingZone()); + + Zone* zone = k->zoneFromAnyThread(); + if (zone != l->zoneFromAnyThread()) + return false; + MOZ_ASSERT(zone->hasUniqueId(k)); + MOZ_ASSERT(zone->hasUniqueId(l)); + + // Since both already have a uid (from hash), the get is infallible. + uint64_t uidK, uidL; + MOZ_ALWAYS_TRUE(zone->getUniqueId(k, &uidK)); + MOZ_ALWAYS_TRUE(zone->getUniqueId(l, &uidL)); + return uidK == uidL; +} + +template struct js::MovableCellHasher; +template struct js::MovableCellHasher; +template struct js::MovableCellHasher; +template struct js::MovableCellHasher; diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index ec8c00debaff..f80a918e4465 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -693,6 +693,31 @@ class ImmutableTenuredPtr const T* address() { return &value; } }; +// Provide hash codes for Cell kinds that may be relocated and, thus, not have +// a stable address to use as the base for a hash code. Instead of the address, +// this hasher uses Cell::getUniqueId to provide exact matches and as a base +// for generating hash codes. +// +// Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr +// would not likely be a useful key, there are some cases where being able to +// hash a nullptr is useful, either on purpose or because of bugs: +// (1) existence checks where the key may happen to be null and (2) some +// aggregate Lookup kinds embed a JSObject* that is frequently null and do not +// null test before dispatching to the hasher. +template +struct MovableCellHasher +{ + static_assert(mozilla::IsBaseOf::Type>::value, + "MovableCellHasher's T must be a Cell type that may move"); + + using Key = T; + using Lookup = T; + + static HashNumber hash(const Lookup& l); + static bool match(const Key& k, const Lookup& l); + static void rekey(Key& k, const Key& newKey) { k = newKey; } +}; + /* Useful for hashtables with a HeapPtr as key. */ template struct HeapPtrHasher diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index e9521ce09e3c..588f9c02575a 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -655,6 +655,11 @@ class GCRuntime size_t maxMallocBytesAllocated() { return maxMallocBytes; } + uint64_t nextCellUniqueId() { + MOZ_ASSERT(nextCellUniqueId_ > 0); + return nextCellUniqueId_++; + } + public: // Internal public interface js::gc::State state() const { return incrementalState; } @@ -1013,6 +1018,9 @@ class GCRuntime size_t maxMallocBytes; + // An incrementing id used to assign unique ids to cells that require one. + uint64_t nextCellUniqueId_; + /* * Number of the committed arenas in all GC chunks including empty chunks. */ diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h index c0f027149215..a478ae8ee091 100644 --- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -294,6 +294,9 @@ class TenuredCell : public Cell #endif }; +/* Cells are aligned to CellShift, so the largest tagged null pointer is: */ +const uintptr_t LargestTaggedNullCellPointer = (1 << CellShift) - 1; + /* * The mark bitmap has one bit per each GC cell. For multi-cell GC things this * wastes space but allows to avoid expensive devisions by thing's size when @@ -806,6 +809,17 @@ ArenaHeader::getThingSize() const */ struct ChunkTrailer { + /* Construct a Nursery ChunkTrailer. */ + ChunkTrailer(JSRuntime* rt, StoreBuffer* sb) + : location(gc::ChunkLocationBitNursery), storeBuffer(sb), runtime(rt) + {} + + /* Construct a Tenured heap ChunkTrailer. */ + explicit ChunkTrailer(JSRuntime* rt) + : location(gc::ChunkLocationBitTenuredHeap), storeBuffer(nullptr), runtime(rt) + {} + + public: /* The index the chunk in the nursery, or LocationTenuredHeap. */ uint32_t location; uint32_t padding; @@ -813,11 +827,12 @@ struct ChunkTrailer /* The store buffer for writes to things in this chunk or nullptr. */ StoreBuffer* storeBuffer; + /* This provides quick access to the runtime from absolutely anywhere. */ JSRuntime* runtime; }; -static_assert(sizeof(ChunkTrailer) == 2 * sizeof(uintptr_t) + sizeof(uint64_t), - "ChunkTrailer size is incorrect."); +static_assert(sizeof(ChunkTrailer) == ChunkTrailerSize, + "ChunkTrailer size must match the API defined size."); /* The chunk header (located at the end of the chunk to preserve arena alignment). */ struct ChunkInfo @@ -1006,13 +1021,16 @@ struct Chunk return reinterpret_cast(addr); } - static bool withinArenasRange(uintptr_t addr) { + static bool withinValidRange(uintptr_t addr) { uintptr_t offset = addr & ChunkMask; - return offset < ArenasPerChunk * ArenaSize; + return Chunk::fromAddress(addr)->isNurseryChunk() + ? offset < ChunkSize - sizeof(ChunkTrailer) + : offset < ArenasPerChunk * ArenaSize; } static size_t arenaIndex(uintptr_t addr) { - MOZ_ASSERT(withinArenasRange(addr)); + MOZ_ASSERT(!Chunk::fromAddress(addr)->isNurseryChunk()); + MOZ_ASSERT(withinValidRange(addr)); return (addr & ChunkMask) >> ArenaShift; } @@ -1030,6 +1048,10 @@ struct Chunk return info.numArenasFree != 0; } + bool isNurseryChunk() const { + return info.trailer.storeBuffer; + } + ArenaHeader* allocateArena(JSRuntime* rt, JS::Zone* zone, AllocKind kind, const AutoLockGC& lock); @@ -1129,7 +1151,7 @@ ArenaHeader::address() const uintptr_t addr = reinterpret_cast(this); MOZ_ASSERT(addr); MOZ_ASSERT(!(addr & ArenaMask)); - MOZ_ASSERT(Chunk::withinArenasRange(addr)); + MOZ_ASSERT(Chunk::withinValidRange(addr)); return addr; } @@ -1298,7 +1320,7 @@ Cell::address() const { uintptr_t addr = uintptr_t(this); MOZ_ASSERT(addr % CellSize == 0); - MOZ_ASSERT(Chunk::withinArenasRange(addr)); + MOZ_ASSERT(Chunk::withinValidRange(addr)); return addr; } diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index f11c621d04cd..83d8eee794e5 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -2117,7 +2117,13 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind if (src->is()) tenuredSize = srcSize = sizeof(NativeObject); + // Copy the Cell contents. js_memcpy(dst, src, srcSize); + + // Move any hash code attached to the object. + src->zone()->transferUniqueId(dst, src); + + // Move the slots and elements, if we need to. if (src->isNative()) { NativeObject* ndst = &dst->as(); NativeObject* nsrc = &src->as(); diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index dcc042234d24..96ffe0f49adf 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -425,7 +425,7 @@ ToMarkable(Cell* cell) MOZ_ALWAYS_INLINE bool IsNullTaggedPointer(void* p) { - return uintptr_t(p) < 32; + return uintptr_t(p) <= LargestTaggedNullCellPointer; } // HashKeyRef represents a reference to a HashMap key. This should normally diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 2c5b266fd9c9..a5bcb6b9696d 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -67,6 +67,9 @@ js::Nursery::init(uint32_t maxNurseryBytes) if (!mallocedBuffers.init()) return false; + if (!cellsWithUid_.init()) + return false; + void* heap = MapAlignedPages(nurserySize(), Alignment); if (!heap) return false; @@ -653,6 +656,16 @@ js::Nursery::waitBackgroundFreeEnd() void js::Nursery::sweep() { + /* Sweep unique id's in all in-use chunks. */ + for (CellsWithUniqueIdSet::Enum e(cellsWithUid_); !e.empty(); e.popFront()) { + JSObject* obj = static_cast(e.front()); + if (!IsForwarded(obj)) + obj->zone()->removeUniqueId(obj); + else + MOZ_ASSERT(Forwarded(obj)->zone()->hasUniqueId(Forwarded(obj))); + } + cellsWithUid_.clear(); + #ifdef JS_GC_ZEAL /* Poison the nursery contents so touching a freed object will crash. */ JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, nurserySize()); @@ -670,10 +683,8 @@ js::Nursery::sweep() { #ifdef JS_CRASH_DIAGNOSTICS JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, allocationEnd() - start()); - for (int i = 0; i < numActiveChunks_; ++i) { - chunk(i).trailer.location = gc::ChunkLocationBitNursery; - chunk(i).trailer.runtime = runtime(); - } + for (int i = 0; i < numActiveChunks_; ++i) + initChunk(i); #endif setCurrentChunk(0); } diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 267578f25c1a..a13468e06023 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -183,6 +183,14 @@ class Nursery void waitBackgroundFreeEnd(); + bool addedUniqueIdToCell(gc::Cell* cell) { + if (!IsInsideNursery(cell) || !isEnabled()) + return true; + MOZ_ASSERT(cellsWithUid_.initialized()); + MOZ_ASSERT(!cellsWithUid_.has(cell)); + return cellsWithUid_.put(cell); + } + size_t sizeOfHeapCommitted() const { return numActiveChunks_ * gc::ChunkSize; } @@ -266,6 +274,21 @@ class Nursery typedef HashMap, SystemAllocPolicy> ForwardedBufferMap; ForwardedBufferMap forwardedBuffers; + /* + * When we assign a unique id to cell in the nursery, that almost always + * means that the cell will be in a hash table, and thus, held live, + * automatically moving the uid from the nursery to its new home in + * tenured. It is possible, if rare, for an object that acquired a uid to + * be dead before the next collection, in which case we need to know to + * remove it when we sweep. + * + * Note: we store the pointers as Cell* here, resulting in an ugly cast in + * sweep. This is because this structure is used to help implement + * stable object hashing and we have to break the cycle somehow. + */ + using CellsWithUniqueIdSet = HashSet, SystemAllocPolicy>; + CellsWithUniqueIdSet cellsWithUid_; + /* The maximum number of bytes allowed to reside in nursery buffers. */ static const size_t MaxNurseryBufferSize = 1024; @@ -287,10 +310,8 @@ class Nursery } MOZ_ALWAYS_INLINE void initChunk(int chunkno) { - NurseryChunkLayout& c = chunk(chunkno); - c.trailer.storeBuffer = JS::shadow::Runtime::asShadowRuntime(runtime())->gcStoreBufferPtr(); - c.trailer.location = gc::ChunkLocationBitNursery; - c.trailer.runtime = runtime(); + gc::StoreBuffer* sb = JS::shadow::Runtime::asShadowRuntime(runtime())->gcStoreBufferPtr(); + new (&chunk(chunkno).trailer) gc::ChunkTrailer(runtime(), sb); } MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) { diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index b9ee0efd5fab..5821ed7ce7f2 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -66,7 +66,7 @@ Zone::~Zone() bool Zone::init(bool isSystemArg) { isSystem = isSystemArg; - return gcZoneGroupEdges.init(); + return uniqueIds_.init() && gcZoneGroupEdges.init(); } void @@ -156,7 +156,6 @@ Zone::logPromotionsToTenured() awaitingTenureLogging.clear(); } - void Zone::sweepBreakpoints(FreeOp* fop) { @@ -257,6 +256,15 @@ Zone::discardJitCode(FreeOp* fop) } } +#ifdef JSGC_HASH_TABLE_CHECKS +void +JS::Zone::checkUniqueIdTableAfterMovingGC() +{ + for (UniqueIdMap::Enum e(uniqueIds_); !e.empty(); e.popFront()) + js::gc::CheckGCThingAfterMovingGC(e.front().key()); +} +#endif + uint64_t Zone::gcNumber() { diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 74c3a977fec0..11c902926b2f 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -13,9 +13,11 @@ #include "jscntxt.h" +#include "ds/SplayTree.h" #include "gc/FindSCCs.h" #include "gc/GCRuntime.h" #include "js/TracingAPI.h" +#include "vm/MallocProvider.h" #include "vm/TypeInference.h" namespace js { @@ -58,6 +60,11 @@ class ZoneHeapThreshold const GCSchedulingTunables& tunables); }; +// Maps a Cell* to a unique, 64bit id. +using UniqueIdMap = HashMap, SystemAllocPolicy>; + +extern uint64_t NextCellUniqueId(JSRuntime* rt); + } // namespace gc } // namespace js @@ -118,7 +125,8 @@ struct Zone : public JS::shadow::Zone, void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t* typePool, - size_t* baselineStubsOptimized); + size_t* baselineStubsOptimized, + size_t* uniqueIdMap); void resetGCMallocBytes(); void setGCMaxMallocBytes(size_t value); @@ -242,6 +250,7 @@ struct Zone : public JS::shadow::Zone, LogTenurePromotionQueue awaitingTenureLogging; void sweepBreakpoints(js::FreeOp* fop); + void sweepUniqueIds(js::FreeOp* fop); void sweepWeakMaps(); void sweepCompartments(js::FreeOp* fop, bool keepAtleastOne, bool lastGC); @@ -251,6 +260,9 @@ struct Zone : public JS::shadow::Zone, return isOnList(); } + // Side map for storing a unique ids for cells, independent of address. + js::gc::UniqueIdMap uniqueIds_; + public: bool hasDebuggers() const { return debuggers && debuggers->length(); } DebuggerVector* getDebuggers() const { return debuggers; } @@ -323,6 +335,73 @@ struct Zone : public JS::shadow::Zone, mozilla::DebugOnly gcLastZoneGroupIndex; + // Creates a HashNumber based on getUniqueId. Returns false on OOM. + bool getHashCode(js::gc::Cell* cell, js::HashNumber* hashp) { + uint64_t uid; + if (!getUniqueId(cell, &uid)) + return false; + *hashp = js::HashNumber(uid >> 32) ^ js::HashNumber(uid & 0xFFFFFFFF); + return true; + } + + // Puts an existing UID in |uidp|, or creates a new UID for this Cell and + // puts that into |uidp|. Returns false on OOM. + bool getUniqueId(js::gc::Cell* cell, uint64_t* uidp) { + MOZ_ASSERT(uidp); + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); + + // Get an existing uid, if one has been set. + auto p = uniqueIds_.lookupForAdd(cell); + if (p) { + *uidp = p->value(); + return true; + } + + // Set a new uid on the cell. + *uidp = js::gc::NextCellUniqueId(runtimeFromAnyThread()); + if (!uniqueIds_.add(p, cell, *uidp)) + return false; + + // If the cell was in the nursery, hopefully unlikely, then we need to + // tell the nursery about it so that it can sweep the uid if the thing + // does not get tenured. + if (!runtimeFromAnyThread()->gc.nursery.addedUniqueIdToCell(cell)) + js::CrashAtUnhandlableOOM("failed to allocate tracking data for a nursery uid"); + return true; + } + + // Return true if this cell has a UID associated with it. + bool hasUniqueId(js::gc::Cell* cell) { + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); + return uniqueIds_.has(cell); + } + + // Transfer an id from another cell. This must only be called on behalf of a + // moving GC. This method is infallible. + void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) { + MOZ_ASSERT(src != tgt); + MOZ_ASSERT(!IsInsideNursery(tgt)); + MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromMainThread())); + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); + uniqueIds_.rekeyIfMoved(src, tgt); + } + + // Remove any unique id associated with this Cell. + void removeUniqueId(js::gc::Cell* cell) { + MOZ_ASSERT(js::CurrentThreadCanAccessZone(this)); + uniqueIds_.remove(cell); + } + + // Off-thread parsing should not result in any UIDs being created. + void assertNoUniqueIdsInZone() const { + MOZ_ASSERT(uniqueIds_.count() == 0); + } + +#ifdef JSGC_HASH_TABLE_CHECKS + // Assert that the UniqueId table has been redirected successfully. + void checkUniqueIdTableAfterMovingGC(); +#endif + private: js::jit::JitZone* jitZone_; diff --git a/js/src/jit-test/lib/asm.js b/js/src/jit-test/lib/asm.js index a505dc392702..4724912556ea 100644 --- a/js/src/jit-test/lib/asm.js +++ b/js/src/jit-test/lib/asm.js @@ -77,9 +77,9 @@ function assertAsmTypeFail() // Verify no error is thrown with warnings off Function.apply(null, arguments); - // Turn on warnings-as-errors - var oldOpts = options("werror"); - assertEq(oldOpts.indexOf("werror"), -1); + // Turn on throwing on validation errors + var oldOpts = options("throw_on_asmjs_validation_failure"); + assertEq(oldOpts.indexOf("throw_on_asmjs_validation_failure"), -1); // Verify an error is thrown var caught = false; @@ -94,7 +94,7 @@ function assertAsmTypeFail() throw new Error("Didn't catch the type failure error"); // Turn warnings-as-errors back off - options("werror"); + options("throw_on_asmjs_validation_failure"); } function assertAsmLinkFail(f) diff --git a/js/src/jit-test/tests/debug/Frame-newTargetOverflow-01.js b/js/src/jit-test/tests/debug/Frame-newTargetOverflow-01.js index 2a413404c177..1dd0b1bd4ac0 100644 --- a/js/src/jit-test/tests/debug/Frame-newTargetOverflow-01.js +++ b/js/src/jit-test/tests/debug/Frame-newTargetOverflow-01.js @@ -7,6 +7,10 @@ load(libdir + "jitopts.js"); if (!jitTogglesMatch(Opts_Ion2NoOffthreadCompilation)) quit(); +// GCs can invalidate JIT code and cause this test to fail. +if ('gczeal' in this) + gczeal(0); + withJitOptions(Opts_Ion2NoOffthreadCompilation, function () { var g = newGlobal(); var dbg = new Debugger; diff --git a/js/src/jit-test/tests/debug/bug-1204863.js b/js/src/jit-test/tests/debug/bug-1204863.js new file mode 100644 index 000000000000..aa7d59c66cd7 --- /dev/null +++ b/js/src/jit-test/tests/debug/bug-1204863.js @@ -0,0 +1,5 @@ +var dbg = newGlobal().Debugger(this); +dbg.onExceptionUnwind = function (frame, exc) { + return { return:"sproon" }; +}; +Intl.Collator.supportedLocalesOf([2]); diff --git a/js/src/jit-test/tests/gc/bug-1208994.js b/js/src/jit-test/tests/gc/bug-1208994.js new file mode 100644 index 000000000000..151ec909aee8 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1208994.js @@ -0,0 +1,4 @@ +// |jit-test| --no-threads + +load(libdir + 'oomTest.js'); +oomTest(() => getBacktrace({args: oomTest[load+1], locals: true, thisprops: true})); diff --git a/js/src/jit/arm64/vixl/Debugger-vixl.cpp b/js/src/jit/arm64/vixl/Debugger-vixl.cpp index 96844127c2bf..27a20a4d7baf 100644 --- a/js/src/jit/arm64/vixl/Debugger-vixl.cpp +++ b/js/src/jit/arm64/vixl/Debugger-vixl.cpp @@ -30,6 +30,10 @@ #include "jit/arm64/vixl/Debugger-vixl.h" +#include "mozilla/Vector.h" + +#include "jsalloc.h" + namespace vixl { // List of commands supported by the debugger. @@ -63,6 +67,8 @@ class Token { static Token* Tokenize(const char* arg); }; +typedef mozilla::Vector TokenVector; + // Tokens often hold one value. template class ValueToken : public Token { public: @@ -124,10 +130,10 @@ class IdentifierToken : public ValueToken { public: explicit IdentifierToken(const char* name) { int size = strlen(name) + 1; - value_ = new char[size]; + value_ = (char*)js_malloc(size); strncpy(value_, name, size); } - virtual ~IdentifierToken() { delete[] value_; } + virtual ~IdentifierToken() { js_free(value_); } virtual bool IsIdentifier() const { return true; } virtual bool CanAddressMemory() const { return strcmp(value(), "pc") == 0; } @@ -227,10 +233,10 @@ class UnknownToken : public Token { public: explicit UnknownToken(const char* arg) { int size = strlen(arg) + 1; - unknown_ = new char[size]; + unknown_ = (char*)js_malloc(size); strncpy(unknown_, arg, size); } - virtual ~UnknownToken() { delete[] unknown_; } + virtual ~UnknownToken() { js_free(unknown_); } virtual bool IsUnknown() const { return true; } virtual void Print(FILE* out = stdout) const; @@ -246,7 +252,7 @@ class DebugCommand { public: explicit DebugCommand(Token* name) : name_(IdentifierToken::Cast(name)) {} DebugCommand() : name_(NULL) {} - virtual ~DebugCommand() { delete name_; } + virtual ~DebugCommand() { js_delete(name_); } const char* name() { return name_->value(); } // Run the command on the given debugger. The command returns true if @@ -272,7 +278,7 @@ class HelpCommand : public DebugCommand { virtual bool Run(Debugger* debugger); - static DebugCommand* Build(std::vector args); + static DebugCommand* Build(TokenVector &&args); static const char* kHelp; static const char* kAliases[]; @@ -286,7 +292,7 @@ class ContinueCommand : public DebugCommand { virtual bool Run(Debugger* debugger); - static DebugCommand* Build(std::vector args); + static DebugCommand* Build(TokenVector &&args); static const char* kHelp; static const char* kAliases[]; @@ -298,13 +304,13 @@ class StepCommand : public DebugCommand { public: StepCommand(Token* name, IntegerToken* count) : DebugCommand(name), count_(count) {} - virtual ~StepCommand() { delete count_; } + virtual ~StepCommand() { js_delete(count_); } int64_t count() { return count_->value(); } virtual bool Run(Debugger* debugger); virtual void Print(FILE* out = stdout); - static DebugCommand* Build(std::vector args); + static DebugCommand* Build(TokenVector &&args); static const char* kHelp; static const char* kAliases[]; @@ -316,7 +322,7 @@ class StepCommand : public DebugCommand { class DisasmCommand : public DebugCommand { public: - static DebugCommand* Build(std::vector args); + static DebugCommand* Build(TokenVector &&args); static const char* kHelp; static const char* kAliases[]; @@ -329,8 +335,8 @@ class PrintCommand : public DebugCommand { PrintCommand(Token* name, Token* target, FormatToken* format) : DebugCommand(name), target_(target), format_(format) {} virtual ~PrintCommand() { - delete target_; - delete format_; + js_delete(target_); + js_delete(format_); } Token* target() { return target_; } @@ -338,7 +344,7 @@ class PrintCommand : public DebugCommand { virtual bool Run(Debugger* debugger); virtual void Print(FILE* out = stdout); - static DebugCommand* Build(std::vector args); + static DebugCommand* Build(TokenVector &&args); static const char* kHelp; static const char* kAliases[]; @@ -357,9 +363,9 @@ class ExamineCommand : public DebugCommand { IntegerToken* count) : DebugCommand(name), target_(target), format_(format), count_(count) {} virtual ~ExamineCommand() { - delete target_; - delete format_; - delete count_; + js_delete(target_); + js_delete(format_); + js_delete(count_); } Token* target() { return target_; } @@ -368,7 +374,7 @@ class ExamineCommand : public DebugCommand { virtual bool Run(Debugger* debugger); virtual void Print(FILE* out = stdout); - static DebugCommand* Build(std::vector args); + static DebugCommand* Build(TokenVector &&args); static const char* kHelp; static const char* kAliases[]; @@ -383,26 +389,26 @@ class ExamineCommand : public DebugCommand { // Commands which name does not match any of the known commnand. class UnknownCommand : public DebugCommand { public: - explicit UnknownCommand(std::vector args) : args_(args) {} + explicit UnknownCommand(TokenVector &&args) : args_(Move(args)) {} virtual ~UnknownCommand(); virtual bool Run(Debugger* debugger); private: - std::vector args_; + TokenVector args_; }; // Commands which name match a known command but the syntax is invalid. class InvalidCommand : public DebugCommand { public: - InvalidCommand(std::vector args, int index, const char* cause) - : args_(args), index_(index), cause_(cause) {} + InvalidCommand(TokenVector &&args, int index, const char* cause) + : args_(Move(args)), index_(index), cause_(cause) {} virtual ~InvalidCommand(); virtual bool Run(Debugger* debugger); private: - std::vector args_; + TokenVector args_; int index_; const char* cause_; }; @@ -530,8 +536,8 @@ Debugger::Debugger(Decoder* decoder, FILE* stream) pending_request_(false), steps_(0), last_command_(NULL) { - disasm_ = new PrintDisassembler(stdout); - printer_ = new Decoder(); + disasm_ = js_new(stdout); + printer_ = js_new(); printer_->AppendVisitor(disasm_); } @@ -823,7 +829,7 @@ Token* Token::Tokenize(const char* arg) { return token; } - return new UnknownToken(arg); + return js_new(arg); } @@ -856,14 +862,14 @@ Token* RegisterToken::Tokenize(const char* arg) { // Is it a X register or alias? for (const char** current = kXAliases[i]; *current != NULL; current++) { if (strcmp(arg, *current) == 0) { - return new RegisterToken(Register::XRegFromCode(i)); + return js_new(Register::XRegFromCode(i)); } } // Is it a W register or alias? for (const char** current = kWAliases[i]; *current != NULL; current++) { if (strcmp(arg, *current) == 0) { - return new RegisterToken(Register::WRegFromCode(i)); + return js_new(Register::WRegFromCode(i)); } } } @@ -904,7 +910,7 @@ Token* FPRegisterToken::Tokenize(const char* arg) { default: VIXL_UNREACHABLE(); } - return new FPRegisterToken(fpreg); + return js_new(fpreg); } return NULL; @@ -935,7 +941,7 @@ Token* IdentifierToken::Tokenize(const char* arg) { } if (*cursor == '\0') { - return new IdentifierToken(arg); + return js_new(arg); } return NULL; @@ -964,7 +970,7 @@ Token* AddressToken::Tokenize(const char* arg) { } uint8_t* address = reinterpret_cast(ptr); - return new AddressToken(address); + return js_new(address); } @@ -979,7 +985,7 @@ Token* IntegerToken::Tokenize(const char* arg) { return NULL; } - return new IntegerToken(value); + return js_new(value); } @@ -993,7 +999,7 @@ Token* FormatToken::Tokenize(const char* arg) { if (length == 1) return NULL; break; case 'i': - if (length == 1) return new Format("%08" PRIx32, 'i'); + if (length == 1) return js_new>("%08" PRIx32, 'i'); default: return NULL; } @@ -1019,32 +1025,32 @@ Token* FormatToken::Tokenize(const char* arg) { switch (arg[0]) { case 'x': switch (count) { - case 8: return new Format("%02" PRIx8, 'x'); - case 16: return new Format("%04" PRIx16, 'x'); - case 32: return new Format("%08" PRIx32, 'x'); - case 64: return new Format("%016" PRIx64, 'x'); + case 8: return js_new>("%02" PRIx8, 'x'); + case 16: return js_new>("%04" PRIx16, 'x'); + case 32: return js_new>("%08" PRIx32, 'x'); + case 64: return js_new>("%016" PRIx64, 'x'); default: return NULL; } case 's': switch (count) { - case 8: return new Format("%4" PRId8, 's'); - case 16: return new Format("%6" PRId16, 's'); - case 32: return new Format("%11" PRId32, 's'); - case 64: return new Format("%20" PRId64, 's'); + case 8: return js_new>("%4" PRId8, 's'); + case 16: return js_new>("%6" PRId16, 's'); + case 32: return js_new>("%11" PRId32, 's'); + case 64: return js_new>("%20" PRId64, 's'); default: return NULL; } case 'u': switch (count) { - case 8: return new Format("%3" PRIu8, 'u'); - case 16: return new Format("%5" PRIu16, 'u'); - case 32: return new Format("%10" PRIu32, 'u'); - case 64: return new Format("%20" PRIu64, 'u'); + case 8: return js_new>("%3" PRIu8, 'u'); + case 16: return js_new>("%5" PRIu16, 'u'); + case 32: return js_new>("%10" PRIu32, 'u'); + case 64: return js_new>("%20" PRIu64, 'u'); default: return NULL; } case 'f': switch (count) { - case 32: return new Format("%13g", 'f'); - case 64: return new Format("%13g", 'f'); + case 32: return js_new>("%13g", 'f'); + case 64: return js_new>("%13g", 'f'); default: return NULL; } default: @@ -1083,7 +1089,7 @@ bool DebugCommand::Match(const char* name, const char** aliases) { DebugCommand* DebugCommand::Parse(char* line) { - std::vector args; + TokenVector args; for (char* chunk = strtok(line, " \t"); chunk != NULL; @@ -1094,35 +1100,35 @@ DebugCommand* DebugCommand::Parse(char* line) { Token* format = FormatToken::Tokenize(dot + 1); if (format != NULL) { *dot = '\0'; - args.push_back(Token::Tokenize(chunk)); - args.push_back(format); + args.append(Token::Tokenize(chunk)); + args.append(format); } else { // Error while parsing the format, push the UnknownToken so an error // can be accurately reported. - args.push_back(Token::Tokenize(chunk)); + args.append(Token::Tokenize(chunk)); } } else { - args.push_back(Token::Tokenize(chunk)); + args.append(Token::Tokenize(chunk)); } } - if (args.size() == 0) { + if (args.empty()) { return NULL; } if (!args[0]->IsIdentifier()) { - return new InvalidCommand(args, 0, "command name is not valid"); + return js_new(Move(args), 0, "command name is not valid"); } const char* name = IdentifierToken::Cast(args[0])->value(); #define RETURN_IF_MATCH(Command) \ if (Match(name, Command::kAliases)) { \ - return Command::Build(args); \ + return Command::Build(Move(args)); \ } DEBUG_COMMAND_LIST(RETURN_IF_MATCH); #undef RETURN_IF_MATCH - return new UnknownCommand(args); + return js_new(Move(args)); } @@ -1160,12 +1166,12 @@ bool HelpCommand::Run(Debugger* debugger) { } -DebugCommand* HelpCommand::Build(std::vector args) { - if (args.size() != 1) { - return new InvalidCommand(args, -1, "too many arguments"); +DebugCommand* HelpCommand::Build(TokenVector &&args) { + if (args.length() != 1) { + return js_new(Move(args), -1, "too many arguments"); } - return new HelpCommand(args[0]); + return js_new(args[0]); } @@ -1177,12 +1183,12 @@ bool ContinueCommand::Run(Debugger* debugger) { } -DebugCommand* ContinueCommand::Build(std::vector args) { - if (args.size() != 1) { - return new InvalidCommand(args, -1, "too many arguments"); +DebugCommand* ContinueCommand::Build(TokenVector &&args) { + if (args.length() != 1) { + return js_new(Move(args), -1, "too many arguments"); } - return new ContinueCommand(args[0]); + return js_new(args[0]); } @@ -1205,52 +1211,52 @@ void StepCommand::Print(FILE* out) { } -DebugCommand* StepCommand::Build(std::vector args) { +DebugCommand* StepCommand::Build(TokenVector &&args) { IntegerToken* count = NULL; - switch (args.size()) { + switch (args.length()) { case 1: { // step [1] - count = new IntegerToken(1); + count = js_new(1); break; } case 2: { // step n Token* first = args[1]; if (!first->IsInteger()) { - return new InvalidCommand(args, 1, "expects int"); + return js_new(Move(args), 1, "expects int"); } count = IntegerToken::Cast(first); break; } default: - return new InvalidCommand(args, -1, "too many arguments"); + return js_new(Move(args), -1, "too many arguments"); } - return new StepCommand(args[0], count); + return js_new(args[0], count); } -DebugCommand* DisasmCommand::Build(std::vector args) { +DebugCommand* DisasmCommand::Build(TokenVector &&args) { IntegerToken* count = NULL; - switch (args.size()) { + switch (args.length()) { case 1: { // disasm [10] - count = new IntegerToken(10); + count = js_new(10); break; } case 2: { // disasm n Token* first = args[1]; if (!first->IsInteger()) { - return new InvalidCommand(args, 1, "expects int"); + return js_new(Move(args), 1, "expects int"); } count = IntegerToken::Cast(first); break; } default: - return new InvalidCommand(args, -1, "too many arguments"); + return js_new(Move(args), -1, "too many arguments"); } - Token* target = new IdentifierToken("pc"); - FormatToken* format = new Format("%08" PRIx32, 'i'); - return new ExamineCommand(args[0], target, format, count); + Token* target = js_new("pc"); + FormatToken* format = js_new>("%08" PRIx32, 'i'); + return js_new(args[0], target, format, count); } @@ -1308,16 +1314,16 @@ bool PrintCommand::Run(Debugger* debugger) { } -DebugCommand* PrintCommand::Build(std::vector args) { - if (args.size() < 2) { - return new InvalidCommand(args, -1, "too few arguments"); +DebugCommand* PrintCommand::Build(TokenVector &&args) { + if (args.length() < 2) { + return js_new(Move(args), -1, "too few arguments"); } Token* target = args[1]; if (!target->IsRegister() && !target->IsFPRegister() && !target->IsIdentifier()) { - return new InvalidCommand(args, 1, "expects reg or identifier"); + return js_new(Move(args), 1, "expects reg or identifier"); } FormatToken* format = NULL; @@ -1332,18 +1338,18 @@ DebugCommand* PrintCommand::Build(std::vector args) { // If the target is an identifier there must be no format. This is checked // in the switch statement below. - switch (args.size()) { + switch (args.length()) { case 2: { if (target->IsRegister()) { switch (target_size) { - case 4: format = new Format("%08" PRIx32, 'x'); break; - case 8: format = new Format("%016" PRIx64, 'x'); break; + case 4: format = js_new>("%08" PRIx32, 'x'); break; + case 8: format = js_new>("%016" PRIx64, 'x'); break; default: VIXL_UNREACHABLE(); } } else if (target->IsFPRegister()) { switch (target_size) { - case 4: format = new Format("%8g", 'f'); break; - case 8: format = new Format("%8g", 'f'); break; + case 4: format = js_new>("%8g", 'f'); break; + case 8: format = js_new>("%8g", 'f'); break; default: VIXL_UNREACHABLE(); } } @@ -1351,27 +1357,27 @@ DebugCommand* PrintCommand::Build(std::vector args) { } case 3: { if (target->IsIdentifier()) { - return new InvalidCommand(args, 2, + return js_new(Move(args), 2, "format is only allowed with registers"); } Token* second = args[2]; if (!second->IsFormat()) { - return new InvalidCommand(args, 2, "expects format"); + return js_new(Move(args), 2, "expects format"); } format = FormatToken::Cast(second); if (format->SizeOf() > target_size) { - return new InvalidCommand(args, 2, "format too wide"); + return js_new(Move(args), 2, "format too wide"); } break; } default: - return new InvalidCommand(args, -1, "too many arguments"); + return js_new(Move(args), -1, "too many arguments"); } - return new PrintCommand(args[0], target, format); + return js_new(args[0], target, format); } @@ -1397,23 +1403,23 @@ void ExamineCommand::Print(FILE* out) { } -DebugCommand* ExamineCommand::Build(std::vector args) { - if (args.size() < 2) { - return new InvalidCommand(args, -1, "too few arguments"); +DebugCommand* ExamineCommand::Build(TokenVector &&args) { + if (args.length() < 2) { + return js_new(Move(args), -1, "too few arguments"); } Token* target = args[1]; if (!target->CanAddressMemory()) { - return new InvalidCommand(args, 1, "expects address"); + return js_new(Move(args), 1, "expects address"); } FormatToken* format = NULL; IntegerToken* count = NULL; - switch (args.size()) { + switch (args.length()) { case 2: { // mem addr[.x64] [10] - format = new Format("%016" PRIx64, 'x'); - count = new IntegerToken(10); + format = js_new>("%016" PRIx64, 'x'); + count = js_new(10); break; } case 3: { // mem addr.format [10] @@ -1421,13 +1427,13 @@ DebugCommand* ExamineCommand::Build(std::vector args) { Token* second = args[2]; if (second->IsFormat()) { format = FormatToken::Cast(second); - count = new IntegerToken(10); + count = js_new(10); break; } else if (second->IsInteger()) { - format = new Format("%016" PRIx64, 'x'); + format = js_new>("%016" PRIx64, 'x'); count = IntegerToken::Cast(second); } else { - return new InvalidCommand(args, 2, "expects format or integer"); + return js_new(Move(args), 2, "expects format or integer"); } VIXL_UNREACHABLE(); break; @@ -1436,24 +1442,23 @@ DebugCommand* ExamineCommand::Build(std::vector args) { Token* second = args[2]; Token* third = args[3]; if (!second->IsFormat() || !third->IsInteger()) { - return new InvalidCommand(args, -1, "expects addr[.format] [n]"); + return js_new(Move(args), -1, "expects addr[.format] [n]"); } format = FormatToken::Cast(second); count = IntegerToken::Cast(third); break; } default: - return new InvalidCommand(args, -1, "too many arguments"); + return js_new(Move(args), -1, "too many arguments"); } - return new ExamineCommand(args[0], target, format, count); + return js_new(args[0], target, format, count); } UnknownCommand::~UnknownCommand() { - const int size = args_.size(); - for (int i = 0; i < size; ++i) { - delete args_[i]; + for (auto arg : args_) { + js_delete(arg); } } @@ -1463,10 +1468,9 @@ bool UnknownCommand::Run(Debugger* debugger) { USE(debugger); printf(" ** Unknown Command:"); - const int size = args_.size(); - for (int i = 0; i < size; ++i) { + for (auto arg : args_) { printf(" "); - args_[i]->Print(stdout); + arg->Print(stdout); } printf(" **\n"); @@ -1475,9 +1479,8 @@ bool UnknownCommand::Run(Debugger* debugger) { InvalidCommand::~InvalidCommand() { - const int size = args_.size(); - for (int i = 0; i < size; ++i) { - delete args_[i]; + for (auto arg : args_) { + js_delete(arg); } } @@ -1487,7 +1490,7 @@ bool InvalidCommand::Run(Debugger* debugger) { USE(debugger); printf(" ** Invalid Command:"); - const int size = args_.size(); + const int size = args_.length(); for (int i = 0; i < size; ++i) { printf(" "); if (i == index_) { diff --git a/js/src/jit/arm64/vixl/Debugger-vixl.h b/js/src/jit/arm64/vixl/Debugger-vixl.h index 2866383df2e1..e140f1808cc5 100644 --- a/js/src/jit/arm64/vixl/Debugger-vixl.h +++ b/js/src/jit/arm64/vixl/Debugger-vixl.h @@ -30,7 +30,6 @@ #include #include #include -#include #include "jit/arm64/vixl/Constants-vixl.h" #include "jit/arm64/vixl/Globals-vixl.h" diff --git a/js/src/jit/arm64/vixl/Decoder-vixl.cpp b/js/src/jit/arm64/vixl/Decoder-vixl.cpp index f60c29d7c5ea..52f05f9b26af 100644 --- a/js/src/jit/arm64/vixl/Decoder-vixl.cpp +++ b/js/src/jit/arm64/vixl/Decoder-vixl.cpp @@ -26,6 +26,8 @@ #include "jit/arm64/vixl/Decoder-vixl.h" +#include + #include "jit/arm64/vixl/Globals-vixl.h" #include "jit/arm64/vixl/Utils-vixl.h" @@ -110,50 +112,45 @@ void Decoder::DecodeInstruction(const Instruction *instr) { } void Decoder::AppendVisitor(DecoderVisitor* new_visitor) { - visitors_.push_back(new_visitor); + visitors_.append(new_visitor); } void Decoder::PrependVisitor(DecoderVisitor* new_visitor) { - visitors_.push_front(new_visitor); + visitors_.insert(visitors_.begin(), new_visitor); } void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor, DecoderVisitor* registered_visitor) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { + for (auto it = visitors_.begin(); it != visitors_.end(); it++) { if (*it == registered_visitor) { visitors_.insert(it, new_visitor); return; } } - // We reached the end of the list. The last element must be - // registered_visitor. - VIXL_ASSERT(*it == registered_visitor); - visitors_.insert(it, new_visitor); + // We reached the end of the list without finding registered_visitor. + visitors_.append(new_visitor); } void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor, DecoderVisitor* registered_visitor) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { + for (auto it = visitors_.begin(); it != visitors_.end(); it++) { if (*it == registered_visitor) { it++; visitors_.insert(it, new_visitor); return; } } - // We reached the end of the list. The last element must be - // registered_visitor. - VIXL_ASSERT(*it == registered_visitor); - visitors_.push_back(new_visitor); + // We reached the end of the list without finding registered_visitor. + visitors_.append(new_visitor); } void Decoder::RemoveVisitor(DecoderVisitor* visitor) { - visitors_.remove(visitor); + visitors_.erase(std::remove(visitors_.begin(), visitors_.end(), visitor), + visitors_.end()); } @@ -698,9 +695,8 @@ void Decoder::DecodeAdvSIMDDataProcessing(const Instruction* instr) { #define DEFINE_VISITOR_CALLERS(A) \ void Decoder::Visit##A(const Instruction *instr) { \ VIXL_ASSERT(instr->Mask(A##FMask) == A##Fixed); \ - std::list::iterator it; \ - for (it = visitors_.begin(); it != visitors_.end(); it++) { \ - (*it)->Visit##A(instr); \ + for (auto visitor : visitors_) { \ + visitor->Visit##A(instr); \ } \ } VISITOR_LIST(DEFINE_VISITOR_CALLERS) diff --git a/js/src/jit/arm64/vixl/Decoder-vixl.h b/js/src/jit/arm64/vixl/Decoder-vixl.h index a095e16f9792..f3228b5cf5d5 100644 --- a/js/src/jit/arm64/vixl/Decoder-vixl.h +++ b/js/src/jit/arm64/vixl/Decoder-vixl.h @@ -27,7 +27,9 @@ #ifndef VIXL_A64_DECODER_A64_H_ #define VIXL_A64_DECODER_A64_H_ -#include +#include "mozilla/Vector.h" + +#include "jsalloc.h" #include "jit/arm64/vixl/Globals-vixl.h" #include "jit/arm64/vixl/Instructions-vixl.h" @@ -118,9 +120,8 @@ class Decoder { // Top-level wrappers around the actual decoding function. void Decode(const Instruction* instr) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - VIXL_ASSERT((*it)->IsConstVisitor()); + for (auto visitor : visitors_) { + VIXL_ASSERT(visitor->IsConstVisitor()); } DecodeInstruction(instr); } @@ -154,10 +155,6 @@ class Decoder { // d.InsertVisitorBefore(V4, V2); // will yield the order // V1, V3, V4, V2, V1, V2 - // - // For more complex modifications of the order of registered visitors, one can - // directly access and modify the list of visitors via the `visitors()' - // accessor. void InsertVisitorBefore(DecoderVisitor* new_visitor, DecoderVisitor* registered_visitor); void InsertVisitorAfter(DecoderVisitor* new_visitor, @@ -171,9 +168,6 @@ class Decoder { VISITOR_LIST(DECLARE) #undef DECLARE - - std::list* visitors() { return &visitors_; } - private: // Decodes an instruction and calls the visitor functions registered with the // Decoder class. @@ -231,7 +225,7 @@ class Decoder { private: // Visitors are registered in a list. - std::list visitors_; + mozilla::Vector visitors_; }; } // namespace vixl diff --git a/js/src/jit/arm64/vixl/Instrument-vixl.cpp b/js/src/jit/arm64/vixl/Instrument-vixl.cpp index c45e95a9209c..bc8535a0d909 100644 --- a/js/src/jit/arm64/vixl/Instrument-vixl.cpp +++ b/js/src/jit/arm64/vixl/Instrument-vixl.cpp @@ -136,8 +136,8 @@ Instrument::Instrument(const char* datafile, uint64_t sample_period) // Construct Counter objects from counter description array. for (int i = 0; i < num_counters; i++) { - Counter* counter = new Counter(kCounterList[i].name, kCounterList[i].type); - counters_.push_back(counter); + Counter* counter = js_new(kCounterList[i].name, kCounterList[i].type); + counters_.append(counter); } DumpCounterNames(); @@ -149,9 +149,8 @@ Instrument::~Instrument() { DumpCounters(); // Free all the counter objects. - std::list::iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - delete *it; + for (auto counter : counters_) { + js_delete(counter); } if (output_stream_ != stdout) { @@ -176,9 +175,8 @@ void Instrument::Update() { void Instrument::DumpCounters() { // Iterate through the counter objects, dumping their values to the output // stream. - std::list::const_iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - fprintf(output_stream_, "%" PRIu64 ",", (*it)->count()); + for (auto counter : counters_) { + fprintf(output_stream_, "%" PRIu64 ",", counter->count()); } fprintf(output_stream_, "\n"); fflush(output_stream_); @@ -188,9 +186,8 @@ void Instrument::DumpCounters() { void Instrument::DumpCounterNames() { // Iterate through the counter objects, dumping the counter names to the // output stream. - std::list::const_iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - fprintf(output_stream_, "%s,", (*it)->name()); + for (auto counter : counters_) { + fprintf(output_stream_, "%s,", counter->name()); } fprintf(output_stream_, "\n"); fflush(output_stream_); @@ -218,10 +215,9 @@ void Instrument::DumpEventMarker(unsigned marker) { Counter* Instrument::GetCounter(const char* name) { // Get a Counter object by name from the counter list. - std::list::const_iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - if (strcmp((*it)->name(), name) == 0) { - return *it; + for (auto counter : counters_) { + if (strcmp(counter->name(), name) == 0) { + return counter; } } @@ -236,17 +232,15 @@ Counter* Instrument::GetCounter(const char* name) { void Instrument::Enable() { - std::list::iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - (*it)->Enable(); + for (auto counter : counters_) { + counter->Enable(); } } void Instrument::Disable() { - std::list::iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - (*it)->Disable(); + for (auto counter : counters_) { + counter->Disable(); } } diff --git a/js/src/jit/arm64/vixl/Instrument-vixl.h b/js/src/jit/arm64/vixl/Instrument-vixl.h index c09fdbabb2c1..43cd37ad74ce 100644 --- a/js/src/jit/arm64/vixl/Instrument-vixl.h +++ b/js/src/jit/arm64/vixl/Instrument-vixl.h @@ -27,6 +27,10 @@ #ifndef VIXL_A64_INSTRUMENT_A64_H_ #define VIXL_A64_INSTRUMENT_A64_H_ +#include "mozilla/Vector.h" + +#include "jsalloc.h" + #include "jit/arm64/vixl/Constants-vixl.h" #include "jit/arm64/vixl/Decoder-vixl.h" #include "jit/arm64/vixl/Globals-vixl.h" @@ -95,7 +99,7 @@ class Instrument: public DecoderVisitor { void InstrumentLoadStore(const Instruction* instr); void InstrumentLoadStorePair(const Instruction* instr); - std::list counters_; + mozilla::Vector counters_; FILE *output_stream_; uint64_t sample_period_; diff --git a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp index c78b5660450b..a9169e41d9ea 100644 --- a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp +++ b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp @@ -91,14 +91,14 @@ void Simulator::init(Decoder* decoder, FILE* stream) { decoder_->AppendVisitor(this); stream_ = stream; - print_disasm_ = new PrintDisassembler(stream_); + print_disasm_ = js_new(stream_); set_coloured_trace(false); trace_parameters_ = LOG_NONE; ResetState(); // Allocate and set up the simulator stack. - stack_ = new byte[stack_size_]; + stack_ = (byte*)js_malloc(stack_size_); stack_limit_ = stack_ + stack_protection_size_; // Configure the starting stack pointer. // - Find the top of the stack. @@ -110,7 +110,7 @@ void Simulator::init(Decoder* decoder, FILE* stream) { set_sp(tos); // Set the sample period to 10, as the VIXL examples and tests are short. - instrumentation_ = new Instrument("vixl_stats.csv", 10); + instrumentation_ = js_new("vixl_stats.csv", 10); // Print a warning about exclusive-access instructions, but only the first // time they are encountered. This warning can be silenced using @@ -120,7 +120,9 @@ void Simulator::init(Decoder* decoder, FILE* stream) { lock_ = PR_NewLock(); if (!lock_) MOZ_CRASH("Could not allocate simulator lock."); +#ifdef DEBUG lockOwner_ = nullptr; +#endif redirection_ = nullptr; } diff --git a/js/src/jit/arm64/vixl/Simulator-vixl.cpp b/js/src/jit/arm64/vixl/Simulator-vixl.cpp index a01df8b2608c..391ca38ffa38 100644 --- a/js/src/jit/arm64/vixl/Simulator-vixl.cpp +++ b/js/src/jit/arm64/vixl/Simulator-vixl.cpp @@ -68,13 +68,13 @@ SimSystemRegister SimSystemRegister::DefaultValueFor(SystemRegister id) { Simulator::~Simulator() { - delete [] stack_; + js_free(stack_); // The decoder may outlive the simulator. decoder_->RemoveVisitor(print_disasm_); - delete print_disasm_; + js_delete(print_disasm_); decoder_->RemoveVisitor(instrumentation_); - delete instrumentation_; + js_delete(instrumentation_); } @@ -2791,7 +2791,7 @@ void Simulator::DoPrintf(const Instruction* instr) { const char * format_base = reg(0); VIXL_ASSERT(format_base != NULL); size_t length = strlen(format_base) + 1; - char * const format = new char[length + arg_count]; + char * const format = (char*)js_malloc(length + arg_count); // A list of chunks, each with exactly one format placeholder. const char * chunks[kPrintfMaxArgCount]; @@ -2867,7 +2867,7 @@ void Simulator::DoPrintf(const Instruction* instr) { // Set LR as if we'd just called a native printf function. set_lr(pc()); - delete[] format; + js_free(format); } } // namespace vixl diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp new file mode 100644 index 000000000000..8baad73d8a1c --- /dev/null +++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp @@ -0,0 +1,1869 @@ +/* -*- 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/. */ + +#include "jit/mips-shared/CodeGenerator-mips-shared.h" + +#include "mozilla/MathAlgorithms.h" + +#include "jscntxt.h" +#include "jscompartment.h" +#include "jsnum.h" + +#include "jit/CodeGenerator.h" +#include "jit/JitCompartment.h" +#include "jit/JitFrames.h" +#include "jit/MIR.h" +#include "jit/MIRGraph.h" +#include "js/Conversions.h" +#include "vm/Shape.h" +#include "vm/TraceLogging.h" + +#include "jsscriptinlines.h" + +#include "jit/MacroAssembler-inl.h" +#include "jit/shared/CodeGenerator-shared-inl.h" + +using namespace js; +using namespace js::jit; + +using mozilla::FloorLog2; +using mozilla::NegativeInfinity; +using JS::GenericNaN; +using JS::ToInt32; + +// inline +Address +CodeGeneratorMIPSShared::ToAddress(const LAllocation& a) +{ + MOZ_ASSERT(a.isMemory()); + int32_t offset = ToStackOffset(&a); + + return Address(StackPointer, offset); +} + +// inline +Address +CodeGeneratorMIPSShared::ToAddress(const LAllocation* a) +{ + return ToAddress(*a); +} + + +// shared +CodeGeneratorMIPSShared::CodeGeneratorMIPSShared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) + : CodeGeneratorShared(gen, graph, masm) +{ +} + +void +CodeGeneratorMIPSShared::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, + MBasicBlock* mir, Assembler::DoubleCondition cond) +{ + // Skip past trivial blocks. + mir = skipTrivialBlocks(mir); + + Label* label = mir->lir()->label(); + if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) { + // Note: the backedge is initially a jump to the next instruction. + // It will be patched to the target block's label during link(). + RepatchLabel rejoin; + + CodeOffsetJump backedge; + Label skip; + if (fmt == Assembler::DoubleFloat) + masm.ma_bc1d(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); + else + masm.ma_bc1s(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); + + backedge = masm.backedgeJump(&rejoin); + masm.bind(&rejoin); + masm.bind(&skip); + + if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) + MOZ_CRASH(); + } else { + if (fmt == Assembler::DoubleFloat) + masm.branchDouble(cond, lhs, rhs, mir->lir()->label()); + else + masm.branchFloat(cond, lhs, rhs, mir->lir()->label()); + } +} + +void +OutOfLineBailout::accept(CodeGeneratorMIPSShared* codegen) +{ + codegen->visitOutOfLineBailout(this); +} + +void +CodeGeneratorMIPSShared::visitTestIAndBranch(LTestIAndBranch* test) +{ + const LAllocation* opd = test->getOperand(0); + MBasicBlock* ifTrue = test->ifTrue(); + MBasicBlock* ifFalse = test->ifFalse(); + + emitBranch(ToRegister(opd), Imm32(0), Assembler::NonZero, ifTrue, ifFalse); +} + +void +CodeGeneratorMIPSShared::visitCompare(LCompare* comp) +{ + Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop()); + const LAllocation* left = comp->getOperand(0); + const LAllocation* right = comp->getOperand(1); + const LDefinition* def = comp->getDef(0); + + if (right->isConstant()) + masm.cmp32Set(cond, ToRegister(left), Imm32(ToInt32(right)), ToRegister(def)); + else if (right->isGeneralReg()) + masm.cmp32Set(cond, ToRegister(left), ToRegister(right), ToRegister(def)); + else + masm.cmp32Set(cond, ToRegister(left), ToAddress(right), ToRegister(def)); +} + +void +CodeGeneratorMIPSShared::visitCompareAndBranch(LCompareAndBranch* comp) +{ + Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop()); + if (comp->right()->isConstant()) { + emitBranch(ToRegister(comp->left()), Imm32(ToInt32(comp->right())), cond, + comp->ifTrue(), comp->ifFalse()); + } else if (comp->right()->isGeneralReg()) { + emitBranch(ToRegister(comp->left()), ToRegister(comp->right()), cond, + comp->ifTrue(), comp->ifFalse()); + } else { + masm.load32(ToAddress(comp->right()), ScratchRegister); + emitBranch(ToRegister(comp->left()), ScratchRegister, cond, + comp->ifTrue(), comp->ifFalse()); + } +} + +bool +CodeGeneratorMIPSShared::generateOutOfLineCode() +{ + if (!CodeGeneratorShared::generateOutOfLineCode()) + return false; + + if (deoptLabel_.used()) { + // All non-table-based bailouts will go here. + masm.bind(&deoptLabel_); + + // Push the frame size, so the handler can recover the IonScript. + // Frame size is stored in 'ra' and pushed by GenerateBailoutThunk + // We have to use 'ra' because generateBailoutTable will implicitly do + // the same. + masm.move32(Imm32(frameSize()), ra); + + JitCode* handler = gen->jitRuntime()->getGenericBailoutHandler(); + + masm.branch(handler); + } + + return true; +} + +void +CodeGeneratorMIPSShared::bailoutFrom(Label* label, LSnapshot* snapshot) +{ + if (masm.bailed()) + return; + + MOZ_ASSERT(label->used()); + MOZ_ASSERT(!label->bound()); + + encode(snapshot); + + // Though the assembler doesn't track all frame pushes, at least make sure + // the known value makes sense. We can't use bailout tables if the stack + // isn't properly aligned to the static frame size. + MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(), + frameClass_.frameSize() == masm.framePushed()); + + // We don't use table bailouts because retargeting is easier this way. + InlineScriptTree* tree = snapshot->mir()->block()->trackedTree(); + OutOfLineBailout* ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed()); + addOutOfLineCode(ool, new(alloc()) BytecodeSite(tree, tree->script()->code())); + + masm.retarget(label, ool->entry()); +} + +void +CodeGeneratorMIPSShared::bailout(LSnapshot* snapshot) +{ + Label label; + masm.jump(&label); + bailoutFrom(&label, snapshot); +} + +void +CodeGeneratorMIPSShared::visitOutOfLineBailout(OutOfLineBailout* ool) +{ + // Push snapshotOffset and make sure stack is aligned. + masm.subPtr(Imm32(2 * sizeof(void*)), StackPointer); + masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0)); + + masm.jump(&deoptLabel_); +} + +void +CodeGeneratorMIPSShared::visitMinMaxD(LMinMaxD* ins) +{ + FloatRegister first = ToFloatRegister(ins->first()); + FloatRegister second = ToFloatRegister(ins->second()); + FloatRegister output = ToFloatRegister(ins->output()); + + MOZ_ASSERT(first == output); + + Assembler::DoubleCondition cond = ins->mir()->isMax() + ? Assembler::DoubleLessThanOrEqual + : Assembler::DoubleGreaterThanOrEqual; + Label nan, equal, returnSecond, done; + + // First or second is NaN, result is NaN. + masm.ma_bc1d(first, second, &nan, Assembler::DoubleUnordered, ShortJump); + // Make sure we handle -0 and 0 right. + masm.ma_bc1d(first, second, &equal, Assembler::DoubleEqual, ShortJump); + masm.ma_bc1d(first, second, &returnSecond, cond, ShortJump); + masm.ma_b(&done, ShortJump); + + // Check for zero. + masm.bind(&equal); + masm.loadConstantDouble(0.0, ScratchDoubleReg); + // First wasn't 0 or -0, so just return it. + masm.ma_bc1d(first, ScratchDoubleReg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump); + + // So now both operands are either -0 or 0. + if (ins->mir()->isMax()) { + // -0 + -0 = -0 and -0 + 0 = 0. + masm.addDouble(second, first); + } else { + masm.negateDouble(first); + masm.subDouble(second, first); + masm.negateDouble(first); + } + masm.ma_b(&done, ShortJump); + + masm.bind(&nan); + masm.loadConstantDouble(GenericNaN(), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&returnSecond); + masm.moveDouble(second, output); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitMinMaxF(LMinMaxF* ins) +{ + FloatRegister first = ToFloatRegister(ins->first()); + FloatRegister second = ToFloatRegister(ins->second()); + FloatRegister output = ToFloatRegister(ins->output()); + + MOZ_ASSERT(first == output); + + Assembler::DoubleCondition cond = ins->mir()->isMax() + ? Assembler::DoubleLessThanOrEqual + : Assembler::DoubleGreaterThanOrEqual; + Label nan, equal, returnSecond, done; + + // First or second is NaN, result is NaN. + masm.ma_bc1s(first, second, &nan, Assembler::DoubleUnordered, ShortJump); + // Make sure we handle -0 and 0 right. + masm.ma_bc1s(first, second, &equal, Assembler::DoubleEqual, ShortJump); + masm.ma_bc1s(first, second, &returnSecond, cond, ShortJump); + masm.ma_b(&done, ShortJump); + + // Check for zero. + masm.bind(&equal); + masm.loadConstantFloat32(0.0f, ScratchFloat32Reg); + // First wasn't 0 or -0, so just return it. + masm.ma_bc1s(first, ScratchFloat32Reg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump); + + // So now both operands are either -0 or 0. + if (ins->mir()->isMax()) { + // -0 + -0 = -0 and -0 + 0 = 0. + masm.as_adds(first, first, second); + } else { + masm.as_negs(first, first); + masm.as_subs(first, first, second); + masm.as_negs(first, first); + } + masm.ma_b(&done, ShortJump); + + masm.bind(&nan); + masm.loadConstantFloat32(GenericNaN(), output); + masm.ma_b(&done, ShortJump); + masm.bind(&returnSecond); + masm.as_movs(output, second); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitAbsD(LAbsD* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + MOZ_ASSERT(input == ToFloatRegister(ins->output())); + masm.as_absd(input, input); +} + +void +CodeGeneratorMIPSShared::visitAbsF(LAbsF* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + MOZ_ASSERT(input == ToFloatRegister(ins->output())); + masm.as_abss(input, input); +} + +void +CodeGeneratorMIPSShared::visitSqrtD(LSqrtD* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + masm.as_sqrtd(output, input); +} + +void +CodeGeneratorMIPSShared::visitSqrtF(LSqrtF* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + masm.as_sqrts(output, input); +} + +void +CodeGeneratorMIPSShared::visitAddI(LAddI* ins) +{ + const LAllocation* lhs = ins->getOperand(0); + const LAllocation* rhs = ins->getOperand(1); + const LDefinition* dest = ins->getDef(0); + + MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); + + // If there is no snapshot, we don't need to check for overflow + if (!ins->snapshot()) { + if (rhs->isConstant()) + masm.ma_addu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_addu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + return; + } + + Label overflow; + if (rhs->isConstant()) + masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); + else + masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); + + bailoutFrom(&overflow, ins->snapshot()); +} + +void +CodeGeneratorMIPSShared::visitSubI(LSubI* ins) +{ + const LAllocation* lhs = ins->getOperand(0); + const LAllocation* rhs = ins->getOperand(1); + const LDefinition* dest = ins->getDef(0); + + MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); + + // If there is no snapshot, we don't need to check for overflow + if (!ins->snapshot()) { + if (rhs->isConstant()) + masm.ma_subu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_subu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + return; + } + + Label overflow; + if (rhs->isConstant()) + masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); + else + masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); + + bailoutFrom(&overflow, ins->snapshot()); +} + +void +CodeGeneratorMIPSShared::visitMulI(LMulI* ins) +{ + const LAllocation* lhs = ins->lhs(); + const LAllocation* rhs = ins->rhs(); + Register dest = ToRegister(ins->output()); + MMul* mul = ins->mir(); + + MOZ_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow()); + + if (rhs->isConstant()) { + int32_t constant = ToInt32(rhs); + Register src = ToRegister(lhs); + + // Bailout on -0.0 + if (mul->canBeNegativeZero() && constant <= 0) { + Assembler::Condition cond = (constant == 0) ? Assembler::LessThan : Assembler::Equal; + bailoutCmp32(cond, src, Imm32(0), ins->snapshot()); + } + + switch (constant) { + case -1: + if (mul->canOverflow()) + bailoutCmp32(Assembler::Equal, src, Imm32(INT32_MIN), ins->snapshot()); + + masm.ma_negu(dest, src); + break; + case 0: + masm.move32(Imm32(0), dest); + break; + case 1: + masm.move32(src, dest); + break; + case 2: + if (mul->canOverflow()) { + Label mulTwoOverflow; + masm.ma_addTestOverflow(dest, src, src, &mulTwoOverflow); + + bailoutFrom(&mulTwoOverflow, ins->snapshot()); + } else { + masm.as_addu(dest, src, src); + } + break; + default: + uint32_t shift = FloorLog2(constant); + + if (!mul->canOverflow() && (constant > 0)) { + // If it cannot overflow, we can do lots of optimizations. + uint32_t rest = constant - (1 << shift); + + // See if the constant has one bit set, meaning it can be + // encoded as a bitshift. + if ((1 << shift) == constant) { + masm.ma_sll(dest, src, Imm32(shift)); + return; + } + + // If the constant cannot be encoded as (1<canOverflow() && (constant > 0) && (src != dest)) { + // To stay on the safe side, only optimize things that are a + // power of 2. + + if ((1 << shift) == constant) { + // dest = lhs * pow(2, shift) + masm.ma_sll(dest, src, Imm32(shift)); + // At runtime, check (lhs == dest >> shift), if this does + // not hold, some bits were lost due to overflow, and the + // computation should be resumed as a double. + masm.ma_sra(ScratchRegister, dest, Imm32(shift)); + bailoutCmp32(Assembler::NotEqual, src, ScratchRegister, ins->snapshot()); + return; + } + } + + if (mul->canOverflow()) { + Label mulConstOverflow; + masm.ma_mul_branch_overflow(dest, ToRegister(lhs), Imm32(ToInt32(rhs)), + &mulConstOverflow); + + bailoutFrom(&mulConstOverflow, ins->snapshot()); + } else { + masm.ma_mult(src, Imm32(ToInt32(rhs))); + masm.as_mflo(dest); + } + break; + } + } else { + Label multRegOverflow; + + if (mul->canOverflow()) { + masm.ma_mul_branch_overflow(dest, ToRegister(lhs), ToRegister(rhs), &multRegOverflow); + bailoutFrom(&multRegOverflow, ins->snapshot()); + } else { + masm.as_mult(ToRegister(lhs), ToRegister(rhs)); + masm.as_mflo(dest); + } + + if (mul->canBeNegativeZero()) { + Label done; + masm.ma_b(dest, dest, &done, Assembler::NonZero, ShortJump); + + // Result is -0 if lhs or rhs is negative. + // In that case result must be double value so bailout + Register scratch = SecondScratchReg; + masm.as_or(scratch, ToRegister(lhs), ToRegister(rhs)); + bailoutCmp32(Assembler::Signed, scratch, scratch, ins->snapshot()); + + masm.bind(&done); + } + } +} + +void +CodeGeneratorMIPSShared::visitDivI(LDivI* ins) +{ + // Extract the registers from this instruction + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register dest = ToRegister(ins->output()); + Register temp = ToRegister(ins->getTemp(0)); + MDiv* mir = ins->mir(); + + Label done; + + // Handle divide by zero. + if (mir->canBeDivideByZero()) { + if (mir->canTruncateInfinities()) { + // Truncated division by zero is zero (Infinity|0 == 0) + Label notzero; + masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(¬zero); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Zero, rhs, rhs, ins->snapshot()); + } + } + + // Handle an integer overflow exception from -2147483648 / -1. + if (mir->canBeNegativeOverflow()) { + Label notMinInt; + masm.move32(Imm32(INT32_MIN), temp); + masm.ma_b(lhs, temp, ¬MinInt, Assembler::NotEqual, ShortJump); + + masm.move32(Imm32(-1), temp); + if (mir->canTruncateOverflow()) { + // (-INT32_MIN)|0 == INT32_MIN + Label skip; + masm.ma_b(rhs, temp, &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(INT32_MIN), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, rhs, temp, ins->snapshot()); + } + masm.bind(¬MinInt); + } + + // Handle negative 0. (0/-Y) + if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) { + Label nonzero; + masm.ma_b(lhs, lhs, &nonzero, Assembler::NonZero, ShortJump); + bailoutCmp32(Assembler::LessThan, rhs, Imm32(0), ins->snapshot()); + masm.bind(&nonzero); + } + // Note: above safety checks could not be verified as Ion seems to be + // smarter and requires double arithmetic in such cases. + + // All regular. Lets call div. + if (mir->canTruncateRemainder()) { + masm.as_div(lhs, rhs); + masm.as_mflo(dest); + } else { + MOZ_ASSERT(mir->fallible()); + + Label remainderNonZero; + masm.ma_div_branch_overflow(dest, lhs, rhs, &remainderNonZero); + bailoutFrom(&remainderNonZero, ins->snapshot()); + } + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitDivPowTwoI(LDivPowTwoI* ins) +{ + Register lhs = ToRegister(ins->numerator()); + Register dest = ToRegister(ins->output()); + Register tmp = ToRegister(ins->getTemp(0)); + int32_t shift = ins->shift(); + + if (shift != 0) { + MDiv* mir = ins->mir(); + if (!mir->isTruncated()) { + // If the remainder is going to be != 0, bailout since this must + // be a double. + masm.ma_sll(tmp, lhs, Imm32(32 - shift)); + bailoutCmp32(Assembler::NonZero, tmp, tmp, ins->snapshot()); + } + + if (!mir->canBeNegativeDividend()) { + // Numerator is unsigned, so needs no adjusting. Do the shift. + masm.ma_sra(dest, lhs, Imm32(shift)); + return; + } + + // Adjust the value so that shifting produces a correctly rounded result + // when the numerator is negative. See 10-1 "Signed Division by a Known + // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight. + if (shift > 1) { + masm.ma_sra(tmp, lhs, Imm32(31)); + masm.ma_srl(tmp, tmp, Imm32(32 - shift)); + masm.add32(lhs, tmp); + } else { + masm.ma_srl(tmp, lhs, Imm32(32 - shift)); + masm.add32(lhs, tmp); + } + + // Do the shift. + masm.ma_sra(dest, tmp, Imm32(shift)); + } else { + masm.move32(lhs, dest); + } +} + +void +CodeGeneratorMIPSShared::visitModI(LModI* ins) +{ + // Extract the registers from this instruction + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register dest = ToRegister(ins->output()); + Register callTemp = ToRegister(ins->callTemp()); + MMod* mir = ins->mir(); + Label done, prevent; + + masm.move32(lhs, callTemp); + + // Prevent INT_MIN % -1; + // The integer division will give INT_MIN, but we want -(double)INT_MIN. + if (mir->canBeNegativeDividend()) { + masm.ma_b(lhs, Imm32(INT_MIN), &prevent, Assembler::NotEqual, ShortJump); + if (mir->isTruncated()) { + // (INT_MIN % -1)|0 == 0 + Label skip; + masm.ma_b(rhs, Imm32(-1), &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, rhs, Imm32(-1), ins->snapshot()); + } + masm.bind(&prevent); + } + + // 0/X (with X < 0) is bad because both of these values *should* be + // doubles, and the result should be -0.0, which cannot be represented in + // integers. X/0 is bad because it will give garbage (or abort), when it + // should give either \infty, -\infty or NAN. + + // Prevent 0 / X (with X < 0) and X / 0 + // testing X / Y. Compare Y with 0. + // There are three cases: (Y < 0), (Y == 0) and (Y > 0) + // If (Y < 0), then we compare X with 0, and bail if X == 0 + // If (Y == 0), then we simply want to bail. + // if (Y > 0), we don't bail. + + if (mir->canBeDivideByZero()) { + if (mir->isTruncated()) { + Label skip; + masm.ma_b(rhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); + } + } + + if (mir->canBeNegativeDividend()) { + Label notNegative; + masm.ma_b(rhs, Imm32(0), ¬Negative, Assembler::GreaterThan, ShortJump); + if (mir->isTruncated()) { + // NaN|0 == 0 and (0 % -X)|0 == 0 + Label skip; + masm.ma_b(lhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, lhs, Imm32(0), ins->snapshot()); + } + masm.bind(¬Negative); + } + + masm.as_div(lhs, rhs); + masm.as_mfhi(dest); + + // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0 + if (mir->canBeNegativeDividend()) { + if (mir->isTruncated()) { + // -0.0|0 == 0 + } else { + MOZ_ASSERT(mir->fallible()); + // See if X < 0 + masm.ma_b(dest, Imm32(0), &done, Assembler::NotEqual, ShortJump); + bailoutCmp32(Assembler::Signed, callTemp, Imm32(0), ins->snapshot()); + } + } + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitModPowTwoI(LModPowTwoI* ins) +{ + Register in = ToRegister(ins->getOperand(0)); + Register out = ToRegister(ins->getDef(0)); + MMod* mir = ins->mir(); + Label negative, done; + + masm.move32(in, out); + masm.ma_b(in, in, &done, Assembler::Zero, ShortJump); + // Switch based on sign of the lhs. + // Positive numbers are just a bitmask + masm.ma_b(in, in, &negative, Assembler::Signed, ShortJump); + { + masm.and32(Imm32((1 << ins->shift()) - 1), out); + masm.ma_b(&done, ShortJump); + } + + // Negative numbers need a negate, bitmask, negate + { + masm.bind(&negative); + masm.neg32(out); + masm.and32(Imm32((1 << ins->shift()) - 1), out); + masm.neg32(out); + } + if (mir->canBeNegativeDividend()) { + if (!mir->isTruncated()) { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, out, zero, ins->snapshot()); + } else { + // -0|0 == 0 + } + } + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitModMaskI(LModMaskI* ins) +{ + Register src = ToRegister(ins->getOperand(0)); + Register dest = ToRegister(ins->getDef(0)); + Register tmp0 = ToRegister(ins->getTemp(0)); + Register tmp1 = ToRegister(ins->getTemp(1)); + MMod* mir = ins->mir(); + + if (!mir->isTruncated() && mir->canBeNegativeDividend()) { + MOZ_ASSERT(mir->fallible()); + + Label bail; + masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), &bail); + bailoutFrom(&bail, ins->snapshot()); + } else { + masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), nullptr); + } +} + +void +CodeGeneratorMIPSShared::visitBitNotI(LBitNotI* ins) +{ + const LAllocation* input = ins->getOperand(0); + const LDefinition* dest = ins->getDef(0); + MOZ_ASSERT(!input->isConstant()); + + masm.ma_not(ToRegister(dest), ToRegister(input)); +} + +void +CodeGeneratorMIPSShared::visitBitOpI(LBitOpI* ins) +{ + const LAllocation* lhs = ins->getOperand(0); + const LAllocation* rhs = ins->getOperand(1); + const LDefinition* dest = ins->getDef(0); + // all of these bitops should be either imm32's, or integer registers. + switch (ins->bitop()) { + case JSOP_BITOR: + if (rhs->isConstant()) + masm.ma_or(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_or(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + break; + case JSOP_BITXOR: + if (rhs->isConstant()) + masm.ma_xor(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_xor(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + break; + case JSOP_BITAND: + if (rhs->isConstant()) + masm.ma_and(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + break; + default: + MOZ_CRASH("unexpected binary opcode"); + } +} + +void +CodeGeneratorMIPSShared::visitShiftI(LShiftI* ins) +{ + Register lhs = ToRegister(ins->lhs()); + const LAllocation* rhs = ins->rhs(); + Register dest = ToRegister(ins->output()); + + if (rhs->isConstant()) { + int32_t shift = ToInt32(rhs) & 0x1F; + switch (ins->bitop()) { + case JSOP_LSH: + if (shift) + masm.ma_sll(dest, lhs, Imm32(shift)); + else + masm.move32(lhs, dest); + break; + case JSOP_RSH: + if (shift) + masm.ma_sra(dest, lhs, Imm32(shift)); + else + masm.move32(lhs, dest); + break; + case JSOP_URSH: + if (shift) { + masm.ma_srl(dest, lhs, Imm32(shift)); + } else { + // x >>> 0 can overflow. + masm.move32(lhs, dest); + if (ins->mir()->toUrsh()->fallible()) + bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); + } + break; + default: + MOZ_CRASH("Unexpected shift op"); + } + } else { + // The shift amounts should be AND'ed into the 0-31 range + masm.ma_and(dest, ToRegister(rhs), Imm32(0x1F)); + + switch (ins->bitop()) { + case JSOP_LSH: + masm.ma_sll(dest, lhs, dest); + break; + case JSOP_RSH: + masm.ma_sra(dest, lhs, dest); + break; + case JSOP_URSH: + masm.ma_srl(dest, lhs, dest); + if (ins->mir()->toUrsh()->fallible()) { + // x >>> 0 can overflow. + bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); + } + break; + default: + MOZ_CRASH("Unexpected shift op"); + } + } +} + +void +CodeGeneratorMIPSShared::visitUrshD(LUrshD* ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register temp = ToRegister(ins->temp()); + + const LAllocation* rhs = ins->rhs(); + FloatRegister out = ToFloatRegister(ins->output()); + + if (rhs->isConstant()) { + masm.ma_srl(temp, lhs, Imm32(ToInt32(rhs))); + } else { + masm.ma_srl(temp, lhs, ToRegister(rhs)); + } + + masm.convertUInt32ToDouble(temp, out); +} + +void +CodeGeneratorMIPSShared::visitClzI(LClzI* ins) +{ + Register input = ToRegister(ins->input()); + Register output = ToRegister(ins->output()); + + masm.as_clz(output, input); +} + +void +CodeGeneratorMIPSShared::visitPowHalfD(LPowHalfD* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + + Label done, skip; + + // Masm.pow(-Infinity, 0.5) == Infinity. + masm.loadConstantDouble(NegativeInfinity(), ScratchDoubleReg); + masm.ma_bc1d(input, ScratchDoubleReg, &skip, Assembler::DoubleNotEqualOrUnordered, ShortJump); + masm.as_negd(output, ScratchDoubleReg); + masm.ma_b(&done, ShortJump); + + masm.bind(&skip); + // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). + // Adding 0 converts any -0 to 0. + masm.loadConstantDouble(0.0, ScratchDoubleReg); + masm.as_addd(output, input, ScratchDoubleReg); + masm.as_sqrtd(output, output); + + masm.bind(&done); +} + +MoveOperand +CodeGeneratorMIPSShared::toMoveOperand(LAllocation a) const +{ + if (a.isGeneralReg()) + return MoveOperand(ToRegister(a)); + if (a.isFloatReg()) { + return MoveOperand(ToFloatRegister(a)); + } + int32_t offset = ToStackOffset(a); + MOZ_ASSERT((offset & 3) == 0); + + return MoveOperand(StackPointer, offset); +} + +void +CodeGeneratorMIPSShared::visitMathD(LMathD* math) +{ + const LAllocation* src1 = math->getOperand(0); + const LAllocation* src2 = math->getOperand(1); + const LDefinition* output = math->getDef(0); + + switch (math->jsop()) { + case JSOP_ADD: + masm.as_addd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_SUB: + masm.as_subd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_MUL: + masm.as_muld(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_DIV: + masm.as_divd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + default: + MOZ_CRASH("unexpected opcode"); + } +} + +void +CodeGeneratorMIPSShared::visitMathF(LMathF* math) +{ + const LAllocation* src1 = math->getOperand(0); + const LAllocation* src2 = math->getOperand(1); + const LDefinition* output = math->getDef(0); + + switch (math->jsop()) { + case JSOP_ADD: + masm.as_adds(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_SUB: + masm.as_subs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_MUL: + masm.as_muls(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_DIV: + masm.as_divs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + default: + MOZ_CRASH("unexpected opcode"); + } +} + +void +CodeGeneratorMIPSShared::visitFloor(LFloor* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchDoubleReg; + Register output = ToRegister(lir->output()); + + Label skipCheck, done; + + // If Nan, 0 or -0 check for bailout + masm.loadConstantDouble(0.0, scratch); + masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If high part is not zero, it is NaN or -0, so we bail. + masm.moveFromDoubleHi(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&skipCheck); + masm.as_floorwd(scratch, input); + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitFloorF(LFloorF* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchFloat32Reg; + Register output = ToRegister(lir->output()); + + Label skipCheck, done; + + // If Nan, 0 or -0 check for bailout + masm.loadConstantFloat32(0.0f, scratch); + masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If binary value is not zero, it is NaN or -0, so we bail. + masm.moveFromDoubleLo(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&skipCheck); + masm.as_floorws(scratch, input); + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitCeil(LCeil* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchDoubleReg; + Register output = ToRegister(lir->output()); + + Label performCeil, done; + + // If x < -1 or x > 0 then perform ceil. + masm.loadConstantDouble(0, scratch); + masm.branchDouble(Assembler::DoubleGreaterThan, input, scratch, &performCeil); + masm.loadConstantDouble(-1, scratch); + masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, scratch, &performCeil); + + // If high part is not zero, the input was not 0, so we bail. + masm.moveFromDoubleHi(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&performCeil); + masm.as_ceilwd(scratch, input); + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitCeilF(LCeilF* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchFloat32Reg; + Register output = ToRegister(lir->output()); + + Label performCeil, done; + + // If x < -1 or x > 0 then perform ceil. + masm.loadConstantFloat32(0.0f, scratch); + masm.branchFloat(Assembler::DoubleGreaterThan, input, scratch, &performCeil); + masm.loadConstantFloat32(-1.0f, scratch); + masm.branchFloat(Assembler::DoubleLessThanOrEqual, input, scratch, &performCeil); + + // If binary value is not zero, the input was not 0, so we bail. + masm.moveFromFloat32(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&performCeil); + masm.as_ceilws(scratch, input); + masm.moveFromFloat32(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitRound(LRound* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister temp = ToFloatRegister(lir->temp()); + FloatRegister scratch = ScratchDoubleReg; + Register output = ToRegister(lir->output()); + + Label bail, negative, end, skipCheck; + + // Load biggest number less than 0.5 in the temp register. + masm.loadConstantDouble(GetBiggestNumberLessThan(0.5), temp); + + // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. + masm.loadConstantDouble(0.0, scratch); + masm.ma_bc1d(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); + + // If Nan, 0 or -0 check for bailout + masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If high part is not zero, it is NaN or -0, so we bail. + masm.moveFromDoubleHi(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&end, ShortJump); + + masm.bind(&skipCheck); + masm.as_addd(scratch, input, temp); + masm.as_floorwd(scratch, scratch); + + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.jump(&end); + + // Input is negative, but isn't -0. + masm.bind(&negative); + + // Inputs in ]-0.5; 0] need to be added 0.5, other negative inputs need to + // be added the biggest double less than 0.5. + Label loadJoin; + masm.loadConstantDouble(-0.5, scratch); + masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &loadJoin); + masm.loadConstantDouble(0.5, temp); + masm.bind(&loadJoin); + + masm.addDouble(input, temp); + + // If input + 0.5 >= 0, input is a negative number >= -0.5 and the + // result is -0. + masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); + bailoutFrom(&bail, lir->snapshot()); + + // Truncate and round toward zero. + // This is off-by-one for everything but integer-valued inputs. + masm.as_floorwd(scratch, temp); + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + + masm.bind(&end); +} + +void +CodeGeneratorMIPSShared::visitRoundF(LRoundF* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister temp = ToFloatRegister(lir->temp()); + FloatRegister scratch = ScratchFloat32Reg; + Register output = ToRegister(lir->output()); + + Label bail, negative, end, skipCheck; + + // Load biggest number less than 0.5 in the temp register. + masm.loadConstantFloat32(GetBiggestNumberLessThan(0.5f), temp); + + // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. + masm.loadConstantFloat32(0.0f, scratch); + masm.ma_bc1s(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); + + // If Nan, 0 or -0 check for bailout + masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If binary value is not zero, it is NaN or -0, so we bail. + masm.moveFromFloat32(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&end, ShortJump); + + masm.bind(&skipCheck); + masm.as_adds(scratch, input, temp); + masm.as_floorws(scratch, scratch); + + masm.moveFromFloat32(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.jump(&end); + + // Input is negative, but isn't -0. + masm.bind(&negative); + + // Inputs in ]-0.5; 0] need to be added 0.5, other negative inputs need to + // be added the biggest double less than 0.5. + Label loadJoin; + masm.loadConstantFloat32(-0.5f, scratch); + masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &loadJoin); + masm.loadConstantFloat32(0.5f, temp); + masm.bind(&loadJoin); + + masm.as_adds(temp, input, temp); + + // If input + 0.5 >= 0, input is a negative number >= -0.5 and the + // result is -0. + masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); + bailoutFrom(&bail, lir->snapshot()); + + // Truncate and round toward zero. + // This is off-by-one for everything but integer-valued inputs. + masm.as_floorws(scratch, temp); + masm.moveFromFloat32(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + + masm.bind(&end); +} + +void +CodeGeneratorMIPSShared::visitTruncateDToInt32(LTruncateDToInt32* ins) +{ + emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()), + ins->mir()); +} + +void +CodeGeneratorMIPSShared::visitTruncateFToInt32(LTruncateFToInt32* ins) +{ + emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()), + ins->mir()); +} + +void +CodeGeneratorMIPSShared::visitValue(LValue* value) +{ + const ValueOperand out = ToOutValue(value); + + masm.moveValue(value->value(), out); +} + +void +CodeGeneratorMIPSShared::visitDouble(LDouble* ins) +{ + const LDefinition* out = ins->getDef(0); + + masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out)); +} + +void +CodeGeneratorMIPSShared::visitFloat32(LFloat32* ins) +{ + const LDefinition* out = ins->getDef(0); + masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out)); +} + +void +CodeGeneratorMIPSShared::visitTestDAndBranch(LTestDAndBranch* test) +{ + FloatRegister input = ToFloatRegister(test->input()); + + MBasicBlock* ifTrue = test->ifTrue(); + MBasicBlock* ifFalse = test->ifFalse(); + + masm.loadConstantDouble(0.0, ScratchDoubleReg); + // If 0, or NaN, the result is false. + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifTrue, + Assembler::DoubleNotEqual); + } else { + branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifFalse, + Assembler::DoubleEqualOrUnordered); + jumpToBlock(ifTrue); + } +} + +void +CodeGeneratorMIPSShared::visitTestFAndBranch(LTestFAndBranch* test) +{ + FloatRegister input = ToFloatRegister(test->input()); + + MBasicBlock* ifTrue = test->ifTrue(); + MBasicBlock* ifFalse = test->ifFalse(); + + masm.loadConstantFloat32(0.0f, ScratchFloat32Reg); + // If 0, or NaN, the result is false. + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifTrue, + Assembler::DoubleNotEqual); + } else { + branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifFalse, + Assembler::DoubleEqualOrUnordered); + jumpToBlock(ifTrue); + } +} + +void +CodeGeneratorMIPSShared::visitCompareD(LCompareD* comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + Register dest = ToRegister(comp->output()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); + masm.ma_cmp_set_double(dest, lhs, rhs, cond); +} + +void +CodeGeneratorMIPSShared::visitCompareF(LCompareF* comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + Register dest = ToRegister(comp->output()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); + masm.ma_cmp_set_float32(dest, lhs, rhs, cond); +} + +void +CodeGeneratorMIPSShared::visitCompareDAndBranch(LCompareDAndBranch* comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); + MBasicBlock* ifTrue = comp->ifTrue(); + MBasicBlock* ifFalse = comp->ifFalse(); + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifTrue, cond); + } else { + branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifFalse, + Assembler::InvertCondition(cond)); + jumpToBlock(ifTrue); + } +} + +void +CodeGeneratorMIPSShared::visitCompareFAndBranch(LCompareFAndBranch* comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); + MBasicBlock* ifTrue = comp->ifTrue(); + MBasicBlock* ifFalse = comp->ifFalse(); + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::SingleFloat, lhs, rhs, ifTrue, cond); + } else { + branchToBlock(Assembler::SingleFloat, lhs, rhs, ifFalse, + Assembler::InvertCondition(cond)); + jumpToBlock(ifTrue); + } +} + +void +CodeGeneratorMIPSShared::visitBitAndAndBranch(LBitAndAndBranch* lir) +{ + if (lir->right()->isConstant()) + masm.ma_and(ScratchRegister, ToRegister(lir->left()), Imm32(ToInt32(lir->right()))); + else + masm.as_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right())); + emitBranch(ScratchRegister, ScratchRegister, Assembler::NonZero, lir->ifTrue(), + lir->ifFalse()); +} + +void +CodeGeneratorMIPSShared::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir) +{ + masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output())); +} + +void +CodeGeneratorMIPSShared::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir) +{ + masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output())); +} + +void +CodeGeneratorMIPSShared::visitNotI(LNotI* ins) +{ + masm.cmp32Set(Assembler::Equal, ToRegister(ins->input()), Imm32(0), + ToRegister(ins->output())); +} + +void +CodeGeneratorMIPSShared::visitNotD(LNotD* ins) +{ + // Since this operation is not, we want to set a bit if + // the double is falsey, which means 0.0, -0.0 or NaN. + FloatRegister in = ToFloatRegister(ins->input()); + Register dest = ToRegister(ins->output()); + + Label falsey, done; + masm.loadConstantDouble(0.0, ScratchDoubleReg); + masm.ma_bc1d(in, ScratchDoubleReg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); + + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + + masm.bind(&falsey); + masm.move32(Imm32(1), dest); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitNotF(LNotF* ins) +{ + // Since this operation is not, we want to set a bit if + // the float32 is falsey, which means 0.0, -0.0 or NaN. + FloatRegister in = ToFloatRegister(ins->input()); + Register dest = ToRegister(ins->output()); + + Label falsey, done; + masm.loadConstantFloat32(0.0f, ScratchFloat32Reg); + masm.ma_bc1s(in, ScratchFloat32Reg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); + + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + + masm.bind(&falsey); + masm.move32(Imm32(1), dest); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitGuardShape(LGuardShape* guard) +{ + Register obj = ToRegister(guard->input()); + Register tmp = ToRegister(guard->tempInt()); + + masm.loadPtr(Address(obj, JSObject::offsetOfShape()), tmp); + bailoutCmpPtr(Assembler::NotEqual, tmp, ImmGCPtr(guard->mir()->shape()), + guard->snapshot()); +} + +void +CodeGeneratorMIPSShared::visitGuardObjectGroup(LGuardObjectGroup* guard) +{ + Register obj = ToRegister(guard->input()); + Register tmp = ToRegister(guard->tempInt()); + MOZ_ASSERT(obj != tmp); + + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp); + Assembler::Condition cond = guard->mir()->bailOnEquality() + ? Assembler::Equal + : Assembler::NotEqual; + bailoutCmpPtr(cond, tmp, ImmGCPtr(guard->mir()->group()), guard->snapshot()); +} + +void +CodeGeneratorMIPSShared::visitGuardClass(LGuardClass* guard) +{ + Register obj = ToRegister(guard->input()); + Register tmp = ToRegister(guard->tempInt()); + + masm.loadObjClass(obj, tmp); + bailoutCmpPtr(Assembler::NotEqual, tmp, ImmPtr(guard->mir()->getClass()), + guard->snapshot()); +} + +void +CodeGeneratorMIPSShared::generateInvalidateEpilogue() +{ + // Ensure that there is enough space in the buffer for the OsiPoint + // patching to occur. Otherwise, we could overwrite the invalidation + // epilogue. + for (size_t i = 0; i < sizeof(void*); i += Assembler::NopSize()) + masm.nop(); + + masm.bind(&invalidate_); + + // Push the return address of the point that we bailed out at to the stack + masm.Push(ra); + + // Push the Ion script onto the stack (when we determine what that + // pointer is). + invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1))); + JitCode* thunk = gen->jitRuntime()->getInvalidationThunk(); + + masm.branch(thunk); + + // We should never reach this point in JIT code -- the invalidation thunk + // should pop the invalidated JS frame and return directly to its caller. + masm.assumeUnreachable("Should have returned directly to its caller instead of here."); +} + +void +CodeGeneratorMIPSShared::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) +{ + MOZ_CRASH("NYI"); +} + +void +CodeGeneratorMIPSShared::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) +{ + MOZ_CRASH("NYI"); +} + +void +CodeGeneratorMIPSShared::visitAsmJSCall(LAsmJSCall* ins) +{ + emitAsmJSCall(ins); +} + +void +CodeGeneratorMIPSShared::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) +{ + const MAsmJSLoadHeap* mir = ins->mir(); + const LAllocation* ptr = ins->ptr(); + const LDefinition* out = ins->output(); + + bool isSigned; + int size; + bool isFloat = false; + switch (mir->accessType()) { + case Scalar::Int8: isSigned = true; size = 8; break; + case Scalar::Uint8: isSigned = false; size = 8; break; + case Scalar::Int16: isSigned = true; size = 16; break; + case Scalar::Uint16: isSigned = false; size = 16; break; + case Scalar::Int32: isSigned = true; size = 32; break; + case Scalar::Uint32: isSigned = false; size = 32; break; + case Scalar::Float64: isFloat = true; size = 64; break; + case Scalar::Float32: isFloat = true; size = 32; break; + default: MOZ_CRASH("unexpected array type"); + } + + if (ptr->isConstant()) { + MOZ_ASSERT(!mir->needsBoundsCheck()); + int32_t ptrImm = ptr->toConstant()->toInt32(); + MOZ_ASSERT(ptrImm >= 0); + if (isFloat) { + if (size == 32) { + masm.loadFloat32(Address(HeapReg, ptrImm), ToFloatRegister(out)); + } else { + masm.loadDouble(Address(HeapReg, ptrImm), ToFloatRegister(out)); + } + } else { + masm.ma_load(ToRegister(out), Address(HeapReg, ptrImm), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + return; + } + + Register ptrReg = ToRegister(ptr); + + if (!mir->needsBoundsCheck()) { + if (isFloat) { + if (size == 32) { + masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + } else { + masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + } + } else { + masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + return; + } + + BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); + + Label done, outOfRange; + masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump); + // Offset is ok, let's load value. + if (isFloat) { + if (size == 32) + masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + else + masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + } else { + masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + masm.ma_b(&done, ShortJump); + masm.bind(&outOfRange); + // Offset is out of range. Load default values. + if (isFloat) { + if (size == 32) + masm.loadFloat32(Address(GlobalReg, AsmJSNaN32GlobalDataOffset - AsmJSGlobalRegBias), + ToFloatRegister(out)); + else + masm.loadDouble(Address(GlobalReg, AsmJSNaN64GlobalDataOffset - AsmJSGlobalRegBias), + ToFloatRegister(out)); + } else { + if (mir->isAtomicAccess()) + masm.ma_b(gen->outOfBoundsLabel()); + else + masm.move32(Imm32(0), ToRegister(out)); + } + masm.bind(&done); + + masm.append(AsmJSHeapAccess(bo.getOffset())); +} + +void +CodeGeneratorMIPSShared::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) +{ + const MAsmJSStoreHeap* mir = ins->mir(); + const LAllocation* value = ins->value(); + const LAllocation* ptr = ins->ptr(); + + bool isSigned; + int size; + bool isFloat = false; + switch (mir->accessType()) { + case Scalar::Int8: isSigned = true; size = 8; break; + case Scalar::Uint8: isSigned = false; size = 8; break; + case Scalar::Int16: isSigned = true; size = 16; break; + case Scalar::Uint16: isSigned = false; size = 16; break; + case Scalar::Int32: isSigned = true; size = 32; break; + case Scalar::Uint32: isSigned = false; size = 32; break; + case Scalar::Float64: isFloat = true; size = 64; break; + case Scalar::Float32: isFloat = true; size = 32; break; + default: MOZ_CRASH("unexpected array type"); + } + + if (ptr->isConstant()) { + MOZ_ASSERT(!mir->needsBoundsCheck()); + int32_t ptrImm = ptr->toConstant()->toInt32(); + MOZ_ASSERT(ptrImm >= 0); + + if (isFloat) { + if (size == 32) { + masm.storeFloat32(ToFloatRegister(value), Address(HeapReg, ptrImm)); + } else { + masm.storeDouble(ToFloatRegister(value), Address(HeapReg, ptrImm)); + } + } else { + masm.ma_store(ToRegister(value), Address(HeapReg, ptrImm), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + return; + } + + Register ptrReg = ToRegister(ptr); + Address dstAddr(ptrReg, 0); + + if (!mir->needsBoundsCheck()) { + if (isFloat) { + if (size == 32) { + masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else + masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else { + masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + return; + } + + BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); + + Label done, outOfRange; + masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump); + + // Offset is ok, let's store value. + if (isFloat) { + if (size == 32) { + masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else + masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else { + masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + masm.ma_b(&done, ShortJump); + masm.bind(&outOfRange); + // Offset is out of range. + if (mir->isAtomicAccess()) + masm.ma_b(gen->outOfBoundsLabel()); + masm.bind(&done); + + masm.append(AsmJSHeapAccess(bo.getOffset())); +} + +void +CodeGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins) +{ + MOZ_CRASH("NYI"); +} + +void +CodeGeneratorMIPSShared::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins) +{ + MOZ_CRASH("NYI"); +} + +void +CodeGeneratorMIPSShared::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins) +{ + const MAsmJSPassStackArg* mir = ins->mir(); + if (ins->arg()->isConstant()) { + masm.storePtr(ImmWord(ToInt32(ins->arg())), Address(StackPointer, mir->spOffset())); + } else { + if (ins->arg()->isGeneralReg()) { + masm.storePtr(ToRegister(ins->arg()), Address(StackPointer, mir->spOffset())); + } else { + masm.storeDouble(ToFloatRegister(ins->arg()).doubleOverlay(), + Address(StackPointer, mir->spOffset())); + } + } +} + +void +CodeGeneratorMIPSShared::visitUDivOrMod(LUDivOrMod* ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register output = ToRegister(ins->output()); + Label done; + + // Prevent divide by zero. + if (ins->canBeDivideByZero()) { + if (ins->mir()->isTruncated()) { + // Infinity|0 == 0 + Label notzero; + masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + masm.bind(¬zero); + } else { + bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); + } + } + + masm.as_divu(lhs, rhs); + masm.as_mfhi(output); + + // If the remainder is > 0, bailout since this must be a double. + if (ins->mir()->isDiv()) { + if (!ins->mir()->toDiv()->canTruncateRemainder()) + bailoutCmp32(Assembler::NonZero, output, output, ins->snapshot()); + // Get quotient + masm.as_mflo(output); + } + + if (!ins->mir()->isTruncated()) + bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitEffectiveAddress(LEffectiveAddress* ins) +{ + const MEffectiveAddress* mir = ins->mir(); + Register base = ToRegister(ins->base()); + Register index = ToRegister(ins->index()); + Register output = ToRegister(ins->output()); + + BaseIndex address(base, index, mir->scale(), mir->displacement()); + masm.computeEffectiveAddress(address, output); +} + +void +CodeGeneratorMIPSShared::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) +{ + const MAsmJSLoadGlobalVar* mir = ins->mir(); + unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; + if (mir->type() == MIRType_Int32) + masm.load32(Address(GlobalReg, addr), ToRegister(ins->output())); + else if (mir->type() == MIRType_Float32) + masm.loadFloat32(Address(GlobalReg, addr), ToFloatRegister(ins->output())); + else + masm.loadDouble(Address(GlobalReg, addr), ToFloatRegister(ins->output())); +} + +void +CodeGeneratorMIPSShared::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins) +{ + const MAsmJSStoreGlobalVar* mir = ins->mir(); + + MOZ_ASSERT(IsNumberType(mir->value()->type())); + unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; + if (mir->value()->type() == MIRType_Int32) + masm.store32(ToRegister(ins->value()), Address(GlobalReg, addr)); + else if (mir->value()->type() == MIRType_Float32) + masm.storeFloat32(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); + else + masm.storeDouble(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); +} + +void +CodeGeneratorMIPSShared::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins) +{ + const MAsmJSLoadFuncPtr* mir = ins->mir(); + + Register index = ToRegister(ins->index()); + Register out = ToRegister(ins->output()); + unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; + + BaseIndex source(GlobalReg, index, ScalePointer, addr); + masm.loadPtr(source, out); +} + +void +CodeGeneratorMIPSShared::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins) +{ + const MAsmJSLoadFFIFunc* mir = ins->mir(); + masm.loadPtr(Address(GlobalReg, mir->globalDataOffset() - AsmJSGlobalRegBias), + ToRegister(ins->output())); +} + +void +CodeGeneratorMIPSShared::visitNegI(LNegI* ins) +{ + Register input = ToRegister(ins->input()); + Register output = ToRegister(ins->output()); + + masm.ma_negu(output, input); +} + +void +CodeGeneratorMIPSShared::visitNegD(LNegD* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + + masm.as_negd(output, input); +} + +void +CodeGeneratorMIPSShared::visitNegF(LNegF* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + + masm.as_negs(output, input); +} diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.h b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h new file mode 100644 index 000000000000..ce06b14eb08a --- /dev/null +++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h @@ -0,0 +1,256 @@ +/* -*- 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 jit_mips_shared_CodeGenerator_mips_shared_h +#define jit_mips_shared_CodeGenerator_mips_shared_h + +#include "jit/shared/CodeGenerator-shared.h" + +namespace js { +namespace jit { + +class OutOfLineBailout; +class OutOfLineTableSwitch; + +class CodeGeneratorMIPSShared : public CodeGeneratorShared +{ + friend class MoveResolverMIPS; + + CodeGeneratorMIPSShared* thisFromCtor() { + return this; + } + + protected: + NonAssertingLabel deoptLabel_; + + inline Address ToAddress(const LAllocation& a); + inline Address ToAddress(const LAllocation* a); + + MoveOperand toMoveOperand(LAllocation a) const; + + template + void bailoutCmp32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { + Label bail; + masm.branch32(c, lhs, rhs, &bail); + bailoutFrom(&bail, snapshot); + } + template + void bailoutCmp32(Assembler::Condition c, Operand lhs, T rhs, LSnapshot* snapshot) { + if (lhs.getTag() == Operand::REG) + bailoutCmp32(c, lhs.toReg(), rhs, snapshot); + else if (lhs.getTag() == Operand::MEM) + bailoutCmp32(c, lhs.toAddress(), rhs, snapshot); + else + MOZ_CRASH("Invalid operand tag."); + } + template + void bailoutTest32(Assembler::Condition c, Register lhs, T rhs, LSnapshot* snapshot) { + Label bail; + masm.branchTest32(c, lhs, rhs, &bail); + bailoutFrom(&bail, snapshot); + } + template + void bailoutCmpPtr(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { + Label bail; + masm.branchPtr(c, lhs, rhs, &bail); + bailoutFrom(&bail, snapshot); + } + void bailoutTestPtr(Assembler::Condition c, Register lhs, Register rhs, LSnapshot* snapshot) { + Label bail; + masm.branchTestPtr(c, lhs, rhs, &bail); + bailoutFrom(&bail, snapshot); + } + void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) { + Label bail; + masm.branchTest32(Assembler::Zero, reg, Imm32(0xFF), &bail); + bailoutFrom(&bail, snapshot); + } + + void bailoutFrom(Label* label, LSnapshot* snapshot); + void bailout(LSnapshot* snapshot); + + protected: + bool generateOutOfLineCode(); + + template + void branchToBlock(Register lhs, T rhs, MBasicBlock* mir, Assembler::Condition cond) + { + mir = skipTrivialBlocks(mir); + + Label* label = mir->lir()->label(); + if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) { + // Note: the backedge is initially a jump to the next instruction. + // It will be patched to the target block's label during link(). + RepatchLabel rejoin; + CodeOffsetJump backedge; + Label skip; + + masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); + backedge = masm.backedgeJump(&rejoin); + masm.bind(&rejoin); + masm.bind(&skip); + + if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) + MOZ_CRASH(); + } else { + masm.ma_b(lhs, rhs, label, cond); + } + } + void branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, + MBasicBlock* mir, Assembler::DoubleCondition cond); + + // Emits a branch that directs control flow to the true block if |cond| is + // true, and the false block if |cond| is false. + template + void emitBranch(Register lhs, T rhs, Assembler::Condition cond, + MBasicBlock* mirTrue, MBasicBlock* mirFalse) + { + if (isNextBlock(mirFalse->lir())) { + branchToBlock(lhs, rhs, mirTrue, cond); + } else { + branchToBlock(lhs, rhs, mirFalse, Assembler::InvertCondition(cond)); + jumpToBlock(mirTrue); + } + } + void testZeroEmitBranch(Assembler::Condition cond, Register reg, + MBasicBlock* ifTrue, MBasicBlock* ifFalse) + { + emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse); + } + + public: + // Instruction visitors. + virtual void visitMinMaxD(LMinMaxD* ins); + virtual void visitMinMaxF(LMinMaxF* ins); + virtual void visitAbsD(LAbsD* ins); + virtual void visitAbsF(LAbsF* ins); + virtual void visitSqrtD(LSqrtD* ins); + virtual void visitSqrtF(LSqrtF* ins); + virtual void visitAddI(LAddI* ins); + virtual void visitSubI(LSubI* ins); + virtual void visitBitNotI(LBitNotI* ins); + virtual void visitBitOpI(LBitOpI* ins); + + virtual void visitMulI(LMulI* ins); + + virtual void visitDivI(LDivI* ins); + virtual void visitDivPowTwoI(LDivPowTwoI* ins); + virtual void visitModI(LModI* ins); + virtual void visitModPowTwoI(LModPowTwoI* ins); + virtual void visitModMaskI(LModMaskI* ins); + virtual void visitPowHalfD(LPowHalfD* ins); + virtual void visitShiftI(LShiftI* ins); + virtual void visitUrshD(LUrshD* ins); + + virtual void visitClzI(LClzI* ins); + + virtual void visitTestIAndBranch(LTestIAndBranch* test); + virtual void visitCompare(LCompare* comp); + virtual void visitCompareAndBranch(LCompareAndBranch* comp); + virtual void visitTestDAndBranch(LTestDAndBranch* test); + virtual void visitTestFAndBranch(LTestFAndBranch* test); + virtual void visitCompareD(LCompareD* comp); + virtual void visitCompareF(LCompareF* comp); + virtual void visitCompareDAndBranch(LCompareDAndBranch* comp); + virtual void visitCompareFAndBranch(LCompareFAndBranch* comp); + virtual void visitBitAndAndBranch(LBitAndAndBranch* lir); + virtual void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir); + virtual void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir); + virtual void visitNotI(LNotI* ins); + virtual void visitNotD(LNotD* ins); + virtual void visitNotF(LNotF* ins); + + virtual void visitMathD(LMathD* math); + virtual void visitMathF(LMathF* math); + virtual void visitFloor(LFloor* lir); + virtual void visitFloorF(LFloorF* lir); + virtual void visitCeil(LCeil* lir); + virtual void visitCeilF(LCeilF* lir); + virtual void visitRound(LRound* lir); + virtual void visitRoundF(LRoundF* lir); + virtual void visitTruncateDToInt32(LTruncateDToInt32* ins); + virtual void visitTruncateFToInt32(LTruncateFToInt32* ins); + + // Out of line visitors. + void visitOutOfLineBailout(OutOfLineBailout* ool); + protected: + virtual ValueOperand ToOutValue(LInstruction* ins) = 0; + + public: + CodeGeneratorMIPSShared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm); + + void visitValue(LValue* value); + void visitDouble(LDouble* ins); + void visitFloat32(LFloat32* ins); + + void visitGuardShape(LGuardShape* guard); + void visitGuardObjectGroup(LGuardObjectGroup* guard); + void visitGuardClass(LGuardClass* guard); + + void visitNegI(LNegI* lir); + void visitNegD(LNegD* lir); + void visitNegF(LNegF* lir); + void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins); + void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins); + void visitAsmJSCall(LAsmJSCall* ins); + void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins); + void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins); + void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins); + void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins); + void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins); + void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins); + void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins); + void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins); + + void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins); + + void generateInvalidateEpilogue(); + + protected: + void visitEffectiveAddress(LEffectiveAddress* ins); + void visitUDivOrMod(LUDivOrMod* ins); + + public: + // Unimplemented SIMD instructions + void visitSimdSplatX4(LSimdSplatX4* lir) { MOZ_CRASH("NYI"); } + void visitInt32x4(LInt32x4* ins) { MOZ_CRASH("NYI"); } + void visitFloat32x4(LFloat32x4* ins) { MOZ_CRASH("NYI"); } + void visitSimdReinterpretCast(LSimdReinterpretCast* ins) { MOZ_CRASH("NYI"); } + void visitSimdExtractElementI(LSimdExtractElementI* ins) { MOZ_CRASH("NYI"); } + void visitSimdExtractElementF(LSimdExtractElementF* ins) { MOZ_CRASH("NYI"); } + void visitSimdSignMaskX4(LSimdSignMaskX4* ins) { MOZ_CRASH("NYI"); } + void visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir) { MOZ_CRASH("NYI"); } + void visitSimdGeneralShuffleI(LSimdGeneralShuffleI* lir) { MOZ_CRASH("NYI"); } + void visitSimdGeneralShuffleF(LSimdGeneralShuffleF* lir) { MOZ_CRASH("NYI"); } +}; + +// An out-of-line bailout thunk. +class OutOfLineBailout : public OutOfLineCodeBase +{ + LSnapshot* snapshot_; + uint32_t frameSize_; + + public: + OutOfLineBailout(LSnapshot* snapshot, uint32_t frameSize) + : snapshot_(snapshot), + frameSize_(frameSize) + { } + + void accept(CodeGeneratorMIPSShared* codegen); + + LSnapshot* snapshot() const { + return snapshot_; + } +}; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_shared_CodeGenerator_mips_shared_h */ diff --git a/js/src/jit/mips32/CodeGenerator-mips32.cpp b/js/src/jit/mips32/CodeGenerator-mips32.cpp index db71c3bccd39..f469463fda76 100644 --- a/js/src/jit/mips32/CodeGenerator-mips32.cpp +++ b/js/src/jit/mips32/CodeGenerator-mips32.cpp @@ -8,10 +8,6 @@ #include "mozilla/MathAlgorithms.h" -#include "jscntxt.h" -#include "jscompartment.h" -#include "jsnum.h" - #include "jit/CodeGenerator.h" #include "jit/JitCompartment.h" #include "jit/JitFrames.h" @@ -21,923 +17,12 @@ #include "vm/Shape.h" #include "vm/TraceLogging.h" -#include "jsscriptinlines.h" - #include "jit/MacroAssembler-inl.h" #include "jit/shared/CodeGenerator-shared-inl.h" using namespace js; using namespace js::jit; -using mozilla::FloorLog2; -using mozilla::NegativeInfinity; -using JS::GenericNaN; -using JS::ToInt32; - -// inline -Address -CodeGeneratorMIPS::ToAddress(const LAllocation& a) -{ - MOZ_ASSERT(a.isMemory()); - int32_t offset = ToStackOffset(&a); - - return Address(StackPointer, offset); -} - -// inline -Address -CodeGeneratorMIPS::ToAddress(const LAllocation* a) -{ - return ToAddress(*a); -} - - -// shared -CodeGeneratorMIPS::CodeGeneratorMIPS(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) - : CodeGeneratorShared(gen, graph, masm) -{ -} - -void -CodeGeneratorMIPS::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, - MBasicBlock* mir, Assembler::DoubleCondition cond) -{ - // Skip past trivial blocks. - mir = skipTrivialBlocks(mir); - - Label* label = mir->lir()->label(); - if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) { - // Note: the backedge is initially a jump to the next instruction. - // It will be patched to the target block's label during link(). - RepatchLabel rejoin; - - CodeOffsetJump backedge; - Label skip; - if (fmt == Assembler::DoubleFloat) - masm.ma_bc1d(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); - else - masm.ma_bc1s(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); - - backedge = masm.backedgeJump(&rejoin); - masm.bind(&rejoin); - masm.bind(&skip); - - if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) - MOZ_CRASH(); - } else { - if (fmt == Assembler::DoubleFloat) - masm.branchDouble(cond, lhs, rhs, mir->lir()->label()); - else - masm.branchFloat(cond, lhs, rhs, mir->lir()->label()); - } -} - -void -OutOfLineBailout::accept(CodeGeneratorMIPS* codegen) -{ - codegen->visitOutOfLineBailout(this); -} - -void -CodeGeneratorMIPS::visitTestIAndBranch(LTestIAndBranch* test) -{ - const LAllocation* opd = test->getOperand(0); - MBasicBlock* ifTrue = test->ifTrue(); - MBasicBlock* ifFalse = test->ifFalse(); - - emitBranch(ToRegister(opd), Imm32(0), Assembler::NonZero, ifTrue, ifFalse); -} - -void -CodeGeneratorMIPS::visitCompare(LCompare* comp) -{ - Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop()); - const LAllocation* left = comp->getOperand(0); - const LAllocation* right = comp->getOperand(1); - const LDefinition* def = comp->getDef(0); - - if (right->isConstant()) - masm.cmp32Set(cond, ToRegister(left), Imm32(ToInt32(right)), ToRegister(def)); - else if (right->isGeneralReg()) - masm.cmp32Set(cond, ToRegister(left), ToRegister(right), ToRegister(def)); - else - masm.cmp32Set(cond, ToRegister(left), ToAddress(right), ToRegister(def)); -} - -void -CodeGeneratorMIPS::visitCompareAndBranch(LCompareAndBranch* comp) -{ - Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop()); - if (comp->right()->isConstant()) { - emitBranch(ToRegister(comp->left()), Imm32(ToInt32(comp->right())), cond, - comp->ifTrue(), comp->ifFalse()); - } else if (comp->right()->isGeneralReg()) { - emitBranch(ToRegister(comp->left()), ToRegister(comp->right()), cond, - comp->ifTrue(), comp->ifFalse()); - } else { - emitBranch(ToRegister(comp->left()), ToAddress(comp->right()), cond, - comp->ifTrue(), comp->ifFalse()); - } -} - -bool -CodeGeneratorMIPS::generateOutOfLineCode() -{ - if (!CodeGeneratorShared::generateOutOfLineCode()) - return false; - - if (deoptLabel_.used()) { - // All non-table-based bailouts will go here. - masm.bind(&deoptLabel_); - - // Push the frame size, so the handler can recover the IonScript. - // Frame size is stored in 'ra' and pushed by GenerateBailoutThunk - // We have to use 'ra' because generateBailoutTable will implicitly do - // the same. - masm.move32(Imm32(frameSize()), ra); - - JitCode* handler = gen->jitRuntime()->getGenericBailoutHandler(); - - masm.branch(handler); - } - - return true; -} - -void -CodeGeneratorMIPS::bailoutFrom(Label* label, LSnapshot* snapshot) -{ - if (masm.bailed()) - return; - - MOZ_ASSERT(label->used()); - MOZ_ASSERT(!label->bound()); - - encode(snapshot); - - // Though the assembler doesn't track all frame pushes, at least make sure - // the known value makes sense. We can't use bailout tables if the stack - // isn't properly aligned to the static frame size. - MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(), - frameClass_.frameSize() == masm.framePushed()); - - // We don't use table bailouts because retargeting is easier this way. - InlineScriptTree* tree = snapshot->mir()->block()->trackedTree(); - OutOfLineBailout* ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed()); - addOutOfLineCode(ool, new(alloc()) BytecodeSite(tree, tree->script()->code())); - - masm.retarget(label, ool->entry()); -} - -void -CodeGeneratorMIPS::bailout(LSnapshot* snapshot) -{ - Label label; - masm.jump(&label); - bailoutFrom(&label, snapshot); -} - -void -CodeGeneratorMIPS::visitOutOfLineBailout(OutOfLineBailout* ool) -{ - // Push snapshotOffset and make sure stack is aligned. - masm.subPtr(Imm32(2 * sizeof(void*)), StackPointer); - masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0)); - - masm.jump(&deoptLabel_); -} - -void -CodeGeneratorMIPS::visitMinMaxD(LMinMaxD* ins) -{ - FloatRegister first = ToFloatRegister(ins->first()); - FloatRegister second = ToFloatRegister(ins->second()); - FloatRegister output = ToFloatRegister(ins->output()); - - MOZ_ASSERT(first == output); - - Assembler::DoubleCondition cond = ins->mir()->isMax() - ? Assembler::DoubleLessThanOrEqual - : Assembler::DoubleGreaterThanOrEqual; - Label nan, equal, returnSecond, done; - - // First or second is NaN, result is NaN. - masm.ma_bc1d(first, second, &nan, Assembler::DoubleUnordered, ShortJump); - // Make sure we handle -0 and 0 right. - masm.ma_bc1d(first, second, &equal, Assembler::DoubleEqual, ShortJump); - masm.ma_bc1d(first, second, &returnSecond, cond, ShortJump); - masm.ma_b(&done, ShortJump); - - // Check for zero. - masm.bind(&equal); - masm.loadConstantDouble(0.0, ScratchDoubleReg); - // First wasn't 0 or -0, so just return it. - masm.ma_bc1d(first, ScratchDoubleReg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump); - - // So now both operands are either -0 or 0. - if (ins->mir()->isMax()) { - // -0 + -0 = -0 and -0 + 0 = 0. - masm.addDouble(second, first); - } else { - masm.negateDouble(first); - masm.subDouble(second, first); - masm.negateDouble(first); - } - masm.ma_b(&done, ShortJump); - - masm.bind(&nan); - masm.loadConstantDouble(GenericNaN(), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&returnSecond); - masm.moveDouble(second, output); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitMinMaxF(LMinMaxF* ins) -{ - FloatRegister first = ToFloatRegister(ins->first()); - FloatRegister second = ToFloatRegister(ins->second()); - FloatRegister output = ToFloatRegister(ins->output()); - - MOZ_ASSERT(first == output); - - Assembler::DoubleCondition cond = ins->mir()->isMax() - ? Assembler::DoubleLessThanOrEqual - : Assembler::DoubleGreaterThanOrEqual; - Label nan, equal, returnSecond, done; - - // First or second is NaN, result is NaN. - masm.ma_bc1s(first, second, &nan, Assembler::DoubleUnordered, ShortJump); - // Make sure we handle -0 and 0 right. - masm.ma_bc1s(first, second, &equal, Assembler::DoubleEqual, ShortJump); - masm.ma_bc1s(first, second, &returnSecond, cond, ShortJump); - masm.ma_b(&done, ShortJump); - - // Check for zero. - masm.bind(&equal); - masm.loadConstantFloat32(0.0, ScratchFloat32Reg); - // First wasn't 0 or -0, so just return it. - masm.ma_bc1s(first, ScratchFloat32Reg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump); - - // So now both operands are either -0 or 0. - if (ins->mir()->isMax()) { - // -0 + -0 = -0 and -0 + 0 = 0. - masm.as_adds(first, first, second); - } else { - masm.as_negs(first, first); - masm.as_subs(first, first, second); - masm.as_negs(first, first); - } - masm.ma_b(&done, ShortJump); - - masm.bind(&nan); - masm.loadConstantFloat32(GenericNaN(), output); - masm.ma_b(&done, ShortJump); - masm.bind(&returnSecond); - masm.as_movs(output, second); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitAbsD(LAbsD* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - MOZ_ASSERT(input == ToFloatRegister(ins->output())); - masm.as_absd(input, input); -} - -void -CodeGeneratorMIPS::visitAbsF(LAbsF* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - MOZ_ASSERT(input == ToFloatRegister(ins->output())); - masm.as_abss(input, input); -} - -void -CodeGeneratorMIPS::visitSqrtD(LSqrtD* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - masm.as_sqrtd(output, input); -} - -void -CodeGeneratorMIPS::visitSqrtF(LSqrtF* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - masm.as_sqrts(output, input); -} - -void -CodeGeneratorMIPS::visitAddI(LAddI* ins) -{ - const LAllocation* lhs = ins->getOperand(0); - const LAllocation* rhs = ins->getOperand(1); - const LDefinition* dest = ins->getDef(0); - - MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); - - // If there is no snapshot, we don't need to check for overflow - if (!ins->snapshot()) { - if (rhs->isConstant()) - masm.ma_addu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_addu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - return; - } - - Label overflow; - if (rhs->isConstant()) - masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); - else - masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); - - bailoutFrom(&overflow, ins->snapshot()); -} - -void -CodeGeneratorMIPS::visitSubI(LSubI* ins) -{ - const LAllocation* lhs = ins->getOperand(0); - const LAllocation* rhs = ins->getOperand(1); - const LDefinition* dest = ins->getDef(0); - - MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); - - // If there is no snapshot, we don't need to check for overflow - if (!ins->snapshot()) { - if (rhs->isConstant()) - masm.ma_subu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_subu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - return; - } - - Label overflow; - if (rhs->isConstant()) - masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); - else - masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); - - bailoutFrom(&overflow, ins->snapshot()); -} - -void -CodeGeneratorMIPS::visitMulI(LMulI* ins) -{ - const LAllocation* lhs = ins->lhs(); - const LAllocation* rhs = ins->rhs(); - Register dest = ToRegister(ins->output()); - MMul* mul = ins->mir(); - - MOZ_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow()); - - if (rhs->isConstant()) { - int32_t constant = ToInt32(rhs); - Register src = ToRegister(lhs); - - // Bailout on -0.0 - if (mul->canBeNegativeZero() && constant <= 0) { - Assembler::Condition cond = (constant == 0) ? Assembler::LessThan : Assembler::Equal; - bailoutCmp32(cond, src, Imm32(0), ins->snapshot()); - } - - switch (constant) { - case -1: - if (mul->canOverflow()) - bailoutCmp32(Assembler::Equal, src, Imm32(INT32_MIN), ins->snapshot()); - - masm.ma_negu(dest, src); - break; - case 0: - masm.move32(Imm32(0), dest); - break; - case 1: - masm.move32(src, dest); - break; - case 2: - if (mul->canOverflow()) { - Label mulTwoOverflow; - masm.ma_addTestOverflow(dest, src, src, &mulTwoOverflow); - - bailoutFrom(&mulTwoOverflow, ins->snapshot()); - } else { - masm.as_addu(dest, src, src); - } - break; - default: - uint32_t shift = FloorLog2(constant); - - if (!mul->canOverflow() && (constant > 0)) { - // If it cannot overflow, we can do lots of optimizations. - uint32_t rest = constant - (1 << shift); - - // See if the constant has one bit set, meaning it can be - // encoded as a bitshift. - if ((1 << shift) == constant) { - masm.ma_sll(dest, src, Imm32(shift)); - return; - } - - // If the constant cannot be encoded as (1<canOverflow() && (constant > 0) && (src != dest)) { - // To stay on the safe side, only optimize things that are a - // power of 2. - - if ((1 << shift) == constant) { - // dest = lhs * pow(2, shift) - masm.ma_sll(dest, src, Imm32(shift)); - // At runtime, check (lhs == dest >> shift), if this does - // not hold, some bits were lost due to overflow, and the - // computation should be resumed as a double. - masm.ma_sra(ScratchRegister, dest, Imm32(shift)); - bailoutCmp32(Assembler::NotEqual, src, ScratchRegister, ins->snapshot()); - return; - } - } - - if (mul->canOverflow()) { - Label mulConstOverflow; - masm.ma_mul_branch_overflow(dest, ToRegister(lhs), Imm32(ToInt32(rhs)), - &mulConstOverflow); - - bailoutFrom(&mulConstOverflow, ins->snapshot()); - } else { - masm.ma_mult(src, Imm32(ToInt32(rhs))); - masm.as_mflo(dest); - } - break; - } - } else { - Label multRegOverflow; - - if (mul->canOverflow()) { - masm.ma_mul_branch_overflow(dest, ToRegister(lhs), ToRegister(rhs), &multRegOverflow); - bailoutFrom(&multRegOverflow, ins->snapshot()); - } else { - masm.as_mult(ToRegister(lhs), ToRegister(rhs)); - masm.as_mflo(dest); - } - - if (mul->canBeNegativeZero()) { - Label done; - masm.ma_b(dest, dest, &done, Assembler::NonZero, ShortJump); - - // Result is -0 if lhs or rhs is negative. - // In that case result must be double value so bailout - Register scratch = SecondScratchReg; - masm.as_or(scratch, ToRegister(lhs), ToRegister(rhs)); - bailoutCmp32(Assembler::Signed, scratch, scratch, ins->snapshot()); - - masm.bind(&done); - } - } -} - -void -CodeGeneratorMIPS::visitDivI(LDivI* ins) -{ - // Extract the registers from this instruction - Register lhs = ToRegister(ins->lhs()); - Register rhs = ToRegister(ins->rhs()); - Register dest = ToRegister(ins->output()); - Register temp = ToRegister(ins->getTemp(0)); - MDiv* mir = ins->mir(); - - Label done; - - // Handle divide by zero. - if (mir->canBeDivideByZero()) { - if (mir->canTruncateInfinities()) { - // Truncated division by zero is zero (Infinity|0 == 0) - Label notzero; - masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - masm.bind(¬zero); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Zero, rhs, rhs, ins->snapshot()); - } - } - - // Handle an integer overflow exception from -2147483648 / -1. - if (mir->canBeNegativeOverflow()) { - Label notMinInt; - masm.move32(Imm32(INT32_MIN), temp); - masm.ma_b(lhs, temp, ¬MinInt, Assembler::NotEqual, ShortJump); - - masm.move32(Imm32(-1), temp); - if (mir->canTruncateOverflow()) { - // (-INT32_MIN)|0 == INT32_MIN - Label skip; - masm.ma_b(rhs, temp, &skip, Assembler::NotEqual, ShortJump); - masm.move32(Imm32(INT32_MIN), dest); - masm.ma_b(&done, ShortJump); - masm.bind(&skip); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, rhs, temp, ins->snapshot()); - } - masm.bind(¬MinInt); - } - - // Handle negative 0. (0/-Y) - if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) { - Label nonzero; - masm.ma_b(lhs, lhs, &nonzero, Assembler::NonZero, ShortJump); - bailoutCmp32(Assembler::LessThan, rhs, Imm32(0), ins->snapshot()); - masm.bind(&nonzero); - } - // Note: above safety checks could not be verified as Ion seems to be - // smarter and requires double arithmetic in such cases. - - // All regular. Lets call div. - if (mir->canTruncateRemainder()) { - masm.as_div(lhs, rhs); - masm.as_mflo(dest); - } else { - MOZ_ASSERT(mir->fallible()); - - Label remainderNonZero; - masm.ma_div_branch_overflow(dest, lhs, rhs, &remainderNonZero); - bailoutFrom(&remainderNonZero, ins->snapshot()); - } - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitDivPowTwoI(LDivPowTwoI* ins) -{ - Register lhs = ToRegister(ins->numerator()); - Register dest = ToRegister(ins->output()); - Register tmp = ToRegister(ins->getTemp(0)); - int32_t shift = ins->shift(); - - if (shift != 0) { - MDiv* mir = ins->mir(); - if (!mir->isTruncated()) { - // If the remainder is going to be != 0, bailout since this must - // be a double. - masm.ma_sll(tmp, lhs, Imm32(32 - shift)); - bailoutCmp32(Assembler::NonZero, tmp, tmp, ins->snapshot()); - } - - if (!mir->canBeNegativeDividend()) { - // Numerator is unsigned, so needs no adjusting. Do the shift. - masm.ma_sra(dest, lhs, Imm32(shift)); - return; - } - - // Adjust the value so that shifting produces a correctly rounded result - // when the numerator is negative. See 10-1 "Signed Division by a Known - // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight. - if (shift > 1) { - masm.ma_sra(tmp, lhs, Imm32(31)); - masm.ma_srl(tmp, tmp, Imm32(32 - shift)); - masm.add32(lhs, tmp); - } else { - masm.ma_srl(tmp, lhs, Imm32(32 - shift)); - masm.add32(lhs, tmp); - } - - // Do the shift. - masm.ma_sra(dest, tmp, Imm32(shift)); - } else { - masm.move32(lhs, dest); - } -} - -void -CodeGeneratorMIPS::visitModI(LModI* ins) -{ - // Extract the registers from this instruction - Register lhs = ToRegister(ins->lhs()); - Register rhs = ToRegister(ins->rhs()); - Register dest = ToRegister(ins->output()); - Register callTemp = ToRegister(ins->callTemp()); - MMod* mir = ins->mir(); - Label done, prevent; - - masm.move32(lhs, callTemp); - - // Prevent INT_MIN % -1; - // The integer division will give INT_MIN, but we want -(double)INT_MIN. - if (mir->canBeNegativeDividend()) { - masm.ma_b(lhs, Imm32(INT_MIN), &prevent, Assembler::NotEqual, ShortJump); - if (mir->isTruncated()) { - // (INT_MIN % -1)|0 == 0 - Label skip; - masm.ma_b(rhs, Imm32(-1), &skip, Assembler::NotEqual, ShortJump); - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - masm.bind(&skip); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, rhs, Imm32(-1), ins->snapshot()); - } - masm.bind(&prevent); - } - - // 0/X (with X < 0) is bad because both of these values *should* be - // doubles, and the result should be -0.0, which cannot be represented in - // integers. X/0 is bad because it will give garbage (or abort), when it - // should give either \infty, -\infty or NAN. - - // Prevent 0 / X (with X < 0) and X / 0 - // testing X / Y. Compare Y with 0. - // There are three cases: (Y < 0), (Y == 0) and (Y > 0) - // If (Y < 0), then we compare X with 0, and bail if X == 0 - // If (Y == 0), then we simply want to bail. - // if (Y > 0), we don't bail. - - if (mir->canBeDivideByZero()) { - if (mir->isTruncated()) { - Label skip; - masm.ma_b(rhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - masm.bind(&skip); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); - } - } - - if (mir->canBeNegativeDividend()) { - Label notNegative; - masm.ma_b(rhs, Imm32(0), ¬Negative, Assembler::GreaterThan, ShortJump); - if (mir->isTruncated()) { - // NaN|0 == 0 and (0 % -X)|0 == 0 - Label skip; - masm.ma_b(lhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - masm.bind(&skip); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, lhs, Imm32(0), ins->snapshot()); - } - masm.bind(¬Negative); - } - - masm.as_div(lhs, rhs); - masm.as_mfhi(dest); - - // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0 - if (mir->canBeNegativeDividend()) { - if (mir->isTruncated()) { - // -0.0|0 == 0 - } else { - MOZ_ASSERT(mir->fallible()); - // See if X < 0 - masm.ma_b(dest, Imm32(0), &done, Assembler::NotEqual, ShortJump); - bailoutCmp32(Assembler::Signed, callTemp, Imm32(0), ins->snapshot()); - } - } - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitModPowTwoI(LModPowTwoI* ins) -{ - Register in = ToRegister(ins->getOperand(0)); - Register out = ToRegister(ins->getDef(0)); - MMod* mir = ins->mir(); - Label negative, done; - - masm.move32(in, out); - masm.ma_b(in, in, &done, Assembler::Zero, ShortJump); - // Switch based on sign of the lhs. - // Positive numbers are just a bitmask - masm.ma_b(in, in, &negative, Assembler::Signed, ShortJump); - { - masm.and32(Imm32((1 << ins->shift()) - 1), out); - masm.ma_b(&done, ShortJump); - } - - // Negative numbers need a negate, bitmask, negate - { - masm.bind(&negative); - masm.neg32(out); - masm.and32(Imm32((1 << ins->shift()) - 1), out); - masm.neg32(out); - } - if (mir->canBeNegativeDividend()) { - if (!mir->isTruncated()) { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, out, zero, ins->snapshot()); - } else { - // -0|0 == 0 - } - } - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitModMaskI(LModMaskI* ins) -{ - Register src = ToRegister(ins->getOperand(0)); - Register dest = ToRegister(ins->getDef(0)); - Register tmp0 = ToRegister(ins->getTemp(0)); - Register tmp1 = ToRegister(ins->getTemp(1)); - MMod* mir = ins->mir(); - - if (!mir->isTruncated() && mir->canBeNegativeDividend()) { - MOZ_ASSERT(mir->fallible()); - - Label bail; - masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), &bail); - bailoutFrom(&bail, ins->snapshot()); - } else { - masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), nullptr); - } -} - -void -CodeGeneratorMIPS::visitBitNotI(LBitNotI* ins) -{ - const LAllocation* input = ins->getOperand(0); - const LDefinition* dest = ins->getDef(0); - MOZ_ASSERT(!input->isConstant()); - - masm.ma_not(ToRegister(dest), ToRegister(input)); -} - -void -CodeGeneratorMIPS::visitBitOpI(LBitOpI* ins) -{ - const LAllocation* lhs = ins->getOperand(0); - const LAllocation* rhs = ins->getOperand(1); - const LDefinition* dest = ins->getDef(0); - // all of these bitops should be either imm32's, or integer registers. - switch (ins->bitop()) { - case JSOP_BITOR: - if (rhs->isConstant()) - masm.ma_or(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_or(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - break; - case JSOP_BITXOR: - if (rhs->isConstant()) - masm.ma_xor(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_xor(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - break; - case JSOP_BITAND: - if (rhs->isConstant()) - masm.ma_and(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - break; - default: - MOZ_CRASH("unexpected binary opcode"); - } -} - -void -CodeGeneratorMIPS::visitShiftI(LShiftI* ins) -{ - Register lhs = ToRegister(ins->lhs()); - const LAllocation* rhs = ins->rhs(); - Register dest = ToRegister(ins->output()); - - if (rhs->isConstant()) { - int32_t shift = ToInt32(rhs) & 0x1F; - switch (ins->bitop()) { - case JSOP_LSH: - if (shift) - masm.ma_sll(dest, lhs, Imm32(shift)); - else - masm.move32(lhs, dest); - break; - case JSOP_RSH: - if (shift) - masm.ma_sra(dest, lhs, Imm32(shift)); - else - masm.move32(lhs, dest); - break; - case JSOP_URSH: - if (shift) { - masm.ma_srl(dest, lhs, Imm32(shift)); - } else { - // x >>> 0 can overflow. - masm.move32(lhs, dest); - if (ins->mir()->toUrsh()->fallible()) - bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); - } - break; - default: - MOZ_CRASH("Unexpected shift op"); - } - } else { - // The shift amounts should be AND'ed into the 0-31 range - masm.ma_and(dest, ToRegister(rhs), Imm32(0x1F)); - - switch (ins->bitop()) { - case JSOP_LSH: - masm.ma_sll(dest, lhs, dest); - break; - case JSOP_RSH: - masm.ma_sra(dest, lhs, dest); - break; - case JSOP_URSH: - masm.ma_srl(dest, lhs, dest); - if (ins->mir()->toUrsh()->fallible()) { - // x >>> 0 can overflow. - bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); - } - break; - default: - MOZ_CRASH("Unexpected shift op"); - } - } -} - -void -CodeGeneratorMIPS::visitUrshD(LUrshD* ins) -{ - Register lhs = ToRegister(ins->lhs()); - Register temp = ToRegister(ins->temp()); - - const LAllocation* rhs = ins->rhs(); - FloatRegister out = ToFloatRegister(ins->output()); - - if (rhs->isConstant()) { - masm.ma_srl(temp, lhs, Imm32(ToInt32(rhs))); - } else { - masm.ma_srl(temp, lhs, ToRegister(rhs)); - } - - masm.convertUInt32ToDouble(temp, out); -} - -void -CodeGeneratorMIPS::visitClzI(LClzI* ins) -{ - Register input = ToRegister(ins->input()); - Register output = ToRegister(ins->output()); - - masm.as_clz(output, input); -} - -void -CodeGeneratorMIPS::visitPowHalfD(LPowHalfD* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - - Label done, skip; - - // Masm.pow(-Infinity, 0.5) == Infinity. - masm.loadConstantDouble(NegativeInfinity(), ScratchDoubleReg); - masm.ma_bc1d(input, ScratchDoubleReg, &skip, Assembler::DoubleNotEqualOrUnordered, ShortJump); - masm.as_negd(output, ScratchDoubleReg); - masm.ma_b(&done, ShortJump); - - masm.bind(&skip); - // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). - // Adding 0 converts any -0 to 0. - masm.loadConstantDouble(0.0, ScratchDoubleReg); - masm.as_addd(output, input, ScratchDoubleReg); - masm.as_sqrtd(output, output); - - masm.bind(&done); -} - -MoveOperand -CodeGeneratorMIPS::toMoveOperand(LAllocation a) const -{ - if (a.isGeneralReg()) - return MoveOperand(ToRegister(a)); - if (a.isFloatReg()) { - return MoveOperand(ToFloatRegister(a)); - } - int32_t offset = ToStackOffset(a); - MOZ_ASSERT((offset & 3) == 0); - - return MoveOperand(StackPointer, offset); -} - class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase { MTableSwitch* mir_; @@ -1013,316 +98,6 @@ CodeGeneratorMIPS::emitTableSwitchDispatch(MTableSwitch* mir, Register index, masm.branch(address); } -void -CodeGeneratorMIPS::visitMathD(LMathD* math) -{ - const LAllocation* src1 = math->getOperand(0); - const LAllocation* src2 = math->getOperand(1); - const LDefinition* output = math->getDef(0); - - switch (math->jsop()) { - case JSOP_ADD: - masm.as_addd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_SUB: - masm.as_subd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_MUL: - masm.as_muld(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_DIV: - masm.as_divd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - default: - MOZ_CRASH("unexpected opcode"); - } -} - -void -CodeGeneratorMIPS::visitMathF(LMathF* math) -{ - const LAllocation* src1 = math->getOperand(0); - const LAllocation* src2 = math->getOperand(1); - const LDefinition* output = math->getDef(0); - - switch (math->jsop()) { - case JSOP_ADD: - masm.as_adds(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_SUB: - masm.as_subs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_MUL: - masm.as_muls(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_DIV: - masm.as_divs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - default: - MOZ_CRASH("unexpected opcode"); - } -} - -void -CodeGeneratorMIPS::visitFloor(LFloor* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister scratch = ScratchDoubleReg; - Register output = ToRegister(lir->output()); - - Label skipCheck, done; - - // If Nan, 0 or -0 check for bailout - masm.loadConstantDouble(0.0, scratch); - masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); - - // If high part is not zero, it is NaN or -0, so we bail. - masm.moveFromDoubleHi(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&skipCheck); - masm.as_floorwd(scratch, input); - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitFloorF(LFloorF* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister scratch = ScratchFloat32Reg; - Register output = ToRegister(lir->output()); - - Label skipCheck, done; - - // If Nan, 0 or -0 check for bailout - masm.loadConstantFloat32(0.0, scratch); - masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); - - // If binary value is not zero, it is NaN or -0, so we bail. - masm.moveFromDoubleLo(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&skipCheck); - masm.as_floorws(scratch, input); - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitCeil(LCeil* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister scratch = ScratchDoubleReg; - Register output = ToRegister(lir->output()); - - Label performCeil, done; - - // If x < -1 or x > 0 then perform ceil. - masm.loadConstantDouble(0, scratch); - masm.branchDouble(Assembler::DoubleGreaterThan, input, scratch, &performCeil); - masm.loadConstantDouble(-1, scratch); - masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, scratch, &performCeil); - - // If high part is not zero, the input was not 0, so we bail. - masm.moveFromDoubleHi(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&performCeil); - masm.as_ceilwd(scratch, input); - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitCeilF(LCeilF* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister scratch = ScratchFloat32Reg; - Register output = ToRegister(lir->output()); - - Label performCeil, done; - - // If x < -1 or x > 0 then perform ceil. - masm.loadConstantFloat32(0, scratch); - masm.branchFloat(Assembler::DoubleGreaterThan, input, scratch, &performCeil); - masm.loadConstantFloat32(-1, scratch); - masm.branchFloat(Assembler::DoubleLessThanOrEqual, input, scratch, &performCeil); - - // If binary value is not zero, the input was not 0, so we bail. - masm.moveFromFloat32(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&performCeil); - masm.as_ceilws(scratch, input); - masm.moveFromFloat32(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitRound(LRound* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister temp = ToFloatRegister(lir->temp()); - FloatRegister scratch = ScratchDoubleReg; - Register output = ToRegister(lir->output()); - - Label bail, negative, end, skipCheck; - - // Load 0.5 in the temp register. - masm.loadConstantDouble(0.5, temp); - - // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. - masm.loadConstantDouble(0.0, scratch); - masm.ma_bc1d(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); - - // If Nan, 0 or -0 check for bailout - masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); - - // If high part is not zero, it is NaN or -0, so we bail. - masm.moveFromDoubleHi(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&end, ShortJump); - - masm.bind(&skipCheck); - masm.loadConstantDouble(0.5, scratch); - masm.addDouble(input, scratch); - masm.as_floorwd(scratch, scratch); - - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.jump(&end); - - // Input is negative, but isn't -0. - masm.bind(&negative); - masm.addDouble(input, temp); - - // If input + 0.5 >= 0, input is a negative number >= -0.5 and the - // result is -0. - masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); - bailoutFrom(&bail, lir->snapshot()); - - // Truncate and round toward zero. - // This is off-by-one for everything but integer-valued inputs. - masm.as_floorwd(scratch, temp); - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - - masm.bind(&end); -} - -void -CodeGeneratorMIPS::visitRoundF(LRoundF* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister temp = ToFloatRegister(lir->temp()); - FloatRegister scratch = ScratchFloat32Reg; - Register output = ToRegister(lir->output()); - - Label bail, negative, end, skipCheck; - - // Load 0.5 in the temp register. - masm.loadConstantFloat32(0.5, temp); - - // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. - masm.loadConstantFloat32(0.0, scratch); - masm.ma_bc1s(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); - - // If Nan, 0 or -0 check for bailout - masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); - - // If binary value is not zero, it is NaN or -0, so we bail. - masm.moveFromFloat32(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&end, ShortJump); - - masm.bind(&skipCheck); - masm.loadConstantFloat32(0.5, scratch); - masm.as_adds(scratch, input, scratch); - masm.as_floorws(scratch, scratch); - - masm.moveFromFloat32(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.jump(&end); - - // Input is negative, but isn't -0. - masm.bind(&negative); - masm.as_adds(temp, input, temp); - - // If input + 0.5 >= 0, input is a negative number >= -0.5 and the - // result is -0. - masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); - bailoutFrom(&bail, lir->snapshot()); - - // Truncate and round toward zero. - // This is off-by-one for everything but integer-valued inputs. - masm.as_floorws(scratch, temp); - masm.moveFromFloat32(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - - masm.bind(&end); -} - -void -CodeGeneratorMIPS::visitTruncateDToInt32(LTruncateDToInt32* ins) -{ - emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()), - ins->mir()); -} - -void -CodeGeneratorMIPS::visitTruncateFToInt32(LTruncateFToInt32* ins) -{ - emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()), - ins->mir()); -} - static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 }; FrameSizeClass @@ -1375,14 +150,6 @@ CodeGeneratorMIPS::ToTempValue(LInstruction* ins, size_t pos) return ValueOperand(typeReg, payloadReg); } -void -CodeGeneratorMIPS::visitValue(LValue* value) -{ - const ValueOperand out = ToOutValue(value); - - masm.moveValue(value->value(), out); -} - void CodeGeneratorMIPS::visitBox(LBox* box) { @@ -1425,129 +192,12 @@ CodeGeneratorMIPS::visitUnbox(LUnbox* unbox) } } -void -CodeGeneratorMIPS::visitDouble(LDouble* ins) -{ - const LDefinition* out = ins->getDef(0); - - masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out)); -} - -void -CodeGeneratorMIPS::visitFloat32(LFloat32* ins) -{ - const LDefinition* out = ins->getDef(0); - masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out)); -} - Register CodeGeneratorMIPS::splitTagForTest(const ValueOperand& value) { return value.typeReg(); } -void -CodeGeneratorMIPS::visitTestDAndBranch(LTestDAndBranch* test) -{ - FloatRegister input = ToFloatRegister(test->input()); - - MBasicBlock* ifTrue = test->ifTrue(); - MBasicBlock* ifFalse = test->ifFalse(); - - masm.loadConstantDouble(0.0, ScratchDoubleReg); - // If 0, or NaN, the result is false. - - if (isNextBlock(ifFalse->lir())) { - branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifTrue, - Assembler::DoubleNotEqual); - } else { - branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifFalse, - Assembler::DoubleEqualOrUnordered); - jumpToBlock(ifTrue); - } -} - -void -CodeGeneratorMIPS::visitTestFAndBranch(LTestFAndBranch* test) -{ - FloatRegister input = ToFloatRegister(test->input()); - - MBasicBlock* ifTrue = test->ifTrue(); - MBasicBlock* ifFalse = test->ifFalse(); - - masm.loadConstantFloat32(0.0, ScratchFloat32Reg); - // If 0, or NaN, the result is false. - - if (isNextBlock(ifFalse->lir())) { - branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifTrue, - Assembler::DoubleNotEqual); - } else { - branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifFalse, - Assembler::DoubleEqualOrUnordered); - jumpToBlock(ifTrue); - } -} - -void -CodeGeneratorMIPS::visitCompareD(LCompareD* comp) -{ - FloatRegister lhs = ToFloatRegister(comp->left()); - FloatRegister rhs = ToFloatRegister(comp->right()); - Register dest = ToRegister(comp->output()); - - Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); - masm.ma_cmp_set_double(dest, lhs, rhs, cond); -} - -void -CodeGeneratorMIPS::visitCompareF(LCompareF* comp) -{ - FloatRegister lhs = ToFloatRegister(comp->left()); - FloatRegister rhs = ToFloatRegister(comp->right()); - Register dest = ToRegister(comp->output()); - - Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); - masm.ma_cmp_set_float32(dest, lhs, rhs, cond); -} - -void -CodeGeneratorMIPS::visitCompareDAndBranch(LCompareDAndBranch* comp) -{ - FloatRegister lhs = ToFloatRegister(comp->left()); - FloatRegister rhs = ToFloatRegister(comp->right()); - - Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); - MBasicBlock* ifTrue = comp->ifTrue(); - MBasicBlock* ifFalse = comp->ifFalse(); - - if (isNextBlock(ifFalse->lir())) { - branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifTrue, cond); - } else { - branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifFalse, - Assembler::InvertCondition(cond)); - jumpToBlock(ifTrue); - } -} - -void -CodeGeneratorMIPS::visitCompareFAndBranch(LCompareFAndBranch* comp) -{ - FloatRegister lhs = ToFloatRegister(comp->left()); - FloatRegister rhs = ToFloatRegister(comp->right()); - - Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); - MBasicBlock* ifTrue = comp->ifTrue(); - MBasicBlock* ifFalse = comp->ifFalse(); - - if (isNextBlock(ifFalse->lir())) { - branchToBlock(Assembler::SingleFloat, lhs, rhs, ifTrue, cond); - } else { - branchToBlock(Assembler::SingleFloat, lhs, rhs, ifFalse, - Assembler::InvertCondition(cond)); - jumpToBlock(ifTrue); - } -} - void CodeGeneratorMIPS::visitCompareB(LCompareB* lir) { @@ -1640,484 +290,6 @@ CodeGeneratorMIPS::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir) emitBranch(lhs.payloadReg(), rhs.payloadReg(), cond, lir->ifTrue(), lir->ifFalse()); } -void -CodeGeneratorMIPS::visitBitAndAndBranch(LBitAndAndBranch* lir) -{ - if (lir->right()->isConstant()) - masm.ma_and(ScratchRegister, ToRegister(lir->left()), Imm32(ToInt32(lir->right()))); - else - masm.as_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right())); - emitBranch(ScratchRegister, ScratchRegister, Assembler::NonZero, lir->ifTrue(), - lir->ifFalse()); -} - -void -CodeGeneratorMIPS::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir) -{ - masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output())); -} - -void -CodeGeneratorMIPS::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir) -{ - masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output())); -} - -void -CodeGeneratorMIPS::visitNotI(LNotI* ins) -{ - masm.cmp32Set(Assembler::Equal, ToRegister(ins->input()), Imm32(0), - ToRegister(ins->output())); -} - -void -CodeGeneratorMIPS::visitNotD(LNotD* ins) -{ - // Since this operation is not, we want to set a bit if - // the double is falsey, which means 0.0, -0.0 or NaN. - FloatRegister in = ToFloatRegister(ins->input()); - Register dest = ToRegister(ins->output()); - - Label falsey, done; - masm.loadConstantDouble(0.0, ScratchDoubleReg); - masm.ma_bc1d(in, ScratchDoubleReg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); - - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - - masm.bind(&falsey); - masm.move32(Imm32(1), dest); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitNotF(LNotF* ins) -{ - // Since this operation is not, we want to set a bit if - // the float32 is falsey, which means 0.0, -0.0 or NaN. - FloatRegister in = ToFloatRegister(ins->input()); - Register dest = ToRegister(ins->output()); - - Label falsey, done; - masm.loadConstantFloat32(0.0, ScratchFloat32Reg); - masm.ma_bc1s(in, ScratchFloat32Reg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); - - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - - masm.bind(&falsey); - masm.move32(Imm32(1), dest); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitGuardShape(LGuardShape* guard) -{ - Register obj = ToRegister(guard->input()); - Register tmp = ToRegister(guard->tempInt()); - - masm.loadPtr(Address(obj, JSObject::offsetOfShape()), tmp); - bailoutCmpPtr(Assembler::NotEqual, tmp, ImmGCPtr(guard->mir()->shape()), - guard->snapshot()); -} - -void -CodeGeneratorMIPS::visitGuardObjectGroup(LGuardObjectGroup* guard) -{ - Register obj = ToRegister(guard->input()); - Register tmp = ToRegister(guard->tempInt()); - MOZ_ASSERT(obj != tmp); - - masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp); - Assembler::Condition cond = guard->mir()->bailOnEquality() - ? Assembler::Equal - : Assembler::NotEqual; - bailoutCmpPtr(cond, tmp, ImmGCPtr(guard->mir()->group()), guard->snapshot()); -} - -void -CodeGeneratorMIPS::visitGuardClass(LGuardClass* guard) -{ - Register obj = ToRegister(guard->input()); - Register tmp = ToRegister(guard->tempInt()); - - masm.loadObjClass(obj, tmp); - bailoutCmpPtr(Assembler::NotEqual, tmp, Imm32((uint32_t)guard->mir()->getClass()), - guard->snapshot()); -} - -void -CodeGeneratorMIPS::generateInvalidateEpilogue() -{ - // Ensure that there is enough space in the buffer for the OsiPoint - // patching to occur. Otherwise, we could overwrite the invalidation - // epilogue. - for (size_t i = 0; i < sizeof(void*); i += Assembler::NopSize()) - masm.nop(); - - masm.bind(&invalidate_); - - // Push the return address of the point that we bailed out at to the stack - masm.Push(ra); - - // Push the Ion script onto the stack (when we determine what that - // pointer is). - invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1))); - JitCode* thunk = gen->jitRuntime()->getInvalidationThunk(); - - masm.branch(thunk); - - // We should never reach this point in JIT code -- the invalidation thunk - // should pop the invalidated JS frame and return directly to its caller. - masm.assumeUnreachable("Should have returned directly to its caller instead of here."); -} - -void -CodeGeneratorMIPS::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGeneratorMIPS::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGeneratorMIPS::visitAsmJSCall(LAsmJSCall* ins) -{ - emitAsmJSCall(ins); -} - -void -CodeGeneratorMIPS::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) -{ - const MAsmJSLoadHeap* mir = ins->mir(); - const LAllocation* ptr = ins->ptr(); - const LDefinition* out = ins->output(); - - bool isSigned; - int size; - bool isFloat = false; - switch (mir->accessType()) { - case Scalar::Int8: isSigned = true; size = 8; break; - case Scalar::Uint8: isSigned = false; size = 8; break; - case Scalar::Int16: isSigned = true; size = 16; break; - case Scalar::Uint16: isSigned = false; size = 16; break; - case Scalar::Int32: isSigned = true; size = 32; break; - case Scalar::Uint32: isSigned = false; size = 32; break; - case Scalar::Float64: isFloat = true; size = 64; break; - case Scalar::Float32: isFloat = true; size = 32; break; - default: MOZ_CRASH("unexpected array type"); - } - - if (ptr->isConstant()) { - MOZ_ASSERT(!mir->needsBoundsCheck()); - int32_t ptrImm = ptr->toConstant()->toInt32(); - MOZ_ASSERT(ptrImm >= 0); - if (isFloat) { - if (size == 32) { - masm.loadFloat32(Address(HeapReg, ptrImm), ToFloatRegister(out)); - } else { - masm.loadDouble(Address(HeapReg, ptrImm), ToFloatRegister(out)); - } - } else { - masm.ma_load(ToRegister(out), Address(HeapReg, ptrImm), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - return; - } - - Register ptrReg = ToRegister(ptr); - - if (!mir->needsBoundsCheck()) { - if (isFloat) { - if (size == 32) { - masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); - } else { - masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); - } - } else { - masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - return; - } - - BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); - - Label done, outOfRange; - masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump); - // Offset is ok, let's load value. - if (isFloat) { - if (size == 32) - masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); - else - masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); - } else { - masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - masm.ma_b(&done, ShortJump); - masm.bind(&outOfRange); - // Offset is out of range. Load default values. - if (isFloat) { - if (size == 32) - masm.loadFloat32(Address(GlobalReg, AsmJSNaN32GlobalDataOffset - AsmJSGlobalRegBias), - ToFloatRegister(out)); - else - masm.loadDouble(Address(GlobalReg, AsmJSNaN64GlobalDataOffset - AsmJSGlobalRegBias), - ToFloatRegister(out)); - } else { - if (mir->isAtomicAccess()) - masm.ma_b(gen->outOfBoundsLabel()); - else - masm.move32(Imm32(0), ToRegister(out)); - } - masm.bind(&done); - - masm.append(AsmJSHeapAccess(bo.getOffset())); -} - -void -CodeGeneratorMIPS::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) -{ - const MAsmJSStoreHeap* mir = ins->mir(); - const LAllocation* value = ins->value(); - const LAllocation* ptr = ins->ptr(); - - bool isSigned; - int size; - bool isFloat = false; - switch (mir->accessType()) { - case Scalar::Int8: isSigned = true; size = 8; break; - case Scalar::Uint8: isSigned = false; size = 8; break; - case Scalar::Int16: isSigned = true; size = 16; break; - case Scalar::Uint16: isSigned = false; size = 16; break; - case Scalar::Int32: isSigned = true; size = 32; break; - case Scalar::Uint32: isSigned = false; size = 32; break; - case Scalar::Float64: isFloat = true; size = 64; break; - case Scalar::Float32: isFloat = true; size = 32; break; - default: MOZ_CRASH("unexpected array type"); - } - - if (ptr->isConstant()) { - MOZ_ASSERT(!mir->needsBoundsCheck()); - int32_t ptrImm = ptr->toConstant()->toInt32(); - MOZ_ASSERT(ptrImm >= 0); - - if (isFloat) { - if (size == 32) { - masm.storeFloat32(ToFloatRegister(value), Address(HeapReg, ptrImm)); - } else { - masm.storeDouble(ToFloatRegister(value), Address(HeapReg, ptrImm)); - } - } else { - masm.ma_store(ToRegister(value), Address(HeapReg, ptrImm), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - return; - } - - Register ptrReg = ToRegister(ptr); - Address dstAddr(ptrReg, 0); - - if (!mir->needsBoundsCheck()) { - if (isFloat) { - if (size == 32) { - masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); - } else - masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); - } else { - masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - return; - } - - BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); - - Label done, outOfRange; - masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump); - - // Offset is ok, let's store value. - if (isFloat) { - if (size == 32) { - masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); - } else - masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); - } else { - masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - masm.ma_b(&done, ShortJump); - masm.bind(&outOfRange); - // Offset is out of range. - if (mir->isAtomicAccess()) - masm.ma_b(gen->outOfBoundsLabel()); - masm.bind(&done); - - masm.append(AsmJSHeapAccess(bo.getOffset())); -} - -void -CodeGeneratorMIPS::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGeneratorMIPS::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGeneratorMIPS::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins) -{ - const MAsmJSPassStackArg* mir = ins->mir(); - if (ins->arg()->isConstant()) { - masm.storePtr(ImmWord(ToInt32(ins->arg())), Address(StackPointer, mir->spOffset())); - } else { - if (ins->arg()->isGeneralReg()) { - masm.storePtr(ToRegister(ins->arg()), Address(StackPointer, mir->spOffset())); - } else { - masm.storeDouble(ToFloatRegister(ins->arg()).doubleOverlay(0), - Address(StackPointer, mir->spOffset())); - } - } -} - -void -CodeGeneratorMIPS::visitUDivOrMod(LUDivOrMod* ins) -{ - Register lhs = ToRegister(ins->lhs()); - Register rhs = ToRegister(ins->rhs()); - Register output = ToRegister(ins->output()); - Label done; - - // Prevent divide by zero. - if (ins->canBeDivideByZero()) { - if (ins->mir()->isTruncated()) { - // Infinity|0 == 0 - Label notzero; - masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - masm.bind(¬zero); - } else { - bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); - } - } - - masm.as_divu(lhs, rhs); - masm.as_mfhi(output); - - // If the remainder is > 0, bailout since this must be a double. - if (ins->mir()->isDiv()) { - if (!ins->mir()->toDiv()->canTruncateRemainder()) - bailoutCmp32(Assembler::NonZero, output, output, ins->snapshot()); - // Get quotient - masm.as_mflo(output); - } - - if (!ins->mir()->isTruncated()) - bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitEffectiveAddress(LEffectiveAddress* ins) -{ - const MEffectiveAddress* mir = ins->mir(); - Register base = ToRegister(ins->base()); - Register index = ToRegister(ins->index()); - Register output = ToRegister(ins->output()); - - BaseIndex address(base, index, mir->scale(), mir->displacement()); - masm.computeEffectiveAddress(address, output); -} - -void -CodeGeneratorMIPS::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) -{ - const MAsmJSLoadGlobalVar* mir = ins->mir(); - unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; - if (mir->type() == MIRType_Int32) - masm.load32(Address(GlobalReg, addr), ToRegister(ins->output())); - else if (mir->type() == MIRType_Float32) - masm.loadFloat32(Address(GlobalReg, addr), ToFloatRegister(ins->output())); - else - masm.loadDouble(Address(GlobalReg, addr), ToFloatRegister(ins->output())); -} - -void -CodeGeneratorMIPS::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins) -{ - const MAsmJSStoreGlobalVar* mir = ins->mir(); - - MOZ_ASSERT(IsNumberType(mir->value()->type())); - unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; - if (mir->value()->type() == MIRType_Int32) - masm.store32(ToRegister(ins->value()), Address(GlobalReg, addr)); - else if (mir->value()->type() == MIRType_Float32) - masm.storeFloat32(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); - else - masm.storeDouble(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); -} - -void -CodeGeneratorMIPS::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins) -{ - const MAsmJSLoadFuncPtr* mir = ins->mir(); - - Register index = ToRegister(ins->index()); - Register out = ToRegister(ins->output()); - unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; - - BaseIndex source(GlobalReg, index, TimesFour, addr); - masm.load32(source, out); -} - -void -CodeGeneratorMIPS::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins) -{ - const MAsmJSLoadFFIFunc* mir = ins->mir(); - masm.loadPtr(Address(GlobalReg, mir->globalDataOffset() - AsmJSGlobalRegBias), - ToRegister(ins->output())); -} - -void -CodeGeneratorMIPS::visitNegI(LNegI* ins) -{ - Register input = ToRegister(ins->input()); - Register output = ToRegister(ins->output()); - - masm.ma_negu(output, input); -} - -void -CodeGeneratorMIPS::visitNegD(LNegD* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - - masm.as_negd(output, input); -} - -void -CodeGeneratorMIPS::visitNegF(LNegF* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - - masm.as_negs(output, input); -} - void CodeGeneratorMIPS::setReturnDoubleRegs(LiveRegisterSet* regs) { diff --git a/js/src/jit/mips32/CodeGenerator-mips32.h b/js/src/jit/mips32/CodeGenerator-mips32.h index 1e56d0ade080..329c03efbfb3 100644 --- a/js/src/jit/mips32/CodeGenerator-mips32.h +++ b/js/src/jit/mips32/CodeGenerator-mips32.h @@ -7,114 +7,14 @@ #ifndef jit_mips32_CodeGenerator_mips32_h #define jit_mips32_CodeGenerator_mips32_h -#include "jit/mips32/Assembler-mips32.h" -#include "jit/shared/CodeGenerator-shared.h" +#include "jit/mips-shared/CodeGenerator-mips-shared.h" namespace js { namespace jit { -class OutOfLineBailout; -class OutOfLineTableSwitch; - -class CodeGeneratorMIPS : public CodeGeneratorShared +class CodeGeneratorMIPS : public CodeGeneratorMIPSShared { - friend class MoveResolverMIPS; - - CodeGeneratorMIPS* thisFromCtor() { - return this; - } - protected: - NonAssertingLabel deoptLabel_; - - inline Address ToAddress(const LAllocation& a); - inline Address ToAddress(const LAllocation* a); - - MoveOperand toMoveOperand(LAllocation a) const; - - template - void bailoutCmp32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { - Label skip; - masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(c), ShortJump); - bailout(snapshot); - masm.bind(&skip); - } - template - void bailoutCmp32(Assembler::Condition c, Operand lhs, T rhs, LSnapshot* snapshot) { - if (lhs.getTag() == Operand::REG) - bailoutCmp32(c, lhs.toReg(), rhs, snapshot); - else if (lhs.getTag() == Operand::MEM) - bailoutCmp32(c, lhs.toAddress(), rhs, snapshot); - else - MOZ_CRASH("Invalid operand tag."); - } - template - void bailoutTest32(Assembler::Condition c, Register lhs, T rhs, LSnapshot* snapshot) { - Label bail; - masm.branchTest32(c, lhs, rhs, &bail); - bailoutFrom(&bail, snapshot); - } - template - void bailoutCmpPtr(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { - bailoutCmp32(c, lhs, rhs, snapshot); - } - void bailoutTestPtr(Assembler::Condition c, Register lhs, Register rhs, LSnapshot* snapshot) { - Label bail; - masm.branchTestPtr(c, lhs, rhs, &bail); - bailoutFrom(&bail, snapshot); - } - void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) { - Label bail; - masm.branchTest32(Assembler::Zero, reg, Imm32(0xFF), &bail); - bailoutFrom(&bail, snapshot); - } - - void bailoutFrom(Label* label, LSnapshot* snapshot); - void bailout(LSnapshot* snapshot); - - protected: - bool generateOutOfLineCode(); - - template - void branchToBlock(Register lhs, T rhs, MBasicBlock* mir, Assembler::Condition cond) - { - mir = skipTrivialBlocks(mir); - - Label* label = mir->lir()->label(); - if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) { - // Note: the backedge is initially a jump to the next instruction. - // It will be patched to the target block's label during link(). - RepatchLabel rejoin; - CodeOffsetJump backedge; - Label skip; - - masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); - backedge = masm.backedgeJump(&rejoin); - masm.bind(&rejoin); - masm.bind(&skip); - - if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) - MOZ_CRASH(); - } else { - masm.ma_b(lhs, rhs, label, cond); - } - } - void branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, - MBasicBlock* mir, Assembler::DoubleCondition cond); - - // Emits a branch that directs control flow to the true block if |cond| is - // true, and the false block if |cond| is false. - template - void emitBranch(Register lhs, T rhs, Assembler::Condition cond, - MBasicBlock* mirTrue, MBasicBlock* mirFalse) - { - if (isNextBlock(mirFalse->lir())) { - branchToBlock(lhs, rhs, mirTrue, cond); - } else { - branchToBlock(lhs, rhs, mirFalse, Assembler::InvertCondition(cond)); - jumpToBlock(mirTrue); - } - } void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value, MBasicBlock* ifTrue, MBasicBlock* ifFalse) { @@ -130,75 +30,17 @@ class CodeGeneratorMIPS : public CodeGeneratorShared { emitBranch(value.typeReg(), (Imm32)ImmType(JSVAL_TYPE_OBJECT), cond, ifTrue, ifFalse); } - void testZeroEmitBranch(Assembler::Condition cond, Register reg, - MBasicBlock* ifTrue, MBasicBlock* ifFalse) - { - emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse); - } void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base); public: - // Instruction visitors. - virtual void visitMinMaxD(LMinMaxD* ins); - virtual void visitMinMaxF(LMinMaxF* ins); - virtual void visitAbsD(LAbsD* ins); - virtual void visitAbsF(LAbsF* ins); - virtual void visitSqrtD(LSqrtD* ins); - virtual void visitSqrtF(LSqrtF* ins); - virtual void visitAddI(LAddI* ins); - virtual void visitSubI(LSubI* ins); - virtual void visitBitNotI(LBitNotI* ins); - virtual void visitBitOpI(LBitOpI* ins); - - virtual void visitMulI(LMulI* ins); - - virtual void visitDivI(LDivI* ins); - virtual void visitDivPowTwoI(LDivPowTwoI* ins); - virtual void visitModI(LModI* ins); - virtual void visitModPowTwoI(LModPowTwoI* ins); - virtual void visitModMaskI(LModMaskI* ins); - virtual void visitPowHalfD(LPowHalfD* ins); - virtual void visitShiftI(LShiftI* ins); - virtual void visitUrshD(LUrshD* ins); - - virtual void visitClzI(LClzI* ins); - - virtual void visitTestIAndBranch(LTestIAndBranch* test); - virtual void visitCompare(LCompare* comp); - virtual void visitCompareAndBranch(LCompareAndBranch* comp); - virtual void visitTestDAndBranch(LTestDAndBranch* test); - virtual void visitTestFAndBranch(LTestFAndBranch* test); - virtual void visitCompareD(LCompareD* comp); - virtual void visitCompareF(LCompareF* comp); - virtual void visitCompareDAndBranch(LCompareDAndBranch* comp); - virtual void visitCompareFAndBranch(LCompareFAndBranch* comp); virtual void visitCompareB(LCompareB* lir); virtual void visitCompareBAndBranch(LCompareBAndBranch* lir); virtual void visitCompareBitwise(LCompareBitwise* lir); virtual void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir); - virtual void visitBitAndAndBranch(LBitAndAndBranch* lir); - virtual void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir); - virtual void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir); - virtual void visitNotI(LNotI* ins); - virtual void visitNotD(LNotD* ins); - virtual void visitNotF(LNotF* ins); - - virtual void visitMathD(LMathD* math); - virtual void visitMathF(LMathF* math); - virtual void visitFloor(LFloor* lir); - virtual void visitFloorF(LFloorF* lir); - virtual void visitCeil(LCeil* lir); - virtual void visitCeilF(LCeilF* lir); - virtual void visitRound(LRound* lir); - virtual void visitRoundF(LRoundF* lir); - virtual void visitTruncateDToInt32(LTruncateDToInt32* ins); - virtual void visitTruncateFToInt32(LTruncateFToInt32* ins); // Out of line visitors. - void visitOutOfLineBailout(OutOfLineBailout* ool); void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool); - protected: ValueOperand ToValue(LInstruction* ins, size_t pos); ValueOperand ToOutValue(LInstruction* ins); @@ -208,84 +50,19 @@ class CodeGeneratorMIPS : public CodeGeneratorShared Register splitTagForTest(const ValueOperand& value); public: - CodeGeneratorMIPS(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm); + CodeGeneratorMIPS(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) + : CodeGeneratorMIPSShared(gen, graph, masm) + { } public: void visitBox(LBox* box); void visitBoxFloatingPoint(LBoxFloatingPoint* box); void visitUnbox(LUnbox* unbox); - void visitValue(LValue* value); - void visitDouble(LDouble* ins); - void visitFloat32(LFloat32* ins); - - void visitGuardShape(LGuardShape* guard); - void visitGuardObjectGroup(LGuardObjectGroup* guard); - void visitGuardClass(LGuardClass* guard); - - void visitNegI(LNegI* lir); - void visitNegD(LNegD* lir); - void visitNegF(LNegF* lir); - void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins); - void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins); - void visitAsmJSCall(LAsmJSCall* ins); - void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins); - void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins); - void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins); - void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins); - void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins); - void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins); - void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins); - void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins); - - void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins); - - void generateInvalidateEpilogue(); - void setReturnDoubleRegs(LiveRegisterSet* regs); - - protected: - void visitEffectiveAddress(LEffectiveAddress* ins); - void visitUDivOrMod(LUDivOrMod* ins); - - public: - // Unimplemented SIMD instructions - void visitSimdSplatX4(LSimdSplatX4* lir) { MOZ_CRASH("NYI"); } - void visitInt32x4(LInt32x4* ins) { MOZ_CRASH("NYI"); } - void visitFloat32x4(LFloat32x4* ins) { MOZ_CRASH("NYI"); } - void visitSimdReinterpretCast(LSimdReinterpretCast* ins) { MOZ_CRASH("NYI"); } - void visitSimdExtractElementI(LSimdExtractElementI* ins) { MOZ_CRASH("NYI"); } - void visitSimdExtractElementF(LSimdExtractElementF* ins) { MOZ_CRASH("NYI"); } - void visitSimdSignMaskX4(LSimdSignMaskX4* ins) { MOZ_CRASH("NYI"); } - void visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir) { MOZ_CRASH("NYI"); } - void visitSimdGeneralShuffleI(LSimdGeneralShuffleI* lir) { MOZ_CRASH("NYI"); } - void visitSimdGeneralShuffleF(LSimdGeneralShuffleF* lir) { MOZ_CRASH("NYI"); } }; typedef CodeGeneratorMIPS CodeGeneratorSpecific; -// An out-of-line bailout thunk. -class OutOfLineBailout : public OutOfLineCodeBase -{ - LSnapshot* snapshot_; - uint32_t frameSize_; - - public: - OutOfLineBailout(LSnapshot* snapshot, uint32_t frameSize) - : snapshot_(snapshot), - frameSize_(frameSize) - { } - - void accept(CodeGeneratorMIPS* codegen); - - LSnapshot* snapshot() const { - return snapshot_; - } -}; - } // namespace jit } // namespace js diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index c151d17efa76..c7c01e50d80e 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -184,8 +184,12 @@ LIRGeneratorShared::defineSinCos(LInstructionHelper<2, Ops, Temps> *lir, MDefini uint32_t vreg = getVirtualRegister(); lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnDoubleReg))); -#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) - lir->setDef(1, LDefinition(vreg + VREG_INCREMENT, LDefinition::DOUBLE, LFloatReg(d1))); +#if defined(JS_CODEGEN_ARM) + lir->setDef(1, LDefinition(vreg + VREG_INCREMENT, LDefinition::DOUBLE, + LFloatReg(FloatRegister(FloatRegisters::d1, FloatRegister::Double)))); +#elif defined(JS_CODEGEN_ARM64) + lir->setDef(1, LDefinition(vreg + VREG_INCREMENT, LDefinition::DOUBLE, + LFloatReg(FloatRegister(FloatRegisters::d1, FloatRegisters::Double)))); #elif defined(JS_CODEGEN_MIPS32) lir->setDef(1, LDefinition(vreg + VREG_INCREMENT, LDefinition::DOUBLE, LFloatReg(f2))); #elif defined(JS_CODEGEN_NONE) diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 780d71b020a7..d1acac67e31c 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -392,7 +392,7 @@ class Assembler : public AssemblerX86Shared } void vhaddpd(FloatRegister src, FloatRegister dest) { - MOZ_ASSERT(HasSSE2()); + MOZ_ASSERT(HasSSE3()); MOZ_ASSERT(src.size() == 16); MOZ_ASSERT(dest.size() == 16); masm.vhaddpd_rr(src.encoding(), dest.encoding()); diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index b7f913a39fc4..6c2b3a49cd89 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -43,6 +43,7 @@ UNIFIED_SOURCES += [ 'testGCMarking.cpp', 'testGCOutOfMemory.cpp', 'testGCStoreBufferRemoval.cpp', + 'testGCUniqueId.cpp', 'testGetPropertyDescriptor.cpp', 'testHashTable.cpp', 'testIndexToString.cpp', diff --git a/js/src/jsapi-tests/testGCUniqueId.cpp b/js/src/jsapi-tests/testGCUniqueId.cpp new file mode 100644 index 000000000000..6efc8167f732 --- /dev/null +++ b/js/src/jsapi-tests/testGCUniqueId.cpp @@ -0,0 +1,120 @@ +/* -*- 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/. */ + +#include "gc/GCInternals.h" +#include "gc/Zone.h" + +static void +MinimizeHeap(JSRuntime* rt) +{ + // The second collection is to force us to wait for the background + // sweeping that the first GC started to finish. + JS_GC(rt); + JS_GC(rt); + js::gc::AutoFinishGC finish(rt); +} + +BEGIN_TEST(testGCUID) +{ +#ifdef JS_GC_ZEAL + AutoLeaveZeal nozeal(cx); +#endif /* JS_GC_ZEAL */ + + uint64_t uid = 0; + uint64_t tmp = 0; + + // Ensure the heap is as minimal as it can get. + MinimizeHeap(rt); + + JS::RootedObject obj(cx, JS_NewPlainObject(cx)); + uintptr_t nurseryAddr = uintptr_t(obj.get()); + CHECK(obj); + CHECK(js::gc::IsInsideNursery(obj)); + + // Do not start with an ID. + CHECK(!obj->zone()->hasUniqueId(obj)); + + // Ensure we can get a new UID. + CHECK(obj->zone()->getUniqueId(obj, &uid)); + CHECK(uid > js::gc::LargestTaggedNullCellPointer); + + // We should now have an id. + CHECK(obj->zone()->hasUniqueId(obj)); + + // Calling again should get us the same thing. + CHECK(obj->zone()->getUniqueId(obj, &tmp)); + CHECK(uid == tmp); + + // Tenure the thing and check that the UID moved with it. + MinimizeHeap(rt); + uintptr_t tenuredAddr = uintptr_t(obj.get()); + CHECK(tenuredAddr != nurseryAddr); + CHECK(!js::gc::IsInsideNursery(obj)); + CHECK(obj->zone()->hasUniqueId(obj)); + CHECK(obj->zone()->getUniqueId(obj, &tmp)); + CHECK(uid == tmp); + + // Allocate a new nursery thing in the same location and check that we + // removed the prior uid that was attached to the location. + obj = JS_NewPlainObject(cx); + CHECK(obj); + CHECK(uintptr_t(obj.get()) == nurseryAddr); + CHECK(!obj->zone()->hasUniqueId(obj)); + + // Try to get another tenured object in the same location and check that + // the uid was removed correctly. + obj = nullptr; + MinimizeHeap(rt); + obj = JS_NewPlainObject(cx); + MinimizeHeap(rt); + CHECK(uintptr_t(obj.get()) == tenuredAddr); + CHECK(!obj->zone()->hasUniqueId(obj)); + CHECK(obj->zone()->getUniqueId(obj, &tmp)); + CHECK(uid != tmp); + uid = tmp; + + // Allocate a few arenas worth of objects to ensure we get some compaction. + const static size_t N = 2049; + using ObjectVector = js::TraceableVector; + JS::Rooted vec(cx, ObjectVector(cx)); + for (size_t i = 0; i < N; ++i) { + obj = JS_NewPlainObject(cx); + CHECK(obj); + CHECK(vec.append(obj)); + } + + // Transfer our vector to tenured if it isn't there already. + MinimizeHeap(rt); + + // Tear holes in the heap by unrooting the even objects and collecting. + JS::Rooted vec2(cx, ObjectVector(cx)); + for (size_t i = 0; i < N; ++i) { + if (i % 2 == 1) + vec2.append(vec[i]); + } + vec.clear(); + MinimizeHeap(rt); + + // Grab the last object in the vector as our object of interest. + obj = vec2.back(); + CHECK(obj); + tenuredAddr = uintptr_t(obj.get()); + CHECK(obj->zone()->getUniqueId(obj, &uid)); + + // Force a compaction to move the object and check that the uid moved to + // the new tenured heap location. + JS::PrepareForFullGC(rt); + JS::GCForReason(rt, GC_SHRINK, JS::gcreason::API); + MinimizeHeap(rt); + CHECK(uintptr_t(obj.get()) != tenuredAddr); + CHECK(obj->zone()->hasUniqueId(obj)); + CHECK(obj->zone()->getUniqueId(obj, &tmp)); + CHECK(uid == tmp); + + return true; +} +END_TEST(testGCUID) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 9a19d5485e02..aa2b0c562f37 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3830,6 +3830,7 @@ JS::TransitiveCompileOptions::copyPODTransitiveOptions(const TransitiveCompileOp extraWarningsOption = rhs.extraWarningsOption; werrorOption = rhs.werrorOption; asmJSOption = rhs.asmJSOption; + throwOnAsmJSValidationFailureOption = rhs.throwOnAsmJSValidationFailureOption; forceAsync = rhs.forceAsync; installedFile = rhs.installedFile; sourceIsLazy = rhs.sourceIsLazy; @@ -3952,6 +3953,7 @@ JS::CompileOptions::CompileOptions(JSContext* cx, JSVersion version) extraWarningsOption = cx->compartment()->options().extraWarnings(cx); werrorOption = cx->runtime()->options().werror(); asmJSOption = cx->runtime()->options().asmJS(); + throwOnAsmJSValidationFailureOption = cx->runtime()->options().throwOnAsmJSValidationFailure(); } enum SyntacticScopeOption { HasSyntacticScope, HasNonSyntacticScope }; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 739f7473ad89..6ac756166442 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1151,6 +1151,7 @@ class JS_PUBLIC_API(RuntimeOptions) { : baseline_(true), ion_(true), asmJS_(true), + throwOnAsmJSValidationFailure_(false), nativeRegExp_(true), unboxedArrays_(false), asyncStack_(true), @@ -1191,6 +1192,16 @@ class JS_PUBLIC_API(RuntimeOptions) { return *this; } + bool throwOnAsmJSValidationFailure() const { return throwOnAsmJSValidationFailure_; } + RuntimeOptions& setThrowOnAsmJSValidationFailure(bool flag) { + throwOnAsmJSValidationFailure_ = flag; + return *this; + } + RuntimeOptions& toggleThrowOnAsmJSValidationFailure() { + throwOnAsmJSValidationFailure_ = !throwOnAsmJSValidationFailure_; + return *this; + } + bool nativeRegExp() const { return nativeRegExp_; } RuntimeOptions& setNativeRegExp(bool flag) { nativeRegExp_ = flag; @@ -1249,6 +1260,7 @@ class JS_PUBLIC_API(RuntimeOptions) { bool baseline_ : 1; bool ion_ : 1; bool asmJS_ : 1; + bool throwOnAsmJSValidationFailure_ : 1; bool nativeRegExp_ : 1; bool unboxedArrays_ : 1; bool asyncStack_ : 1; @@ -3361,6 +3373,7 @@ class JS_FRIEND_API(TransitiveCompileOptions) extraWarningsOption(false), werrorOption(false), asmJSOption(false), + throwOnAsmJSValidationFailureOption(false), forceAsync(false), installedFile(false), sourceIsLazy(false), @@ -3395,6 +3408,7 @@ class JS_FRIEND_API(TransitiveCompileOptions) bool extraWarningsOption; bool werrorOption; bool asmJSOption; + bool throwOnAsmJSValidationFailureOption; bool forceAsync; bool installedFile; // 'true' iff pre-compiling js file in packaged app bool sourceIsLazy; @@ -5386,9 +5400,12 @@ GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject * The same notes above about SavedFrame accessors applies here as well: cx * doesn't need to be in stack's compartment, and stack can be null, a * SavedFrame object, or a wrapper (CCW or Xray) around a SavedFrame object. + * + * Optional indent parameter specifies the number of white spaces to indent + * each line. */ extern JS_PUBLIC_API(bool) -BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp); +BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp, size_t indent = 0); } /* namespace JS */ diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index eab5fd9804a9..0cf8e06aa64b 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -498,7 +498,12 @@ js::ToAtom(ExclusiveContext* cx, typename MaybeRooted::HandleTyp if (str->isAtom()) return &str->asAtom(); - return AtomizeString(cx, str); + JSAtom* atom = AtomizeString(cx, str); + if (!atom && !allowGC) { + MOZ_ASSERT_IF(cx->isJSContext(), cx->asJSContext()->isThrowingOutOfMemory()); + cx->recoverFromOutOfMemory(); + } + return atom; } template JSAtom* diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 2d785f4141ac..0c5cc3e17584 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -833,9 +833,7 @@ Chunk::init(JSRuntime* rt) /* Initialize the chunk info. */ info.init(); - info.trailer.storeBuffer = nullptr; - info.trailer.location = ChunkLocationBitTenuredHeap; - info.trailer.runtime = rt; + new (&info.trailer) ChunkTrailer(rt); /* The rest of info fields are initialized in pickChunk. */ } @@ -1110,6 +1108,7 @@ GCRuntime::GCRuntime(JSRuntime* rt) : usage(nullptr), mMemProfiler(rt), maxMallocBytes(0), + nextCellUniqueId_(LargestTaggedNullCellPointer + 1), // Ensure disjoint from null tagged pointers. numArenasFreeCommitted(0), verifyPreData(nullptr), chunkAllocationSinceLastGC(false), @@ -2042,6 +2041,9 @@ RelocateCell(Zone* zone, TenuredCell* src, AllocKind thingKind, size_t thingSize // Copy source cell contents to destination. memcpy(dst, src, thingSize); + // Move any uid attached to the object. + src->zone()->transferUniqueId(dst, src); + if (IsObjectAllocKind(thingKind)) { JSObject* srcObj = static_cast(static_cast(src)); JSObject* dstObj = static_cast(static_cast(dst)); @@ -2173,7 +2175,6 @@ bool ArenaLists::relocateArenas(Zone* zone, ArenaHeader*& relocatedListOut, JS::gcreason::Reason reason, SliceBudget& sliceBudget, gcstats::Statistics& stats) { - // This is only called from the main thread while we are doing a GC, so // there is no need to lock. MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); @@ -3588,6 +3589,29 @@ GCRuntime::shouldReleaseObservedTypes() return releaseTypes; } +struct IsAboutToBeFinalizedFunctor { + template bool operator()(Cell** t) { + mozilla::DebugOnly prior = *t; + bool result = IsAboutToBeFinalizedUnbarriered(reinterpret_cast(t)); + // Sweep should not have to deal with moved pointers, since moving GC + // handles updating the UID table manually. + MOZ_ASSERT(*t == prior); + return result; + } +}; + +void +JS::Zone::sweepUniqueIds(js::FreeOp* fop) +{ + for (UniqueIdMap::Enum e(uniqueIds_); !e.empty(); e.popFront()) { + if (DispatchTraceKindTyped(IsAboutToBeFinalizedFunctor(), e.front().key()->getTraceKind(), + &e.front().mutableKey())) + { + e.removeFront(); + } + } +} + /* * It's simpler if we preserve the invariant that every zone has at least one * compartment. If we know we're deleting the entire zone, then @@ -5058,6 +5082,12 @@ GCRuntime::beginSweepingZoneGroup() zone->sweepBreakpoints(&fop); } } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_BREAKPOINT); + for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) + zone->sweepUniqueIds(&fop); + } } if (sweepingAtoms) { @@ -6741,6 +6771,9 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) // Merge other info in source's zone into target's zone. target->zone()->types.typeLifoAlloc.transferFrom(&source->zone()->types.typeLifoAlloc); + + // Ensure that we did not create any UIDs when running off-thread. + source->zone()->assertNoUniqueIdsInZone(); } void @@ -7150,6 +7183,9 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt) * Check that internal hash tables no longer have any pointers to things * that have been moved. */ + for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { + zone->checkUniqueIdTableAfterMovingGC(); + } for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { c->objectGroups.checkTablesAfterMovingGC(); c->checkInitialShapesTableAfterMovingGC(); @@ -7375,6 +7411,12 @@ JS::IsGenerationalGCEnabled(JSRuntime* rt) return rt->gc.isGenerationalGCEnabled(); } +uint64_t +js::gc::NextCellUniqueId(JSRuntime* rt) +{ + return rt->gc.nextCellUniqueId(); +} + namespace js { namespace gc { namespace MemInfo { diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index f4fba5d4aa1e..8b81d5acf6ae 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -76,6 +76,7 @@ JSObject::finalize(js::FreeOp* fop) MOZ_ASSERT(CurrentThreadCanAccessRuntime(fop->runtime())); } #endif + const js::Class* clasp = getClass(); if (clasp->finalize) clasp->finalize(fop, this); diff --git a/js/src/moz.build b/js/src/moz.build index 7f2ea6ce811c..24ab58746af0 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -481,6 +481,7 @@ elif CONFIG['JS_CODEGEN_MIPS32']: 'jit/mips-shared/Bailouts-mips-shared.cpp', 'jit/mips-shared/BaselineCompiler-mips-shared.cpp', 'jit/mips-shared/BaselineIC-mips-shared.cpp', + 'jit/mips-shared/CodeGenerator-mips-shared.cpp', 'jit/mips-shared/Lowering-mips-shared.cpp', 'jit/mips-shared/MoveEmitter-mips-shared.cpp', ] diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index ee297cb3e21f..dfae9253d79c 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -85,6 +85,7 @@ #include "jscompartmentinlines.h" #include "jsobjinlines.h" +#include "vm/ErrorObject-inl.h" #include "vm/Interpreter-inl.h" #include "vm/Stack-inl.h" @@ -714,6 +715,8 @@ Options(JSContext* cx, unsigned argc, Value* vp) JS::RuntimeOptionsRef(cx).toggleExtraWarnings(); else if (strcmp(opt.ptr(), "werror") == 0) JS::RuntimeOptionsRef(cx).toggleWerror(); + else if (strcmp(opt.ptr(), "throw_on_asmjs_validation_failure") == 0) + JS::RuntimeOptionsRef(cx).toggleThrowOnAsmJSValidationFailure(); else if (strcmp(opt.ptr(), "strict_mode") == 0) JS::RuntimeOptionsRef(cx).toggleStrictMode(); else { @@ -736,6 +739,10 @@ Options(JSContext* cx, unsigned argc, Value* vp) names = JS_sprintf_append(names, "%s%s", found ? "," : "", "werror"); found = true; } + if (names && oldRuntimeOptions.throwOnAsmJSValidationFailure()) { + names = JS_sprintf_append(names, "%s%s", found ? "," : "", "throw_on_asmjs_validation_failure"); + found = true; + } if (names && oldRuntimeOptions.strictMode()) { names = JS_sprintf_append(names, "%s%s", found ? "," : "", "strict_mode"); found = true; @@ -5132,6 +5139,41 @@ CreateLastWarningObject(JSContext* cx, JSErrorReport* report) return true; } +static bool +PrintStackTrace(JSContext* cx, HandleValue exn) +{ + if (!exn.isObject()) + return false; + + Maybe ac; + RootedObject exnObj(cx, &exn.toObject()); + if (IsCrossCompartmentWrapper(exnObj)) { + exnObj = UncheckedUnwrap(exnObj); + ac.emplace(cx, exnObj); + } + + // Ignore non-ErrorObject thrown by |throw| statement. + if (!exnObj->is()) + return true; + + RootedObject stackObj(cx, exnObj->as().stack()); + if (!stackObj) + return false; + + RootedString stackStr(cx); + if (!BuildStackString(cx, stackObj, &stackStr, 2)) + return false; + + UniquePtr stack(JS_EncodeStringToUTF8(cx, stackStr)); + if (!stack) + return false; + + fputs("Stack:\n", gErrFile); + fputs(stack.get(), gErrFile); + + return true; +} + void js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report) { @@ -5144,7 +5186,19 @@ js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* r savedExc.restore(); } + // Get exception object before printing and clearing exception. + RootedValue exn(cx); + if (JS_IsExceptionPending(cx)) + (void) JS_GetPendingException(cx, &exn); + gGotError = PrintError(cx, gErrFile, message, report, reportWarnings); + if (!exn.isUndefined()) { + JS::AutoSaveExceptionState savedExc(cx); + if (!PrintStackTrace(cx, exn)) + fputs("(Unable to print stack trace)\n", gOutFile); + savedExc.restore(); + } + if (report->exnType != JSEXN_NONE && !JSREPORT_IS_WARNING(report->flags)) { if (report->errorNumber == JSMSG_OUT_OF_MEMORY) { gExitCode = EXITCODE_OUT_OF_MEMORY; diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index f55cb24db7e6..66fc69449607 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -455,6 +455,7 @@ Debugger::getScriptFrameWithIter(JSContext* cx, AbstractFramePtr frame, const ScriptFrameIter* maybeIter, MutableHandleValue vp) { MOZ_ASSERT_IF(maybeIter, maybeIter->abstractFramePtr() == frame); + MOZ_ASSERT(!frame.script()->selfHosted()); FrameMap::AddPtr p = frames.lookupForAdd(frame); if (!p) { @@ -726,6 +727,10 @@ Debugger::slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame) if (cx->isThrowingOverRecursed() || cx->isThrowingOutOfMemory()) return JSTRAP_CONTINUE; + // The Debugger API mustn't muck with frames from self-hosted scripts. + if (frame.script()->selfHosted()) + return JSTRAP_CONTINUE; + RootedValue rval(cx); JSTrapStatus status = dispatchHook( cx, @@ -5265,8 +5270,9 @@ Debugger::observesScript(JSScript* script) const { if (!enabled) return false; - return observesGlobal(&script->global()) && (!script->selfHosted() || - SelfHostedFramesVisible()); + // Don't ever observe self-hosted scripts: the Debugger API can break + // self-hosted invariants. + return observesGlobal(&script->global()) && !script->selfHosted(); } /* static */ bool diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index 7d947e682a5b..3be957eb97c9 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -311,7 +311,8 @@ StatsZoneCallback(JSRuntime* rt, void* data, Zone* zone) zone->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &zStats.typePool, - &zStats.baselineStubsOptimized); + &zStats.baselineStubsOptimized, + &zStats.uniqueIdMap); } static void diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index 6063229f2d78..56eff1761b9f 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -737,7 +737,7 @@ GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject } JS_PUBLIC_API(bool) -BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp) +BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp, size_t indent) { js::StringBuffer sb(cx); @@ -764,7 +764,8 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp) asyncCause.set(cx->names().Async); js::RootedAtom name(cx, frame->getFunctionDisplayName()); - if ((asyncCause && (!sb.append(asyncCause) || !sb.append('*'))) + if ((indent && !sb.appendN(' ', indent)) + || (asyncCause && (!sb.append(asyncCause) || !sb.append('*'))) || (name && !sb.append(name)) || !sb.append('@') || !sb.append(frame->getSource()) diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index ff291134618d..1abb36a0b892 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -4285,13 +4285,15 @@ TypeScript::destroy() void Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t* typePool, - size_t* baselineStubsOptimized) + size_t* baselineStubsOptimized, + size_t* uniqueIdMap) { *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf); if (jitZone()) { *baselineStubsOptimized += jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf); } + *uniqueIdMap += uniqueIds_.sizeOfExcludingThis(mallocSizeOf); } TypeZone::TypeZone(Zone* zone) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 740bf09c6aa1..51bc42319383 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1513,6 +1513,8 @@ ReloadPrefsCallback(const char* pref, void* data) bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit") && !safeMode; bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion") && !safeMode; bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs") && !safeMode; + bool throwOnAsmJSValidationFailure = Preferences::GetBool(JS_OPTIONS_DOT_STR + "throw_on_asmjs_validation_failure"); bool useNativeRegExp = Preferences::GetBool(JS_OPTIONS_DOT_STR "native_regexp") && !safeMode; bool parallelParsing = Preferences::GetBool(JS_OPTIONS_DOT_STR "parallel_parsing"); @@ -1536,6 +1538,7 @@ ReloadPrefsCallback(const char* pref, void* data) JS::RuntimeOptionsRef(rt).setBaseline(useBaseline) .setIon(useIon) .setAsmJS(useAsmJS) + .setThrowOnAsmJSValidationFailure(throwOnAsmJSValidationFailure) .setNativeRegExp(useNativeRegExp) .setAsyncStack(useAsyncStack) .setWerror(werror) @@ -1901,6 +1904,10 @@ ReportZoneStats(const JS::ZoneStats& zStats, zStats.unusedGCThings.totalSize(), "Unused GC thing cells within non-empty arenas."); + ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("unique-id-map"), + zStats.uniqueIdMap, + "Address-independent cell identities."); + ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"), zStats.lazyScriptsGCHeap, "Scripts that haven't executed yet."); diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index ace67493567c..b4d1c4b3df73 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -54,7 +54,6 @@ #include "ImageContainer.h" #include "mozilla/Telemetry.h" #include "gfxUtils.h" -#include "gfxColor.h" #include "gfxGradientCache.h" #include "GraphicsFilter.h" #include "nsInlineFrame.h" diff --git a/layout/base/tests/chrome/test_bug551434.html b/layout/base/tests/chrome/test_bug551434.html index 15c78693b0a5..10691c53a94a 100644 --- a/layout/base/tests/chrome/test_bug551434.html +++ b/layout/base/tests/chrome/test_bug551434.html @@ -69,7 +69,7 @@ function runTest() // key events should also be retargeted when the focus changes to an // element in a chrome document from a content document. - i4.addEventListener("keydown", function () $("i3").focus(), false); + i4.addEventListener("keydown", () => $("i3").focus(), false); synthesizeKey("c", { code: "KeyC", keyCode: KeyboardEvent.DOM_VK_C }); diff --git a/layout/forms/test/test_bug534785.html b/layout/forms/test/test_bug534785.html index eb1c6702e581..76590798f521 100644 --- a/layout/forms/test/test_bug534785.html +++ b/layout/forms/test/test_bug534785.html @@ -61,15 +61,15 @@ SimpleTest.waitForFocus(function() { [textAreaWithValue, inputWithValue].forEach(function (elem) { is(elem.value, "test", "Value should persist correctly"); }); - [inputWithoutValue, inputWithValue].forEach(function (elem) elem.type = "submit"); + [inputWithoutValue, inputWithValue].forEach(elem => elem.type = "submit"); document.body.offsetWidth; is(inputWithoutValue.value, "", "Value should persist correctly"); is(inputWithValue.value, "test", "Value should persist correctly"); - [inputWithoutValue, inputWithValue].forEach(function (elem) elem.type = "text"); + [inputWithoutValue, inputWithValue].forEach(elem => elem.type = "text"); document.body.offsetWidth; is(inputWithoutValue.value, "", "Value should persist correctly"); is(inputWithValue.value, "test", "Value should persist correctly"); - [inputWithoutValue, inputWithValue].forEach(function (elem) elem.focus()); // initialze the editor + [inputWithoutValue, inputWithValue].forEach(elem => elem.focus()); // initialze the editor reframeDiv.style.display = "none"; document.body.offsetWidth; reframeDiv.style.display = ""; diff --git a/layout/forms/test/test_textarea_resize.html b/layout/forms/test/test_textarea_resize.html index 5761b1f9d50d..c71f554e0dd0 100644 --- a/layout/forms/test/test_textarea_resize.html +++ b/layout/forms/test/test_textarea_resize.html @@ -17,7 +17,7 @@ /** Test for textbox resizing **/ SimpleTest.waitForExplicitFinish(); -addLoadEvent(function() SimpleTest.executeSoon(doTheTest)); +addLoadEvent(() => SimpleTest.executeSoon(doTheTest)); // -1 means use the default value which is 'both', then test explicitly // setting each possible value. diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp index 97a713d9f2ae..81f248f893fb 100644 --- a/layout/generic/nsPluginFrame.cpp +++ b/layout/generic/nsPluginFrame.cpp @@ -776,9 +776,7 @@ nsPluginFrame::GetRemoteTabChromeOffset() if (topWindow) { dom::TabChild* tc = dom::TabChild::GetFrom(topWindow); if (tc) { - LayoutDeviceIntPoint chromeOffset; - tc->SendGetTabOffset(&chromeOffset); - offset -= chromeOffset; + offset += tc->GetChromeDisplacement(); } } } diff --git a/layout/generic/test/test_bug290397.html b/layout/generic/test/test_bug290397.html index c0652e3d93cd..b907b53fa51b 100644 --- a/layout/generic/test/test_bug290397.html +++ b/layout/generic/test/test_bug290397.html @@ -31,7 +31,7 @@ onhashchange = function() { }; function runTest() { var image = document.getElementById("image"); - SimpleTest.waitForFocus(function() synthesizeMouse(image, 10, 10, {})); + SimpleTest.waitForFocus(() => synthesizeMouse(image, 10, 10, {})); } addLoadEvent(runTest); diff --git a/layout/generic/test/test_bug469613.xul b/layout/generic/test/test_bug469613.xul index d15ec4732f32..0310cfe8c97c 100644 --- a/layout/generic/test/test_bug469613.xul +++ b/layout/generic/test/test_bug469613.xul @@ -78,7 +78,7 @@ function doTest() { } SimpleTest.waitForExplicitFinish(); -addLoadEvent(function() setTimeout(doTest, 0)); +addLoadEvent(() => setTimeout(doTest, 0)); ]]> diff --git a/layout/style/nsCSSValue.h b/layout/style/nsCSSValue.h index 225dd9061438..9a4eee2ba048 100644 --- a/layout/style/nsCSSValue.h +++ b/layout/style/nsCSSValue.h @@ -1493,6 +1493,7 @@ public: return mPropertyID == aOther.mPropertyID && mShorthandPropertyID == aOther.mShorthandPropertyID && mTokenStream.Equals(aOther.mTokenStream) && + mLevel == aOther.mLevel && (mBaseURI == aOther.mBaseURI || (mBaseURI && aOther.mBaseURI && NS_SUCCEEDED(mBaseURI->Equals(aOther.mBaseURI, &eq)) && diff --git a/layout/style/test/test_transitions_cancel_near_end.html b/layout/style/test/test_transitions_cancel_near_end.html index 124d90890aac..4fca67adaa0f 100644 --- a/layout/style/test/test_transitions_cancel_near_end.html +++ b/layout/style/test/test_transitions_cancel_near_end.html @@ -66,10 +66,10 @@ window.addEventListener('load', function() { }, false); }); - cases.forEach(function(aCase) aCase.className = 'another' ); + cases.forEach(aCase => aCase.className = 'another' ); - window.setTimeout(function() cases[0].className = '', 500); - window.setTimeout(function() cases[1].className = cases[2].className = '', 250); + window.setTimeout(() => cases[0].className = '', 500); + window.setTimeout(() => cases[1].className = cases[2].className = '', 250); }, false); diff --git a/layout/svg/nsSVGGradientFrame.cpp b/layout/svg/nsSVGGradientFrame.cpp index f56beb77dc9f..9af45a8270b3 100644 --- a/layout/svg/nsSVGGradientFrame.cpp +++ b/layout/svg/nsSVGGradientFrame.cpp @@ -14,7 +14,6 @@ #include "nsContentUtils.h" #include "nsSVGEffects.h" #include "nsSVGAnimatedTransformList.h" -#include "gfxColor.h" // XXX Tight coupling with content classes ahead! diff --git a/layout/svg/nsSVGPatternFrame.cpp b/layout/svg/nsSVGPatternFrame.cpp index b8240a6faaf3..0b2a6d257eb7 100644 --- a/layout/svg/nsSVGPatternFrame.cpp +++ b/layout/svg/nsSVGPatternFrame.cpp @@ -23,7 +23,6 @@ #include "nsSVGUtils.h" #include "nsSVGAnimatedTransformList.h" #include "SVGContentUtils.h" -#include "gfxColor.h" using namespace mozilla; using namespace mozilla::dom; diff --git a/layout/xul/test/test_windowminmaxsize.xul b/layout/xul/test/test_windowminmaxsize.xul index d41f6a5f5eb2..2d54ee39f32b 100644 --- a/layout/xul/test/test_windowminmaxsize.xul +++ b/layout/xul/test/test_windowminmaxsize.xul @@ -207,8 +207,8 @@ function titledPanelWindowOpened(panelwindow) { var panel = panelwindow.document.documentElement.firstChild; panel.openPopup(); - panel.addEventListener("popupshown", function() doTitledPanelTest(panel), false); - panel.addEventListener("popuphidden", function() done(panelwindow), false); + panel.addEventListener("popupshown", () => doTitledPanelTest(panel), false); + panel.addEventListener("popuphidden", () => done(panelwindow), false); } function doTitledPanelTest(panel) diff --git a/media/mtransport/test/stunserver.cpp b/media/mtransport/test/stunserver.cpp index 1ed02905787f..67b150ed4f47 100644 --- a/media/mtransport/test/stunserver.cpp +++ b/media/mtransport/test/stunserver.cpp @@ -92,7 +92,7 @@ extern "C" { #include "local_addr.h" #include "stun_util.h" #include "registry.h" -#include "nr_socket_multi_tcp.h" +#include "nr_socket_buffered_stun.h" } #include "stunserver.h" @@ -131,11 +131,15 @@ static int nr_socket_wrapped_sendto(void *obj, const void *msg, size_t len, int static int nr_socket_wrapped_recvfrom(void *obj, void * restrict buf, size_t maxlen, size_t *len, int flags, nr_transport_addr *addr) { - MOZ_CRASH(); + nr_socket_wrapped *wrapped = static_cast(obj); + + return nr_socket_recvfrom(wrapped->sock_, buf, maxlen, len, flags, addr); } static int nr_socket_wrapped_getfd(void *obj, NR_SOCKET *fd) { - MOZ_CRASH(); + nr_socket_wrapped *wrapped = static_cast(obj); + + return nr_socket_getfd(wrapped->sock_, fd); } static int nr_socket_wrapped_getaddr(void *obj, nr_transport_addr *addrp) { @@ -186,10 +190,10 @@ int nr_socket_wrapped_create(nr_socket *inner, nr_socket **outp) { // Instance static. // Note: Calling Create() at static init time is not going to be safe, since // we have no reason to expect this will be initted to a nullptr yet. -TestStunServer* TestStunServer::instance; -TestStunTcpServer* TestStunTcpServer::instance; -TestStunServer* TestStunServer::instance6; -TestStunTcpServer* TestStunTcpServer::instance6; +TestStunServer *TestStunServer::instance; +TestStunTcpServer *TestStunTcpServer::instance; +TestStunServer *TestStunServer::instance6; +TestStunTcpServer *TestStunTcpServer::instance6; uint16_t TestStunServer::instance_port = 3478; uint16_t TestStunTcpServer::instance_port = 3478; @@ -216,7 +220,7 @@ TestStunServer::~TestStunServer() { delete response_addr_; } -int TestStunServer::SetInternalPort(nr_local_addr* addr, uint16_t port) { +int TestStunServer::SetInternalPort(nr_local_addr *addr, uint16_t port) { if (nr_transport_addr_set_port(&addr->addr, port)) { MOZ_MTLOG(ML_ERROR, "Couldn't set port"); return R_INTERNAL; @@ -230,7 +234,7 @@ int TestStunServer::SetInternalPort(nr_local_addr* addr, uint16_t port) { return 0; } -int TestStunServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { +int TestStunServer::TryOpenListenSocket(nr_local_addr *addr, uint16_t port) { int r = SetInternalPort(addr, port); @@ -372,21 +376,30 @@ void TestStunServer::ShutdownInstance() { struct DeferredStunOperation { DeferredStunOperation(TestStunServer *server, const char *data, size_t len, - nr_transport_addr *addr) : + nr_transport_addr *addr, + nr_socket *sock) : server_(server), - buffer_(reinterpret_cast(data), len) { + buffer_(reinterpret_cast(data), len), + sock_(sock) { nr_transport_addr_copy(&addr_, addr); } TestStunServer *server_; DataBuffer buffer_; nr_transport_addr addr_; + nr_socket *sock_; }; -void TestStunServer::Process(const uint8_t *msg, size_t len, nr_transport_addr *addr) { +void TestStunServer::Process(const uint8_t *msg, size_t len, nr_transport_addr *addr, nr_socket *sock) { + + if (!sock) { + sock = send_sock_; + } + // Set the wrapped address so that the response goes to the right place. - nr_socket_wrapped_set_send_addr(send_sock_, addr); - nr_stun_server_process_request(stun_server_, send_sock_, + nr_socket_wrapped_set_send_addr(sock, addr); + + nr_stun_server_process_request(stun_server_, sock, const_cast(reinterpret_cast(msg)), len, response_addr_ ? @@ -397,32 +410,43 @@ void TestStunServer::Process(const uint8_t *msg, size_t len, nr_transport_addr * void TestStunServer::process_cb(NR_SOCKET s, int how, void *cb_arg) { DeferredStunOperation *op = static_cast(cb_arg); op->server_->timer_handle_ = nullptr; - op->server_->Process(op->buffer_.data(), op->buffer_.len(), &op->addr_); + op->server_->Process(op->buffer_.data(), op->buffer_.len(), &op->addr_, op->sock_); delete op; } -void TestStunServer::readable_cb(NR_SOCKET s, int how, void *cb_arg) { - TestStunServer* server = static_cast(cb_arg); +nr_socket* TestStunServer::GetReceivingSocket(NR_SOCKET s) { + return listen_sock_; +} - char message[4096]; +nr_socket* TestStunServer::GetSendingSocket(nr_socket *sock) { + return send_sock_; +} + +void TestStunServer::readable_cb(NR_SOCKET s, int how, void *cb_arg) { + TestStunServer *server = static_cast(cb_arg); + + char message[max_stun_message_size]; size_t message_len; nr_transport_addr addr; + nr_socket *recv_sock = server->GetReceivingSocket(s); + if (!recv_sock) { + MOZ_MTLOG(ML_ERROR, "Failed to lookup receiving socket"); + return; + } + nr_socket *send_sock = server->GetSendingSocket(recv_sock); - int r = nr_socket_recvfrom(server->listen_sock_, message, sizeof(message), - &message_len, 0, &addr); + /* Re-arm. */ + NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server); - if (r) { + if (nr_socket_recvfrom(recv_sock, message, sizeof(message), + &message_len, 0, &addr)) { MOZ_MTLOG(ML_ERROR, "Couldn't read STUN message"); return; } MOZ_MTLOG(ML_DEBUG, "Received data of length " << message_len); - // Re-arm. - NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server); - - // If we have initial dropping set, check at this point. std::string key(addr.as_string); @@ -444,10 +468,11 @@ void TestStunServer::readable_cb(NR_SOCKET s, int how, void *cb_arg) { new DeferredStunOperation( server, message, message_len, - &addr), + &addr, send_sock), &server->timer_handle_); } else { - server->Process(reinterpret_cast(message), message_len, &addr); + server->Process(reinterpret_cast(message), message_len, + &addr, send_sock); } } @@ -525,11 +550,12 @@ TestStunTcpServer* TestStunTcpServer::GetInstance(int address_family) { void TestStunTcpServer::ShutdownInstance() { delete instance; - instance = nullptr; + delete instance6; + instance6 = nullptr; } -int TestStunTcpServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { +int TestStunTcpServer::TryOpenListenSocket(nr_local_addr *addr, uint16_t port) { addr->addr.protocol=IPPROTO_TCP; @@ -538,17 +564,15 @@ int TestStunTcpServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { if (r) return r; - if (ice_ctx_ == NULL) - ice_ctx_ = NrIceCtx::Create("stun", false, false, false, false, false); + nr_socket *sock; + if (nr_socket_local_create(nullptr, &addr->addr, &sock)) { + MOZ_MTLOG(ML_ERROR, "Couldn't create listen tcp socket"); + return R_ALREADY; + } - //TODO (nils@mozilla.com) can we replace this with a more basic TCP socket - // alternative which would allow us to remove the framing argument from the - // nr_socket_multi_tcp_create() call? - if(nr_socket_multi_tcp_create(ice_ctx_->ctx(), - &addr->addr, TCP_TYPE_PASSIVE, 0, 0, 2048, - &listen_sock_)) { - MOZ_MTLOG(ML_ERROR, "Couldn't create listen socket"); - return R_ALREADY; + if (nr_socket_buffered_stun_create(sock, 2048, TURN_TCP_FRAMING, &listen_sock_)) { + MOZ_MTLOG(ML_ERROR, "Couldn't create listen tcp socket"); + return R_ALREADY; } if(nr_socket_listen(listen_sock_, 10)) { @@ -559,6 +583,51 @@ int TestStunTcpServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { return 0; } +nr_socket* TestStunTcpServer::GetReceivingSocket(NR_SOCKET s) { + return connections_[s]; +} + +nr_socket* TestStunTcpServer::GetSendingSocket(nr_socket *sock) { + return sock; +} + +void TestStunTcpServer::accept_cb(NR_SOCKET s, int how, void *cb_arg) { + TestStunTcpServer *server = static_cast(cb_arg); + nr_socket *newsock, *bufsock, *wrapsock; + nr_transport_addr remote_addr; + NR_SOCKET fd; + + /* rearm */ + NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunTcpServer::accept_cb, cb_arg); + + /* accept */ + if (nr_socket_accept(server->listen_sock_, &remote_addr, &newsock)) { + MOZ_MTLOG(ML_ERROR, "Couldn't accept incoming tcp connection"); + return; + } + + if(nr_socket_buffered_stun_create(newsock, 2048, TURN_TCP_FRAMING, &bufsock)) { + MOZ_MTLOG(ML_ERROR, "Couldn't create connected tcp socket"); + return; + } + + nr_socket_buffered_set_connected_to(bufsock, &remote_addr); + + if(nr_socket_wrapped_create(bufsock, &wrapsock)) { + MOZ_MTLOG(ML_ERROR, "Couldn't wrap connected tcp socket"); + return; + } + + if(nr_socket_getfd(bufsock, &fd)) { + MOZ_MTLOG(ML_ERROR, "Couldn't get fd from connected tcp socket"); + return; + } + + server->connections_[fd] = wrapsock; + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server); +} + TestStunTcpServer* TestStunTcpServer::Create(int address_family) { NR_reg_init(NR_REG_MODE_LOCAL); @@ -568,15 +637,23 @@ TestStunTcpServer* TestStunTcpServer::Create(int address_family) { return nullptr; } - nr_socket_multi_tcp_set_readable_cb(server->listen_sock_, - &TestStunServer::readable_cb, server.get()); + NR_SOCKET fd; + if(nr_socket_getfd(server->listen_sock_, &fd)) { + MOZ_MTLOG(ML_ERROR, "Couldn't get tcp fd"); + return nullptr; + } + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunTcpServer::accept_cb, server.get()); return server.forget(); } TestStunTcpServer::~TestStunTcpServer() { - ice_ctx_ = nullptr; - nr_socket_destroy(&listen_sock_); + for (auto it = connections_.begin(); it != connections_.end();) { + NR_ASYNC_CANCEL(it->first, NR_ASYNC_WAIT_READ); + nr_socket_destroy(&it->second); + connections_.erase(it++); + } } } // close namespace diff --git a/media/mtransport/test/stunserver.h b/media/mtransport/test/stunserver.h index ad8cfb8b82fc..c1a8c9e93541 100644 --- a/media/mtransport/test/stunserver.h +++ b/media/mtransport/test/stunserver.h @@ -18,6 +18,7 @@ typedef struct nr_stun_server_ctx_ nr_stun_server_ctx; typedef struct nr_socket_ nr_socket; typedef struct nr_local_addr_ nr_local_addr; + namespace mozilla { class TestStunServer { @@ -47,6 +48,11 @@ class TestStunServer { void Reset(); + static const size_t max_stun_message_size = 4096; + + virtual nr_socket* GetReceivingSocket(NR_SOCKET s); + virtual nr_socket* GetSendingSocket(nr_socket *sock); + protected: TestStunServer() : listen_port_(0), @@ -59,13 +65,14 @@ class TestStunServer { response_addr_(nullptr), timer_handle_(nullptr) {} - int SetInternalPort(nr_local_addr* addr, uint16_t port); + int SetInternalPort(nr_local_addr *addr, uint16_t port); int Initialize(int address_family); + static void readable_cb(NR_SOCKET sock, int how, void *cb_arg); private: - void Process(const uint8_t *msg, size_t len, nr_transport_addr *addr_in); - virtual int TryOpenListenSocket(nr_local_addr* addr, uint16_t port); + void Process(const uint8_t *msg, size_t len, nr_transport_addr *addr_in, nr_socket *sock); + virtual int TryOpenListenSocket(nr_local_addr *addr, uint16_t port); static void process_cb(NR_SOCKET sock, int how, void *cb_arg); protected: @@ -82,8 +89,8 @@ class TestStunServer { void *timer_handle_; std::map received_ct_; - static TestStunServer* instance; - static TestStunServer* instance6; + static TestStunServer *instance; + static TestStunServer *instance6; static uint16_t instance_port; }; @@ -93,18 +100,23 @@ class TestStunTcpServer: public TestStunServer { static void ShutdownInstance(); static void ConfigurePort(uint16_t port); virtual ~TestStunTcpServer(); - protected: - TestStunTcpServer() - : ice_ctx_(nullptr) {} - nsRefPtr ice_ctx_; + virtual nr_socket* GetReceivingSocket(NR_SOCKET s); + virtual nr_socket* GetSendingSocket(nr_socket *sock); + + protected: + TestStunTcpServer() {} + static void accept_cb(NR_SOCKET sock, int how, void *cb_arg); + private: - virtual int TryOpenListenSocket(nr_local_addr* addr, uint16_t port); + virtual int TryOpenListenSocket(nr_local_addr *addr, uint16_t port); static TestStunTcpServer *Create(int address_family); - static TestStunTcpServer* instance; - static TestStunTcpServer* instance6; + static TestStunTcpServer *instance; + static TestStunTcpServer *instance6; static uint16_t instance_port; + + std::map connections_; }; } // End of namespace mozilla #endif diff --git a/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c index c4c042162515..b2a5631c402f 100644 --- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c +++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c @@ -86,6 +86,8 @@ static int nr_socket_buffered_stun_close(void *obj); static int nr_socket_buffered_stun_connect(void *sock, nr_transport_addr *addr); static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written); static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg); +static int nr_socket_buffered_stun_listen(void *obj, int backlog); +static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp); static nr_socket_vtbl nr_socket_buffered_stun_vtbl={ 2, @@ -98,8 +100,8 @@ static nr_socket_vtbl nr_socket_buffered_stun_vtbl={ 0, 0, nr_socket_buffered_stun_close, - 0, - 0 + nr_socket_buffered_stun_listen, + nr_socket_buffered_stun_accept }; int nr_socket_buffered_set_connected_to(nr_socket *sock, nr_transport_addr *remote_addr) @@ -368,6 +370,30 @@ static int nr_socket_buffered_stun_close(void *obj) return nr_socket_close(sock->inner); } +static int nr_socket_buffered_stun_listen(void *obj, int backlog) +{ + int r, _status; + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + + if (!sock->inner) + ABORT(R_FAILED); + + if ((r=nr_socket_listen(sock->inner, backlog))) + ABORT(r); + + _status=0; +abort: + return(_status); +} + + +static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp) +{ + nr_socket_buffered_stun *bsock = (nr_socket_buffered_stun *)obj; + + return nr_socket_accept(bsock->inner, addrp, sockp); +} + static void nr_socket_buffered_stun_connected_cb(NR_SOCKET s, int how, void *arg) { nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg; diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 000c6fa970a2..987c9a0d802e 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1118,6 +1118,7 @@ pref("javascript.options.asyncstack", true); #else pref("javascript.options.asyncstack", false); #endif +pref("javascript.options.throw_on_asmjs_validation_failure", false); pref("javascript.options.ion.offthread_compilation", true); // This preference instructs the JS engine to discard the // source of any privileged JS after compilation. This saves diff --git a/services/common/observers.js b/services/common/observers.js index 46f79b9b5b5f..78b14376a37f 100644 --- a/services/common/observers.js +++ b/services/common/observers.js @@ -62,9 +62,9 @@ this.Observers = { // we can make it. We could index by topic, but we can't index by callback // or thisObject, as far as I know, since the keys to JavaScript hashes // (a.k.a. objects) can apparently only be primitive values. - let [observer] = this._cache.filter(function(v) v.topic == topic && - v.callback == callback && - v.thisObject == thisObject); + let [observer] = this._cache.filter(v => v.topic == topic && + v.callback == callback && + v.thisObject == thisObject); if (observer) { this._service.removeObserver(observer, topic); this._cache.splice(this._cache.indexOf(observer), 1); diff --git a/services/common/tests/unit/test_utils_stackTrace.js b/services/common/tests/unit/test_utils_stackTrace.js index 31fa598248aa..6c71d07535d2 100644 --- a/services/common/tests/unit/test_utils_stackTrace.js +++ b/services/common/tests/unit/test_utils_stackTrace.js @@ -2,8 +2,8 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ _("Define some functions in well defined line positions for the test"); -function foo(v) bar(v + 1); // line 2 -function bar(v) baz(v + 1); // line 3 +function foo(v) { return bar(v + 1); } // line 2 +function bar(v) { return baz(v + 1); } // line 3 function baz(v) { throw new Error(v + 1); } // line 4 _("Make sure lazy constructor calling/assignment works"); diff --git a/services/common/utils.js b/services/common/utils.js index 6921dbbe35ef..4353a12cf7f4 100644 --- a/services/common/utils.js +++ b/services/common/utils.js @@ -314,8 +314,9 @@ this.CommonUtils = { } // Handle a left shift, restricted to bytes. - function left(octet, shift) - (octet << shift) & 0xff; + function left(octet, shift) { + return (octet << shift) & 0xff; + } advance(); accumulate(left(val, 3)); diff --git a/services/crypto/modules/WeaveCrypto.js b/services/crypto/modules/WeaveCrypto.js index df6cd57839a1..fc1140851d4b 100644 --- a/services/crypto/modules/WeaveCrypto.js +++ b/services/crypto/modules/WeaveCrypto.js @@ -538,7 +538,9 @@ WeaveCrypto.prototype = { } }, - generateRandomIV : function() this.generateRandomBytes(this.ivLength), + generateRandomIV : function() { + return this.generateRandomBytes(this.ivLength); + }, generateRandomBytes : function(byteCount) { this.log("generateRandomBytes() called"); diff --git a/services/fxaccounts/FxAccounts.jsm b/services/fxaccounts/FxAccounts.jsm index 2984cd5cb5b8..4e665f8c6a44 100644 --- a/services/fxaccounts/FxAccounts.jsm +++ b/services/fxaccounts/FxAccounts.jsm @@ -88,7 +88,9 @@ AccountState.prototype = { whenKeysReadyDeferred: null, // If the storage manager has been nuked then we are no longer current. - get isCurrent() this.storageManager != null, + get isCurrent() { + return this.storageManager != null; + }, abort() { if (this.whenVerifiedDeferred) { diff --git a/services/fxaccounts/tests/xpcshell/test_profile.js b/services/fxaccounts/tests/xpcshell/test_profile.js index 76d0969d2d93..81d239100ded 100644 --- a/services/fxaccounts/tests/xpcshell/test_profile.js +++ b/services/fxaccounts/tests/xpcshell/test_profile.js @@ -69,7 +69,9 @@ function FxaMock() { FxaMock.prototype = { currentAccountState: { profile: null, - get isCurrent() true, + get isCurrent() { + return true; + } }, getSignedInUser: function () { diff --git a/services/sync/modules/engines/clients.js b/services/sync/modules/engines/clients.js index f423242c9ac0..7d55cded58fc 100644 --- a/services/sync/modules/engines/clients.js +++ b/services/sync/modules/engines/clients.js @@ -49,7 +49,9 @@ ClientEngine.prototype = { _trackerObj: ClientsTracker, // Always sync client data as it controls other sync behavior - get enabled() true, + get enabled() { + return true; + }, get lastRecordUpload() { return Svc.Prefs.get(this.name + ".lastRecordUpload", 0); @@ -102,7 +104,9 @@ ClientEngine.prototype = { let localID = Svc.Prefs.get("client.GUID", ""); return localID == "" ? this.localID = Utils.makeGUID() : localID; }, - set localID(value) Svc.Prefs.set("client.GUID", value), + set localID(value) { + Svc.Prefs.set("client.GUID", value); + }, get brandName() { let brand = new StringBundle("chrome://branding/locale/brand.properties"); @@ -116,10 +120,16 @@ ClientEngine.prototype = { return this.localName = Utils.getDefaultDeviceName(); }, - set localName(value) Svc.Prefs.set("client.name", value), + set localName(value) { + Svc.Prefs.set("client.name", value); + }, - get localType() Svc.Prefs.get("client.type", "desktop"), - set localType(value) Svc.Prefs.set("client.type", value), + get localType() { + return Svc.Prefs.get("client.type", "desktop"); + }, + set localType(value) { + Svc.Prefs.set("client.type", value); + }, isMobile: function isMobile(id) { if (this._store._remoteClients[id]) diff --git a/services/sync/modules/engines/forms.js b/services/sync/modules/engines/forms.js index a139280b31e0..84e46de622c2 100644 --- a/services/sync/modules/engines/forms.js +++ b/services/sync/modules/engines/forms.js @@ -109,7 +109,9 @@ FormEngine.prototype = { syncPriority: 6, - get prefName() "history", + get prefName() { + return "history"; + }, _findDupe: function _findDupe(item) { return FormWrapper.getGUID(item.name, item.value); diff --git a/services/sync/modules/main.js b/services/sync/modules/main.js index f59beb184d78..e8e705e726af 100644 --- a/services/sync/modules/main.js +++ b/services/sync/modules/main.js @@ -15,12 +15,14 @@ var lazies = { }; function lazyImport(module, dest, props) { - function getter(prop) function() { - let ns = {}; - Components.utils.import(module, ns); - delete dest[prop]; - return dest[prop] = ns[prop]; - }; + function getter(prop) { + return function() { + let ns = {}; + Components.utils.import(module, ns); + delete dest[prop]; + return dest[prop] = ns[prop]; + }; + } props.forEach(function (prop) { dest.__defineGetter__(prop, getter(prop)); }); } diff --git a/services/sync/modules/policies.js b/services/sync/modules/policies.js index fbc547d1073d..e940f3febb3f 100644 --- a/services/sync/modules/policies.js +++ b/services/sync/modules/policies.js @@ -61,20 +61,40 @@ SyncScheduler.prototype = { }, // nextSync is in milliseconds, but prefs can't hold that much - get nextSync() Svc.Prefs.get("nextSync", 0) * 1000, - set nextSync(value) Svc.Prefs.set("nextSync", Math.floor(value / 1000)), + get nextSync() { + return Svc.Prefs.get("nextSync", 0) * 1000; + }, + set nextSync(value) { + Svc.Prefs.set("nextSync", Math.floor(value / 1000)); + }, - get syncInterval() Svc.Prefs.get("syncInterval", this.singleDeviceInterval), - set syncInterval(value) Svc.Prefs.set("syncInterval", value), + get syncInterval() { + return Svc.Prefs.get("syncInterval", this.singleDeviceInterval); + }, + set syncInterval(value) { + Svc.Prefs.set("syncInterval", value); + }, - get syncThreshold() Svc.Prefs.get("syncThreshold", SINGLE_USER_THRESHOLD), - set syncThreshold(value) Svc.Prefs.set("syncThreshold", value), + get syncThreshold() { + return Svc.Prefs.get("syncThreshold", SINGLE_USER_THRESHOLD); + }, + set syncThreshold(value) { + Svc.Prefs.set("syncThreshold", value); + }, - get globalScore() Svc.Prefs.get("globalScore", 0), - set globalScore(value) Svc.Prefs.set("globalScore", value), + get globalScore() { + return Svc.Prefs.get("globalScore", 0); + }, + set globalScore(value) { + Svc.Prefs.set("globalScore", value); + }, - get numClients() Svc.Prefs.get("numClients", 0), - set numClients(value) Svc.Prefs.set("numClients", value), + get numClients() { + return Svc.Prefs.get("numClients", 0); + }, + set numClients(value) { + Svc.Prefs.set("numClients", value); + }, init: function init() { this._log.level = Log.Level[Svc.Prefs.get("log.logger.service.main")]; diff --git a/services/sync/modules/record.js b/services/sync/modules/record.js index 71a6b4083f6a..43e452a41001 100644 --- a/services/sync/modules/record.js +++ b/services/sync/modules/record.js @@ -196,7 +196,9 @@ CryptoWrapper.prototype = { }, // The custom setter below masks the parent's getter, so explicitly call it :( - get id() WBORecord.prototype.__lookupGetter__("id").call(this), + get id() { + return WBORecord.prototype.__lookupGetter__("id").call(this); + }, // Keep both plaintext and encrypted versions of the id to verify integrity set id(val) { @@ -356,8 +358,9 @@ CollectionKeyManager.prototype = { /** * Create a WBO for the current keys. */ - asWBO: function(collection, id) - this._makeWBO(this._collections, this._default), + asWBO: function(collection, id) { + return this._makeWBO(this._collections, this._default); + }, /** * Compute a new default key, and new keys for any specified collections. @@ -560,14 +563,14 @@ Collection.prototype = { }, // Apply the action to a certain set of ids - get ids() this._ids, + get ids() { return this._ids; }, set ids(value) { this._ids = value; this._rebuildURL(); }, // Limit how many records to get - get limit() this._limit, + get limit() { return this._limit; }, set limit(value) { this._limit = value; this._rebuildURL(); diff --git a/services/sync/modules/resource.js b/services/sync/modules/resource.js index 9bacb3491a98..84cc99f8d71e 100644 --- a/services/sync/modules/resource.js +++ b/services/sync/modules/resource.js @@ -134,7 +134,9 @@ AsyncResource.prototype = { // // Get and set the data encapulated in the resource. _data: null, - get data() this._data, + get data() { + return this._data; + }, set data(value) { this._data = value; }, diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index 43b1d979c9f8..9c7dd0321a76 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -79,7 +79,9 @@ Sync11Service.prototype = { metaURL: null, cryptoKeyURL: null, - get serverURL() Svc.Prefs.get("serverURL"), + get serverURL() { + return Svc.Prefs.get("serverURL"); + }, set serverURL(value) { if (!value.endsWith("/")) { value += "/"; @@ -94,7 +96,9 @@ Sync11Service.prototype = { Svc.Prefs.reset("clusterURL"); }, - get clusterURL() Svc.Prefs.get("clusterURL", ""), + get clusterURL() { + return Svc.Prefs.get("clusterURL", ""); + }, set clusterURL(value) { Svc.Prefs.set("clusterURL", value); this._updateCachedURLs(); diff --git a/services/sync/modules/util.js b/services/sync/modules/util.js index 85f67d78ae22..a43d506884e7 100644 --- a/services/sync/modules/util.js +++ b/services/sync/modules/util.js @@ -477,7 +477,7 @@ this.Utils = { // 20-char sync key. if (pp.length == 23 && - [5, 11, 17].every(function(i) pp[i] == '-')) { + [5, 11, 17].every(i => pp[i] == '-')) { return pp.slice(0, 5) + pp.slice(6, 11) + pp.slice(12, 17) + pp.slice(18, 23); @@ -485,7 +485,7 @@ this.Utils = { // "Modern" 26-char key. if (pp.length == 31 && - [1, 7, 13, 19, 25].every(function(i) pp[i] == '-')) { + [1, 7, 13, 19, 25].every(i => pp[i] == '-')) { return pp.slice(0, 1) + pp.slice(2, 7) + pp.slice(8, 13) + pp.slice(14, 19) diff --git a/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js b/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js index a7e3a46477f0..207372ed68cb 100644 --- a/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js +++ b/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js @@ -85,12 +85,12 @@ function run_test() { do_check_eq(PlacesUtils.bookmarks.getKeywordForBookmark(id), null); do_check_throws( - function () PlacesUtils.annotations.getItemAnnotation(id, GENERATORURI_ANNO), + () => PlacesUtils.annotations.getItemAnnotation(id, GENERATORURI_ANNO), Cr.NS_ERROR_NOT_AVAILABLE ); do_check_throws( - function () PlacesUtils.annotations.getItemAnnotation(id, STATICTITLE_ANNO), + () => PlacesUtils.annotations.getItemAnnotation(id, STATICTITLE_ANNO), Cr.NS_ERROR_NOT_AVAILABLE ); diff --git a/services/sync/tests/unit/test_corrupt_keys.js b/services/sync/tests/unit/test_corrupt_keys.js index 2db080a8f809..cce01636170c 100644 --- a/services/sync/tests/unit/test_corrupt_keys.js +++ b/services/sync/tests/unit/test_corrupt_keys.js @@ -51,7 +51,7 @@ add_task(function test_locally_changed_keys() { }]}]}; delete Svc.Session; Svc.Session = { - getBrowserState: function () JSON.stringify(myTabs) + getBrowserState: () => JSON.stringify(myTabs) }; setBasicCredentials("johndoe", "password", passphrase); diff --git a/services/sync/tests/unit/test_service_detect_upgrade.js b/services/sync/tests/unit/test_service_detect_upgrade.js index 528bd751b133..0f46832d9836 100644 --- a/services/sync/tests/unit/test_service_detect_upgrade.js +++ b/services/sync/tests/unit/test_service_detect_upgrade.js @@ -60,7 +60,7 @@ add_test(function v4_upgrade() { }]}]}; delete Svc.Session; Svc.Session = { - getBrowserState: function () JSON.stringify(myTabs) + getBrowserState: () => JSON.stringify(myTabs) }; Service.status.resetSync(); @@ -229,7 +229,7 @@ add_test(function v5_upgrade() { }]}]}; delete Svc.Session; Svc.Session = { - getBrowserState: function () JSON.stringify(myTabs) + getBrowserState: () => JSON.stringify(myTabs) }; Service.status.resetSync(); diff --git a/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js b/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js index ffc631dd5f80..5192e694d07b 100644 --- a/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js +++ b/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js @@ -41,7 +41,9 @@ function StirlingEngine() { StirlingEngine.prototype = { __proto__: SteamEngine.prototype, // This engine's enabled state is the same as the SteamEngine's. - get prefName() "steam" + get prefName() { + return "steam"; + } }; Service.engineManager.register(StirlingEngine); diff --git a/services/sync/tests/unit/test_tab_tracker.js b/services/sync/tests/unit/test_tab_tracker.js index 620ea07e58a4..f0bff3cc91d3 100644 --- a/services/sync/tests/unit/test_tab_tracker.js +++ b/services/sync/tests/unit/test_tab_tracker.js @@ -15,7 +15,9 @@ function fakeSvcWinMediator() { getEnumerator: function() { return { cnt: 2, - hasMoreElements: function() this.cnt-- > 0, + hasMoreElements: function() { + return this.cnt-- > 0; + }, getNext: function() { let elt = {addTopics: [], remTopics: []}; logs.push(elt); diff --git a/services/sync/tests/unit/test_utils_catch.js b/services/sync/tests/unit/test_utils_catch.js index a10e5eb0d804..302e20e2c311 100644 --- a/services/sync/tests/unit/test_utils_catch.js +++ b/services/sync/tests/unit/test_utils_catch.js @@ -15,31 +15,39 @@ function run_test() { } }, - func: function() this.catch(function() { - rightThis = this == obj; - didCall = true; - return 5; - })(), + func: function() { + return this.catch(function() { + rightThis = this == obj; + didCall = true; + return 5; + })(); + }, - throwy: function() this.catch(function() { - rightThis = this == obj; - didCall = true; - throw 10; - })(), + throwy: function() { + return this.catch(function() { + rightThis = this == obj; + didCall = true; + throw 10; + })(); + }, - callbacky: function() this.catch(function() { - rightThis = this == obj; - didCall = true; - throw 10; - }, function(ex) { - wasTen = (ex == 10) - })(), + callbacky: function() { + return this.catch(function() { + rightThis = this == obj; + didCall = true; + throw 10; + }, function(ex) { + wasTen = (ex == 10) + })(); + }, - lockedy: function() this.catch(function() { - rightThis = this == obj; - didCall = true; - throw("Could not acquire lock."); - })() + lockedy: function() { + return this.catch(function() { + rightThis = this == obj; + didCall = true; + throw("Could not acquire lock."); + })(); + } }; _("Make sure a normal call will call and return"); diff --git a/services/sync/tests/unit/test_utils_deferGetSet.js b/services/sync/tests/unit/test_utils_deferGetSet.js index 55c0fcb0e670..9d58a9873e3b 100644 --- a/services/sync/tests/unit/test_utils_deferGetSet.js +++ b/services/sync/tests/unit/test_utils_deferGetSet.js @@ -6,8 +6,12 @@ function run_test() { base.prototype = { dst: {}, - get a() "a", - set b(val) this.dst.b = val + "!!!" + get a() { + return "a"; + }, + set b(val) { + this.dst.b = val + "!!!"; + } }; let src = new base(); diff --git a/services/sync/tests/unit/test_utils_lock.js b/services/sync/tests/unit/test_utils_lock.js index fd8a4b1f522e..d1830787e63a 100644 --- a/services/sync/tests/unit/test_utils_lock.js +++ b/services/sync/tests/unit/test_utils_lock.js @@ -27,19 +27,23 @@ function run_test() { this._locked = false; }, - func: function() this._lock("Test utils lock", - function() { - rightThis = this == obj; - didCall = true; - return 5; - })(), + func: function() { + return this._lock("Test utils lock", + function() { + rightThis = this == obj; + didCall = true; + return 5; + })(); + }, - throwy: function() this._lock("Test utils lock throwy", - function() { - rightThis = this == obj; - didCall = true; - this.throwy(); - })() + throwy: function() { + return this._lock("Test utils lock throwy", + function() { + rightThis = this == obj; + didCall = true; + this.throwy(); + })(); + } }; _("Make sure a normal call will call and return"); diff --git a/services/sync/tests/unit/test_utils_notify.js b/services/sync/tests/unit/test_utils_notify.js index c191bbfef67d..5bd38da5f272 100644 --- a/services/sync/tests/unit/test_utils_notify.js +++ b/services/sync/tests/unit/test_utils_notify.js @@ -9,17 +9,21 @@ function run_test() { trace: function() {} }, - func: function() this.notify("bar", "baz", function() { - rightThis = this == obj; - didCall = true; - return 5; - })(), + func: function() { + return this.notify("bar", "baz", function() { + rightThis = this == obj; + didCall = true; + return 5; + })(); + }, - throwy: function() this.notify("bad", "one", function() { - rightThis = this == obj; - didCall = true; - throw 10; - })() + throwy: function() { + return this.notify("bad", "one", function() { + rightThis = this == obj; + didCall = true; + throw 10; + })(); + } }; let state = 0; diff --git a/testing/docker/phone-builder/Dockerfile b/testing/docker/phone-builder/Dockerfile index 97f0f05deb8f..bb4de4033133 100644 --- a/testing/docker/phone-builder/Dockerfile +++ b/testing/docker/phone-builder/Dockerfile @@ -8,7 +8,7 @@ ADD bin /home/worker/bin ADD config /home/worker/.aws/config ADD socorro.token /home/worker/socorro.token -RUN yum install -y bc lzop +RUN yum install -y bc lzop java-1.7.0-openjdk RUN pip install awscli RUN npm install -g bower gulp apm grunt-cli diff --git a/testing/docker/phone-builder/VERSION b/testing/docker/phone-builder/VERSION index fe04e7f676f5..818944f5b824 100644 --- a/testing/docker/phone-builder/VERSION +++ b/testing/docker/phone-builder/VERSION @@ -1 +1 @@ -0.0.20 +0.0.22 diff --git a/testing/docker/phone-builder/build.sh b/testing/docker/phone-builder/build.sh index ad2738b579e8..8a5e598a887f 100755 --- a/testing/docker/phone-builder/build.sh +++ b/testing/docker/phone-builder/build.sh @@ -6,10 +6,10 @@ while getopts "t:i:k:s:" arg; do TAG=$OPTARG ;; i) - KEY_ID=$OPTARG + AWS_ACCESS_KEY_ID=$OPTARG ;; k) - SECRET_KEY=$OPTARG + AWS_SECRET_ACCESS_KEY=$OPTARG ;; s) SOCORRO_TOKEN=$OPTARG @@ -20,13 +20,13 @@ done pushd $(dirname $0) test $TAG -test $KEY_ID -test $SECRET_KEY +test $AWS_ACCESS_KEY_ID +test $AWS_SECRET_ACCESS_KEY test $SOCORRO_TOKEN (echo '[default]' -echo "aws_access_key_id = $KEY_ID" -echo "aws_secret_access_key = $SECRET_KEY") > config +echo "aws_access_key_id = $AWS_ACCESS_KEY_ID" +echo "aws_secret_access_key = $AWS_SECRET_ACCESS_KEY") > config echo $SOCORRO_TOKEN > socorro.token diff --git a/testing/mozharness/configs/firefox_ui_tests/jenkins.py b/testing/mozharness/configs/firefox_ui_tests/jenkins.py deleted file mode 100644 index c3811688c5c9..000000000000 --- a/testing/mozharness/configs/firefox_ui_tests/jenkins.py +++ /dev/null @@ -1,6 +0,0 @@ -config = { - 'virtualenv_modules': [ - # optional packages we need for Jenkins - 'mozdownload==1.17', - ] -} diff --git a/testing/mozharness/configs/firefox_ui_tests/qa_jenkins.py b/testing/mozharness/configs/firefox_ui_tests/qa_jenkins.py new file mode 100644 index 000000000000..cd10677c0148 --- /dev/null +++ b/testing/mozharness/configs/firefox_ui_tests/qa_jenkins.py @@ -0,0 +1,25 @@ +# Default configuration as used by Mozmill CI (Jenkins) + +import os + + +config = { + 'env': { + 'PIP_TRUSTED_HOST': 'pypi.pub.build.mozilla.org', + }, + + # General local variable overwrite + 'exes': { + 'gittool.py': os.path.join(os.getcwd(), 'external_tools', 'gittool.py'), + 'hgtool.py': os.path.join(os.getcwd(), 'external_tools', 'hgtool.py'), + }, + + # PIP + 'find_links': ['http://pypi.pub.build.mozilla.org/pub'], + 'pip_index': False, + + # mozcrash support + 'download_minidump_stackwalk': True, + 'download_symbols': 'ondemand', + 'download_tooltool': True, +} diff --git a/testing/mozharness/configs/firefox_ui_tests/releng_release.py b/testing/mozharness/configs/firefox_ui_tests/releng_release.py new file mode 100644 index 000000000000..77e1e2ecee93 --- /dev/null +++ b/testing/mozharness/configs/firefox_ui_tests/releng_release.py @@ -0,0 +1,25 @@ +# Default configuration as used by Release Engineering for testing release/beta builds + +import os + + +config = { + 'env': { + 'PIP_TRUSTED_HOST': 'pypi.pub.build.mozilla.org', + }, + + # General local variable overwrite + 'exes': { + 'gittool.py': os.path.join(os.getcwd(), 'external_tools', 'gittool.py'), + 'hgtool.py': os.path.join(os.getcwd(), 'external_tools', 'hgtool.py'), + }, + + # PIP + 'find_links': ['http://pypi.pub.build.mozilla.org/pub'], + 'pip_index': False, + + # mozcrash support + 'download_minidump_stackwalk': True, + 'download_symbols': 'ondemand', + 'download_tooltool': True, +} diff --git a/testing/mozharness/configs/mediatests/buildbot_posix_config.py b/testing/mozharness/configs/mediatests/buildbot_posix_config.py index bcf2c8962eac..0cce8276c9ce 100644 --- a/testing/mozharness/configs/mediatests/buildbot_posix_config.py +++ b/testing/mozharness/configs/mediatests/buildbot_posix_config.py @@ -41,9 +41,9 @@ config = { "firefox_media_repo": 'https://github.com/mjzffr/firefox-media-tests.git', "firefox_media_branch": 'master', - "firefox_media_rev": '82c45fba24457b5fe447e967bbcaaec5eb14e3ee', + "firefox_media_rev": 'b11d6c3d7f6af166be314d2ac6673e78c1edb566', "firefox_ui_repo": 'https://github.com/mozilla/firefox-ui-tests.git', - "firefox_ui_branch": 'master', + "firefox_ui_branch": 'mozilla-central', "firefox_ui_rev": '6d6d57917f85399e903ac69b7e4297091b2d474c', } diff --git a/testing/mozharness/configs/mediatests/buildbot_windows_config.py b/testing/mozharness/configs/mediatests/buildbot_windows_config.py index b457678c88b6..9f837a09f246 100644 --- a/testing/mozharness/configs/mediatests/buildbot_windows_config.py +++ b/testing/mozharness/configs/mediatests/buildbot_windows_config.py @@ -17,7 +17,10 @@ config = { 'mozinstall': ['%s/build/venv/scripts/python' % os.getcwd(), '%s/build/venv/scripts/mozinstall-script.py' % os.getcwd()], 'tooltool.py': [sys.executable, 'C:/mozilla-build/tooltool.py'], - 'gittool.py': os.path.join(external_tools_path, 'gittool.py'), + 'gittool.py': [sys.executable, + os.path.join(external_tools_path, 'gittool.py')], + 'hgtool.py': [sys.executable, + os.path.join(external_tools_path, 'hgtool.py')], }, @@ -49,9 +52,9 @@ config = { "firefox_media_repo": 'https://github.com/mjzffr/firefox-media-tests.git', "firefox_media_branch": 'master', - "firefox_media_rev": '82c45fba24457b5fe447e967bbcaaec5eb14e3ee', + "firefox_media_rev": 'b11d6c3d7f6af166be314d2ac6673e78c1edb566', "firefox_ui_repo": 'https://github.com/mozilla/firefox-ui-tests.git', - "firefox_ui_branch": 'master', + "firefox_ui_branch": 'mozilla-central', "firefox_ui_rev": '6d6d57917f85399e903ac69b7e4297091b2d474c', } diff --git a/testing/mozharness/configs/talos/linux_config.py b/testing/mozharness/configs/talos/linux_config.py index c6140d10653d..192de17c61de 100644 --- a/testing/mozharness/configs/talos/linux_config.py +++ b/testing/mozharness/configs/talos/linux_config.py @@ -35,8 +35,6 @@ config = { "install", "run-tests", ], - "python_webserver": False, - "webroot": '%s/../talos-data' % os.getcwd(), "default_blob_upload_servers": [ "https://blobupload.elasticbeanstalk.com", ], diff --git a/testing/mozharness/configs/talos/mac_config.py b/testing/mozharness/configs/talos/mac_config.py index c8dae2574e34..56876dbdd3f0 100644 --- a/testing/mozharness/configs/talos/mac_config.py +++ b/testing/mozharness/configs/talos/mac_config.py @@ -38,8 +38,6 @@ config = { "install", "run-tests", ], - "python_webserver": False, - "webroot": '%s/../talos-data' % os.getcwd(), "run_cmd_checks_enabled": True, "preflight_run_cmd_suites": [ SCREEN_RESOLUTION_CHECK, diff --git a/testing/mozharness/configs/talos/windows_config.py b/testing/mozharness/configs/talos/windows_config.py index 20ba7f76c2d3..50c924c4441d 100644 --- a/testing/mozharness/configs/talos/windows_config.py +++ b/testing/mozharness/configs/talos/windows_config.py @@ -37,10 +37,6 @@ config = { "install", "run-tests", ], - "python_webserver": False, - "webroot": 'c:/slave/talos-data', - # Srsly gly? Ys - "webroot_extract_cmd": r'''c:/mozilla-build/msys/bin/bash -c "PATH=/c/mozilla-build/msys/bin:$PATH tar zx --strip-components=1 -f '%(tarball)s' --wildcards '**/talos/'"''', "default_blob_upload_servers": [ "https://blobupload.elasticbeanstalk.com", ], diff --git a/testing/mozharness/mozharness/lib/python/authentication.py b/testing/mozharness/mozharness/lib/python/authentication.py index 26efc1abee8f..2e5f83f373ad 100644 --- a/testing/mozharness/mozharness/lib/python/authentication.py +++ b/testing/mozharness/mozharness/lib/python/authentication.py @@ -10,11 +10,15 @@ import os CREDENTIALS_PATH = os.path.expanduser("~/.mozilla/credentials.cfg") DIRNAME = os.path.dirname(CREDENTIALS_PATH) +LDAP_PASSWORD = None def get_credentials(): - """ Returns credentials for http access either from - disk or directly from the user (which we store) + """ Returns http credentials. + + The user's email address is stored on disk (for convenience in the future) + while the password is requested from the user on first invocation. """ + global LDAP_PASSWORD if not os.path.exists(DIRNAME): os.makedirs(DIRNAME) @@ -23,19 +27,24 @@ def get_credentials(): content = file_handler.read().splitlines() https_username = content[0].strip() - https_password = content[1].strip() + + if len(content) > 1: + # We want to remove files which contain the password + os.remove(CREDENTIALS_PATH) else: https_username = \ raw_input("Please enter your full LDAP email address: ") - https_password = getpass.getpass() with open(CREDENTIALS_PATH, "w+") as file_handler: file_handler.write("%s\n" % https_username) - file_handler.write("%s\n" % https_password) os.chmod(CREDENTIALS_PATH, 0600) - return https_username, https_password + if not LDAP_PASSWORD: + print "Please enter your LDAP password (we won't store it):" + LDAP_PASSWORD = getpass.getpass() + + return https_username, LDAP_PASSWORD def get_credentials_path(): if os.path.isfile(CREDENTIALS_PATH): diff --git a/testing/mozharness/mozharness/mozilla/testing/firefox_media_tests.py b/testing/mozharness/mozharness/mozilla/testing/firefox_media_tests.py index 81fdf60c124e..d4107e05914b 100644 --- a/testing/mozharness/mozharness/mozilla/testing/firefox_media_tests.py +++ b/testing/mozharness/mozharness/mozilla/testing/firefox_media_tests.py @@ -157,15 +157,14 @@ class FirefoxMediaTestsBase(TestingMixin, VCSToolsScript): @PreScriptAction('create-virtualenv') def _pre_create_virtualenv(self, action): dirs = self.query_abs_dirs() - # cwd is $workspace/build + requirements_file = os.path.join(dirs['firefox_media_dir'], + 'requirements.txt') + if os.path.isfile(requirements_file): + self.register_virtualenv_module(requirements=[requirements_file]) self.register_virtualenv_module(name='firefox-ui-tests', - url=dirs['firefox_ui_dir'], - method='pip', - editable='true') + url=dirs['firefox_ui_dir']) self.register_virtualenv_module(name='firefox-media-tests', - url=dirs['firefox_media_dir'], - method='pip', - editable='true') + url=dirs['firefox_media_dir']) def query_abs_dirs(self): if self.abs_dirs: @@ -209,7 +208,12 @@ class FirefoxMediaTestsBase(TestingMixin, VCSToolsScript): if not self.binary_path: self.fatal("Binary path could not be determined. " "Should be set by default during 'install' action.") - cmd = ['firefox-media-tests'] + dirs = self.query_abs_dirs() + venv_python_path = self.query_python_path() + runner_script = os.path.join(dirs['firefox_media_dir'], + 'media_test_harness', + 'runtests.py') + cmd = [venv_python_path, runner_script] cmd += ['--binary', self.binary_path] if self.symbols_path: cmd += ['--symbols-path', self.symbols_path] diff --git a/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py b/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py index c43baffeeeb2..a5ce7c966c1b 100644 --- a/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py +++ b/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py @@ -12,14 +12,15 @@ Author: Armen Zambrano G. import copy import os import sys -import urllib2 +import urlparse from mozharness.base.python import ( PreScriptAction, - VirtualenvMixin, - virtualenv_config_options, ) -from mozharness.mozilla.testing.testbase import (INSTALLER_SUFFIXES) +from mozharness.mozilla.testing.testbase import ( + TestingMixin, + testing_config_options, +) from mozharness.mozilla.vcstools import VCSToolsScript @@ -44,15 +45,7 @@ firefox_ui_tests_config_options = [ 'help': 'absolute path to directory containing breakpad ' 'symbols, or the url of a zip file containing symbols.', }], - [['--installer-url'], { - 'dest': 'installer_url', - 'help': 'Point to an installer to download and test against.', - }], - [['--installer-path'], { - 'dest': 'installer_path', - 'help': 'Point to an installer to test against.', - }], -] + copy.deepcopy(virtualenv_config_options) +] + copy.deepcopy(testing_config_options) # Command line arguments for update tests firefox_ui_update_harness_config_options = [ @@ -94,7 +87,7 @@ firefox_ui_update_config_options = firefox_ui_update_harness_config_options \ + copy.deepcopy(firefox_ui_tests_config_options) -class FirefoxUITests(VCSToolsScript, VirtualenvMixin): +class FirefoxUITests(TestingMixin, VCSToolsScript): cli_script = 'firefox-ui-tests' @@ -106,15 +99,15 @@ class FirefoxUITests(VCSToolsScript, VirtualenvMixin): 'clobber', 'checkout', 'create-virtualenv', + 'query_minidump_stackwalk', 'run-tests', ] - VCSToolsScript.__init__(self, - config_options=config_options, - all_actions=all_actions or actions, - default_actions=default_actions or actions, - *args, **kwargs) - VirtualenvMixin.__init__(self) + super(FirefoxUITests, self).__init__( + config_options=config_options, + all_actions=all_actions or actions, + default_actions=default_actions or actions, + *args, **kwargs) self.firefox_ui_repo = self.config['firefox_ui_repo'] self.firefox_ui_branch = self.config.get('firefox_ui_branch') @@ -124,16 +117,13 @@ class FirefoxUITests(VCSToolsScript, VirtualenvMixin): 'Please specify --firefox-ui-branch. Valid values can be found ' 'in here https://github.com/mozilla/firefox-ui-tests/branches') + # As long as we don't run on buildbot the installers are not handled by TestingMixin self.installer_url = self.config.get('installer_url') self.installer_path = self.config.get('installer_path') if self.installer_path: self.installer_path = os.path.abspath(self.installer_path) - if not os.path.exists(self.installer_path): - self.critical('Please make sure that the path to the installer exists.') - sys.exit(1) - @PreScriptAction('create-virtualenv') def _pre_create_virtualenv(self, action): dirs = self.query_abs_dirs() @@ -150,28 +140,6 @@ class FirefoxUITests(VCSToolsScript, VirtualenvMixin): self.register_virtualenv_module('firefox-ui-tests', url=dirs['fx_ui_dir']) - def _query_symbols_url(self, installer_url): - for suffix in INSTALLER_SUFFIXES: - if installer_url.endswith(suffix): - symbols_url = installer_url[:-len(suffix)] + '.crashreporter-symbols.zip' - continue - - if symbols_url: - self.info('Symbols_url: {}'.format(symbols_url)) - if not symbols_url.startswith('http'): - return symbols_url - - try: - # Let's see if the symbols are available - urllib2.urlopen(symbols_url) - return symbols_url - - except urllib2.HTTPError, e: - self.warning('{} - {}'.format(str(e), symbols_url)) - return None - else: - self.fatal('Can\'t find symbols_url from installer_url: {}!'.format(installer_url)) - @PreScriptAction('checkout') def _pre_checkout(self, action): if not self.firefox_ui_branch: @@ -212,7 +180,32 @@ class FirefoxUITests(VCSToolsScript, VirtualenvMixin): """ return [] - def run_test(self, installer_path, script_name, env=None, symbols_url=None, + def query_minidump_stackwalk(self): + """We don't have an extracted test package available to get the manifest file. + + So we have to explicitely download the latest version of the manifest from the + mozilla-central repository and feed it into the query_minidump_stackwalk() method. + + We can remove this whole method once our tests are part of the tree. + + """ + manifest_path = None + + if self.config.get('download_minidump_stackwalk'): + tooltool_manifest = self.query_minidump_tooltool_manifest() + url_base = 'https://hg.mozilla.org/mozilla-central/raw-file/default/testing/' + + dirs = self.query_abs_dirs() + manifest_path = os.path.join(dirs['abs_work_dir'], 'releng.manifest') + try: + self.download_file(urlparse.urljoin(url_base, tooltool_manifest), + manifest_path) + except Exception as e: + self.fatal('Download of tooltool manifest file failed: %s' % e.message) + + super(FirefoxUITests, self).query_minidump_stackwalk(manifest=manifest_path) + + def run_test(self, installer_path, script_name, env=None, cleanup=True, marionette_port=2828): """All required steps for running the tests against an installer.""" dirs = self.query_abs_dirs() @@ -233,12 +226,17 @@ class FirefoxUITests(VCSToolsScript, VirtualenvMixin): '--workspace', dirs['abs_work_dir'], ] - if symbols_url: - cmd += ['--symbols-path', symbols_url] - # Collect all pass-through harness options to the script cmd.extend(self.query_extra_cmd_args()) + # Set further environment settings + env = env or self.query_env() + if self.minidump_stackwalk_path: + env['MINIDUMP_STACKWALK'] = self.minidump_stackwalk_path + + if self.query_symbols_url(): + cmd += ['--symbols-path', self.symbols_url] + return_code = self.run_command(cmd, cwd=dirs['abs_work_dir'], output_timeout=300, env=env) @@ -279,13 +277,10 @@ class FirefoxUITests(VCSToolsScript, VirtualenvMixin): parent_dir=dirs['abs_work_dir'] ) - symbols_url = self._query_symbols_url(installer_url=self.installer_path) - return self.run_test( installer_path=self.installer_path, script_name=self.cli_script, env=self.query_env(), - symbols_url=symbols_url, cleanup=False, ) diff --git a/testing/mozharness/mozharness/mozilla/testing/talos.py b/testing/mozharness/mozharness/mozilla/testing/talos.py index 493354d8238a..95720bc89f6c 100755 --- a/testing/mozharness/mozharness/mozilla/testing/talos.py +++ b/testing/mozharness/mozharness/mozilla/testing/talos.py @@ -157,11 +157,6 @@ class Talos(TestingMixin, MercurialScript, BlobUploadMixin): self.talos_json_config = self.config.get("talos_json_config") self.tests = None self.pagesets_url = None - self.pagesets_parent_dir_path = None - self.pagesets_manifest_path = None - self.abs_pagesets_paths = None - self.pagesets_manifest_filename = None - self.pagesets_manifest_parent_path = None self.sps_profile = self.config.get('sps_profile') self.sps_profile_interval = self.config.get('sps_profile_interval') @@ -270,62 +265,6 @@ class Talos(TestingMixin, MercurialScript, BlobUploadMixin): self.pagesets_url = self.talos_json_config['suites'][self.config['suite']].get('pagesets_url') return self.pagesets_url - def query_pagesets_parent_dir_path(self): - """ We have to copy the pageset into the webroot separately. - - Helper method to avoid hardcodes. - """ - if self.pagesets_parent_dir_path: - return self.pagesets_parent_dir_path - if self.query_talos_json_config(): - self.pagesets_parent_dir_path = self.talos_json_config['suites'][self.config['suite']].get('pagesets_parent_dir_path') - return self.pagesets_parent_dir_path - - def query_pagesets_manifest_path(self): - """ We have to copy the tp manifest from webroot to talos root when - those two directories aren't the same, until bug 795172 is fixed. - - Helper method to avoid hardcodes. - """ - if self.pagesets_manifest_path: - return self.pagesets_manifest_path - if self.query_talos_json_config(): - self.pagesets_manifest_path = self.talos_json_config['suites'][self.config['suite']].get('pagesets_manifest_path') - return self.pagesets_manifest_path - - def query_pagesets_manifest_filename(self): - if self.pagesets_manifest_filename: - return self.pagesets_manifest_filename - else: - manifest_path = self.query_pagesets_manifest_path() - self.pagesets_manifest_filename = os.path.basename(manifest_path) - return self.pagesets_manifest_filename - - def query_pagesets_manifest_parent_path(self): - if self.pagesets_manifest_parent_path: - return self.pagesets_manifest_parent_path - if self.query_talos_json_config(): - manifest_path = self.query_pagesets_manifest_path() - self.pagesets_manifest_parent_path = os.path.dirname(manifest_path) - return self.pagesets_manifest_parent_path - - def query_abs_pagesets_paths(self): - """ Returns a bunch of absolute pagesets directory paths. - We need this to make the dir and copy the manifest to the local dir. - """ - if self.abs_pagesets_paths: - return self.abs_pagesets_paths - else: - paths = {} - manifest_parent_path = self.query_pagesets_manifest_parent_path() - paths['pagesets_manifest_parent'] = os.path.join(self.talos_path, manifest_parent_path) - - manifest_path = self.query_pagesets_manifest_path() - paths['pagesets_manifest'] = os.path.join(self.talos_path, manifest_path) - - self.abs_pagesets_paths = paths - return self.abs_pagesets_paths - def talos_options(self, args=None, **kw): """return options to talos""" # binary path @@ -335,8 +274,6 @@ class Talos(TestingMixin, MercurialScript, BlobUploadMixin): # talos options options = [] - if self.config.get('python_webserver', True): - options.append('--develop') # talos can't gather data if the process name ends with '.exe' if binary_path.endswith('.exe'): binary_path = binary_path[:-4] @@ -376,12 +313,6 @@ class Talos(TestingMixin, MercurialScript, BlobUploadMixin): def populate_webroot(self): """Populate the production test slaves' webroots""" c = self.config - if not c.get('webroot'): - self.fatal("webroot need to be set to populate_webroot!") - self.info("Populating webroot %s..." % c['webroot']) - talos_webdir = os.path.join(c['webroot'], 'talos') - self.mkdir_p(c['webroot'], error_level=FATAL) - self.rmtree(talos_webdir, error_level=FATAL) self.talos_path = os.path.join( self.query_abs_dirs()['abs_work_dir'], 'tests', 'talos' @@ -389,25 +320,12 @@ class Talos(TestingMixin, MercurialScript, BlobUploadMixin): if c.get('run_local'): self.talos_path = os.path.dirname(self.talos_json) - # the apache server needs the talos directory (talos/talos) - # to be in the webroot src_talos_webdir = os.path.join(self.talos_path, 'talos') - self.copytree(src_talos_webdir, talos_webdir) if self.query_pagesets_url(): self.info("Downloading pageset...") - pagesets_path = os.path.join(c['webroot'], self.query_pagesets_parent_dir_path()) - self._download_unzip(self.pagesets_url, pagesets_path) - - # mkdir for the missing manifest directory in talos_repo/talos/page_load_test directory - abs_pagesets_paths = self.query_abs_pagesets_paths() - abs_manifest_parent_path = abs_pagesets_paths['pagesets_manifest_parent'] - self.mkdir_p(abs_manifest_parent_path, error_level=FATAL) - - # copy all the manifest file from unzipped zip file into the manifest dir - src_manifest_file = os.path.join(c['webroot'], self.query_pagesets_manifest_path()) - dest_manifest_file = abs_pagesets_paths['pagesets_manifest'] - self.copyfile(src_manifest_file, dest_manifest_file, error_level=FATAL) + src_talos_pageset = os.path.join(src_talos_webdir, 'tests') + self._download_unzip(self.pagesets_url, src_talos_pageset) # Action methods. {{{1 # clobber defined in BaseScript @@ -452,20 +370,6 @@ class Talos(TestingMixin, MercurialScript, BlobUploadMixin): 'requirements.txt')] ) - def postflight_create_virtualenv(self): - """ This belongs in download_and_install() but requires the - virtualenv to be set up :( - - The real fix here may be a --tpmanifest option for PerfConfigurator. - """ - c = self.config - if not c.get('python_webserver', True) and self.query_pagesets_url(): - pagesets_path = self.query_pagesets_manifest_path() - manifest_source = os.path.join(c['webroot'], pagesets_path) - manifest_target = os.path.join(self.query_python_site_packages_path(), pagesets_path) - self.mkdir_p(os.path.dirname(manifest_target)) - self.copyfile(manifest_source, manifest_target) - def run_tests(self, args=None, **kw): """run Talos tests""" diff --git a/testing/mozharness/mozharness/mozilla/testing/testbase.py b/testing/mozharness/mozharness/mozilla/testing/testbase.py index fc19622f0a2a..782300ae85df 100755 --- a/testing/mozharness/mozharness/mozilla/testing/testbase.py +++ b/testing/mozharness/mozharness/mozilla/testing/testbase.py @@ -642,7 +642,7 @@ Did you run with --create-virtualenv? Is mozinstall in virtualenv_modules?""") else: self.fatal('could not determine minidump filename') - def query_minidump_stackwalk(self): + def query_minidump_stackwalk(self, manifest=None): if self.minidump_stackwalk_path: return self.minidump_stackwalk_path c = self.config @@ -650,13 +650,17 @@ Did you run with --create-virtualenv? Is mozinstall in virtualenv_modules?""") if c.get('download_minidump_stackwalk'): minidump_stackwalk_path = self.query_minidump_filename() - tooltool_manifest_path = self.query_minidump_tooltool_manifest() + + if not manifest: + tooltool_manifest_path = self.query_minidump_tooltool_manifest() + manifest = os.path.join(dirs.get('abs_test_install_dir', + os.path.join(dirs['abs_work_dir'], 'tests')), + tooltool_manifest_path) + self.info('grabbing minidump binary from tooltool') try: self.tooltool_fetch( - manifest=os.path.join(dirs.get('abs_test_install_dir', - os.path.join(dirs['abs_work_dir'], 'tests')), - tooltool_manifest_path), + manifest=manifest, output_dir=dirs['abs_work_dir'], cache=c.get('tooltool_cache') ) diff --git a/testing/mozharness/scripts/firefox_ui_tests/update_release.py b/testing/mozharness/scripts/firefox_ui_tests/update_release.py index 7d8ba1f1b923..92aa6ab40dea 100755 --- a/testing/mozharness/scripts/firefox_ui_tests/update_release.py +++ b/testing/mozharness/scripts/firefox_ui_tests/update_release.py @@ -12,7 +12,6 @@ Author: Armen Zambrano G. import copy import os import pprint -import re import sys import urllib @@ -72,6 +71,7 @@ class ReleaseFirefoxUIUpdateTests(FirefoxUIUpdateTests): 'clobber', 'checkout', 'create-virtualenv', + 'query_minidump_stackwalk', 'read-release-update-config', 'run-tests', ] @@ -93,26 +93,6 @@ class ReleaseFirefoxUIUpdateTests(FirefoxUIUpdateTests): # from tools/release/updates/*cfg self.releases = None - def _modify_url(self, rel_info): - # This is a temporary hack to find crash symbols. It should be replaced - # with something that doesn't make wild guesses about where symbol - # packages are. - # We want this: - # https://ftp.mozilla.org/pub/mozilla.org/firefox/candidates/40.0b1-candidates/build1/mac/en-US/Firefox%2040.0b1.crashreporter-symbols.zip - # https://ftp.mozilla.org/pub/mozilla.org//firefox/releases/40.0b1/mac/en-US/Firefox%2040.0b1.crashreporter-symbols.zip - installer_from = rel_info['from'] - version = (re.search('/firefox/releases/(%s.*)\/.*\/.*\/.*' % rel_info['release'], - installer_from)).group(1) - - temp_from = installer_from.replace(version, '%s-candidates/build%s' % ( - version, self.config['build_number']), - 1).replace('releases', 'candidates') - temp_url = rel_info['ftp_server_from'] + urllib.quote(temp_from.replace('%locale%', - 'en-US')) - self.info('Installer url under stage/candidates dir: {}'.format(temp_url)) - - return temp_url - def checkout(self): """ We checkout the tools repository and update to the right branch @@ -253,12 +233,6 @@ class ReleaseFirefoxUIUpdateTests(FirefoxUIUpdateTests): if self.config['dry_run']: continue - # Safe temporary hack to determine symbols URL from en-US - # build1 in the candidates dir - ftp_candidates_installer_url = self._modify_url(rel_info) - symbols_url = self._query_symbols_url( - installer_url=ftp_candidates_installer_url) - # Determine from where to download the file installer_url = '{server}/{fragment}'.format( server=rel_info['ftp_server_from'], @@ -275,7 +249,6 @@ class ReleaseFirefoxUIUpdateTests(FirefoxUIUpdateTests): installer_path=installer_path, script_name=self.cli_script, env=self.query_env(avoid_host_env=True), - symbols_url=symbols_url, marionette_port=marionette_port, ) @@ -293,8 +266,8 @@ class ReleaseFirefoxUIUpdateTests(FirefoxUIUpdateTests): for config in self.config['config_files']: base_cmd += ' --cfg {}'.format(config) - if symbols_url: - base_cmd += ' --symbols-path {}'.format(symbols_url) + if self.symbols_url: + base_cmd += ' --symbols-path {}'.format(self.symbols_url) base_cmd += ' --installer-url {}'.format(installer_url) diff --git a/testing/talos/mach_commands.py b/testing/talos/mach_commands.py index ebd321ff1fdb..9832f53e827b 100644 --- a/testing/talos/mach_commands.py +++ b/testing/talos/mach_commands.py @@ -45,7 +45,6 @@ class TalosRunner(MozbuildObject): def init_variables(self, talos_args): self.talos_dir = os.path.join(self.topsrcdir, 'testing', 'talos') - self.talos_webroot = os.path.join(self.topobjdir, 'testing', 'talos') self.mozharness_dir = os.path.join(self.topsrcdir, 'testing', 'mozharness') self.config_dir = os.path.join(self.mozharness_dir, 'configs', 'talos') @@ -79,16 +78,13 @@ class TalosRunner(MozbuildObject): 'create-virtualenv', 'run-tests', ], - 'python_webserver': False, 'talos_extra_options': ['--develop'] + self.talos_args, } def make_args(self): self.args = { - 'config': { - 'webroot': self.talos_webroot, - }, - 'initial_config_file': self.config_file_path, + 'config': {}, + 'initial_config_file': self.config_file_path, } def write_config(self): diff --git a/testing/talos/talos.json b/testing/talos/talos.json index a21ecf7af7cb..90c8d92c9a87 100644 --- a/testing/talos/talos.json +++ b/testing/talos/talos.json @@ -44,29 +44,21 @@ }, "g1": { "tests": ["tp5o_scroll", "glterrain"], - "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip", - "pagesets_parent_dir_path": "talos/page_load_test/", - "pagesets_manifest_path": "talos/page_load_test/tp5n/tp5o.manifest" + "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip" }, "g1-e10s": { "tests": ["tp5o_scroll", "glterrain"], "talos_options": ["--e10s"], - "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip", - "pagesets_parent_dir_path": "talos/page_load_test/", - "pagesets_manifest_path": "talos/page_load_test/tp5n/tp5o.manifest" + "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip" }, "g2": { "tests": ["damp", "tps"], - "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip", - "pagesets_parent_dir_path": "talos/page_load_test/", - "pagesets_manifest_path": "talos/page_load_test/tp5n/tp5o.manifest" + "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip" }, "g2-e10s": { "tests": ["damp", "tps"], "talos_options": ["--e10s"], - "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip", - "pagesets_parent_dir_path": "talos/page_load_test/", - "pagesets_manifest_path": "talos/page_load_test/tp5n/tp5o.manifest" + "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip" }, "g3": { "tests": ["dromaeo_dom"] @@ -83,22 +75,16 @@ }, "tp5o": { "tests": ["tp5o"], - "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip", - "pagesets_parent_dir_path": "talos/page_load_test/", - "pagesets_manifest_path": "talos/page_load_test/tp5n/tp5o.manifest" + "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip" }, "tp5o-e10s": { "tests": ["tp5o"], "talos_options": ["--e10s"], - "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip", - "pagesets_parent_dir_path": "talos/page_load_test/", - "pagesets_manifest_path": "talos/page_load_test/tp5n/tp5o.manifest" + "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip" }, "xperf": { "tests": ["tp5n"], "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip", - "pagesets_parent_dir_path": "talos/page_load_test/", - "pagesets_manifest_path": "talos/page_load_test/tp5n/tp5n.manifest", "talos_options": [ "--xperf_path", "\"c:/Program Files/Microsoft Windows Performance Toolkit/xperf.exe\"" @@ -107,8 +93,6 @@ "xperf-e10s": { "tests": ["tp5n"], "pagesets_url": "http://talos-bundles.pvt.build.mozilla.org/zips/tp5n.zip", - "pagesets_parent_dir_path": "talos/page_load_test/", - "pagesets_manifest_path": "talos/page_load_test/tp5n/tp5n.manifest", "talos_options": [ "--e10s", "--xperf_path", diff --git a/testing/talos/talos/cmdline.py b/testing/talos/talos/cmdline.py index 46cff2fde4ae..43411b6c5d96 100644 --- a/testing/talos/talos/cmdline.py +++ b/testing/talos/talos/cmdline.py @@ -98,12 +98,11 @@ def create_parser(mach_interface=False): metavar="PREF=VALUE", help="defines an extra user preference") add_arg('--webServer', dest='webserver', - help="address of the webserver hosting the talos files") + help="DEPRECATED") if not mach_interface: add_arg('--develop', action='store_true', default=False, help="useful for running tests on a developer machine." - " Creates a local webserver and doesn't upload to the" - " graph servers.") + " Doesn't upload to the graph servers.") add_arg('--responsiveness', action='store_true', help="turn on responsiveness collection") add_arg("--cycles", type=int, diff --git a/testing/talos/talos/config.py b/testing/talos/talos/config.py index 1c66413ae7e8..e88652c7ebc6 100644 --- a/testing/talos/talos/config.py +++ b/testing/talos/talos/config.py @@ -252,9 +252,15 @@ def fix_xperf(config): @validator -def check_webserver(config): - if config['develop'] and not config['webserver']: - config['webserver'] = 'localhost:15707' +def set_webserver(config): + # pick a free port + import socket + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('', 0)) + port = sock.getsockname()[1] + sock.close() + + config['webserver'] = 'localhost:%d' % port @validator @@ -338,8 +344,9 @@ def build_manifest(config, manifestName): # write modified manifest lines with open(manifestName + '.develop', 'w') as newHandle: for line in manifestLines: - newHandle.write(line.replace('localhost', - config['webserver'])) + newline = line.replace('localhost', config['webserver']) + newline = newline.replace('page_load_test', 'tests') + newHandle.write(newline) newManifestName = manifestName + '.develop' @@ -368,7 +375,7 @@ def get_test(config, global_overrides, counters, test_instance): # fix up tpmanifest tpmanifest = getattr(test_instance, 'tpmanifest', None) - if tpmanifest and config.get('develop'): + if tpmanifest: test_instance.tpmanifest = \ build_manifest(config, utils.interpolate(tpmanifest)) @@ -405,7 +412,7 @@ def tests(config): def get_browser_config(config): required = ('preferences', 'extensions', 'browser_path', 'browser_wait', - 'extra_args', 'buildid', 'env', 'init_url') + 'extra_args', 'buildid', 'env', 'init_url', 'webserver') optional = {'bcontroller_config': '${talos}/bcontroller.json', 'branch_name': '', 'child_process': 'plugin-container', @@ -417,7 +424,6 @@ def get_browser_config(config): 'symbols_path': None, 'test_name_extension': '', 'test_timeout': 1200, - 'webserver': '', 'xperf_path': None, 'error_filename': None, } diff --git a/testing/talos/talos/filter.py b/testing/talos/talos/filter.py index 06fb25e58b35..88b63777cebd 100644 --- a/testing/talos/talos/filter.py +++ b/testing/talos/talos/filter.py @@ -94,7 +94,7 @@ def dromaeo(series): dromaeo: https://wiki.mozilla.org/Dromaeo, pull the internal calculation out * This is for 'runs/s' based tests, not 'ms' tests. - * chunksize: defined in dromaeo: page_load_test/dromaeo/webrunner.js#l8 + * chunksize: defined in dromaeo: tests/dromaeo/webrunner.js#l8 """ means = [] chunksize = 5 diff --git a/testing/talos/talos/generate-tart-xpi.html b/testing/talos/talos/generate-tart-xpi.html index 69f0f69174ef..ccc1de103bef 100644 --- a/testing/talos/talos/generate-tart-xpi.html +++ b/testing/talos/talos/generate-tart-xpi.html @@ -11,7 +11,7 @@ - + shutdown script diff --git a/testing/talos/talos/page_load_test/canvasmark/canvasmark.manifest b/testing/talos/talos/page_load_test/canvasmark/canvasmark.manifest deleted file mode 100644 index 08f10e00fb74..000000000000 --- a/testing/talos/talos/page_load_test/canvasmark/canvasmark.manifest +++ /dev/null @@ -1,3 +0,0 @@ -% http://localhost/page_load_test/canvasmark/index.html?auto=true - - diff --git a/testing/talos/talos/page_load_test/dromaeo/css.manifest b/testing/talos/talos/page_load_test/dromaeo/css.manifest deleted file mode 100644 index d4e489e78f44..000000000000 --- a/testing/talos/talos/page_load_test/dromaeo/css.manifest +++ /dev/null @@ -1,6 +0,0 @@ -% http://localhost/page_load_test/dromaeo/cssquery-dojo.html -% http://localhost/page_load_test/dromaeo/cssquery-ext.html -% http://localhost/page_load_test/dromaeo/cssquery-jquery.html -% http://localhost/page_load_test/dromaeo/cssquery-mootools.html -% http://localhost/page_load_test/dromaeo/cssquery-prototype.html -% http://localhost/page_load_test/dromaeo/cssquery-yui.html diff --git a/testing/talos/talos/page_load_test/dromaeo/dom.manifest b/testing/talos/talos/page_load_test/dromaeo/dom.manifest deleted file mode 100644 index b1f2099ac580..000000000000 --- a/testing/talos/talos/page_load_test/dromaeo/dom.manifest +++ /dev/null @@ -1,4 +0,0 @@ -% http://localhost/page_load_test/dromaeo/dom-attr.html -% http://localhost/page_load_test/dromaeo/dom-modify.html -% http://localhost/page_load_test/dromaeo/dom-query.html -% http://localhost/page_load_test/dromaeo/dom-traverse.html diff --git a/testing/talos/talos/page_load_test/kraken/kraken.manifest b/testing/talos/talos/page_load_test/kraken/kraken.manifest deleted file mode 100644 index f017c851a75d..000000000000 --- a/testing/talos/talos/page_load_test/kraken/kraken.manifest +++ /dev/null @@ -1,14 +0,0 @@ -% http://localhost/page_load_test/kraken/driver.html?testName=ai-astar -% http://localhost/page_load_test/kraken/driver.html?testName=audio-beat-detection -% http://localhost/page_load_test/kraken/driver.html?testName=audio-dft -% http://localhost/page_load_test/kraken/driver.html?testName=audio-fft -% http://localhost/page_load_test/kraken/driver.html?testName=audio-oscillator -% http://localhost/page_load_test/kraken/driver.html?testName=imaging-gaussian-blur -% http://localhost/page_load_test/kraken/driver.html?testName=imaging-darkroom -% http://localhost/page_load_test/kraken/driver.html?testName=imaging-desaturate -% http://localhost/page_load_test/kraken/driver.html?testName=json-parse-financial -% http://localhost/page_load_test/kraken/driver.html?testName=json-stringify-tinderbox -% http://localhost/page_load_test/kraken/driver.html?testName=stanford-crypto-aes -% http://localhost/page_load_test/kraken/driver.html?testName=stanford-crypto-ccm -% http://localhost/page_load_test/kraken/driver.html?testName=stanford-crypto-pbkdf2 -% http://localhost/page_load_test/kraken/driver.html?testName=stanford-crypto-sha256-iterative diff --git a/testing/talos/talos/page_load_test/scroll/scroll.manifest b/testing/talos/talos/page_load_test/scroll/scroll.manifest deleted file mode 100644 index 1c832dea6c07..000000000000 --- a/testing/talos/talos/page_load_test/scroll/scroll.manifest +++ /dev/null @@ -1,6 +0,0 @@ -% http://localhost/page_load_test/scroll/tiled.html -% http://localhost/page_load_test/scroll/tiled-fixed.html -% http://localhost/page_load_test/scroll/tiled-downscale.html -% http://localhost/page_load_test/scroll/tiled-fixed-downscale.html -% http://localhost/page_load_test/scroll/iframe.svg -% http://localhost/page_load_test/scroll/reader.htm diff --git a/testing/talos/talos/page_load_test/svg_opacity/svg_opacity.manifest b/testing/talos/talos/page_load_test/svg_opacity/svg_opacity.manifest deleted file mode 100644 index ef13cebe9d4e..000000000000 --- a/testing/talos/talos/page_load_test/svg_opacity/svg_opacity.manifest +++ /dev/null @@ -1,4 +0,0 @@ -# opacity tests - -http://localhost/page_load_test/svg_opacity/big-optimizable-group-opacity-2500.svg -http://localhost/page_load_test/svg_opacity/small-group-opacity-2500.svg diff --git a/testing/talos/talos/page_load_test/svgx/svgm.manifest b/testing/talos/talos/page_load_test/svgx/svgm.manifest deleted file mode 100644 index 5c6448cd65db..000000000000 --- a/testing/talos/talos/page_load_test/svgx/svgm.manifest +++ /dev/null @@ -1,17 +0,0 @@ -# gearflowers image -http://localhost/page_load_test/svgx/gearflowers.svg - -# some generic image compositing tests -http://localhost/page_load_test/svgx/composite-scale.svg -http://localhost/page_load_test/svgx/composite-scale-opacity.svg -http://localhost/page_load_test/svgx/composite-scale-rotate.svg -http://localhost/page_load_test/svgx/composite-scale-rotate-opacity.svg - -# Painting multiple complex paths -% http://localhost/page_load_test/svgx/hixie-001.xml -# Painting text -% http://localhost/page_load_test/svgx/hixie-003.xml -# Painting linear gradients -% http://localhost/page_load_test/svgx/hixie-005.xml -# World Map -% http://localhost/page_load_test/svgx/hixie-007.xml diff --git a/testing/talos/talos/page_load_test/svgx/svgx.manifest b/testing/talos/talos/page_load_test/svgx/svgx.manifest deleted file mode 100644 index f37d382f4af2..000000000000 --- a/testing/talos/talos/page_load_test/svgx/svgx.manifest +++ /dev/null @@ -1,23 +0,0 @@ -# gearflowers image -http://localhost/page_load_test/svgx/gearflowers.svg - -# some generic image compositing tests -http://localhost/page_load_test/svgx/composite-scale.svg -http://localhost/page_load_test/svgx/composite-scale-opacity.svg -http://localhost/page_load_test/svgx/composite-scale-rotate.svg -http://localhost/page_load_test/svgx/composite-scale-rotate-opacity.svg - -# Painting multiple complex paths -% http://localhost/page_load_test/svgx/hixie-001.xml -# Painting multiple complex paths with transparency -% http://localhost/page_load_test/svgx/hixie-002.xml -# Painting text -% http://localhost/page_load_test/svgx/hixie-003.xml -# Painting images -% http://localhost/page_load_test/svgx/hixie-004.xml -# Painting linear gradients -% http://localhost/page_load_test/svgx/hixie-005.xml -# Painting radial gradients -% http://localhost/page_load_test/svgx/hixie-006.xml -# World Map -% http://localhost/page_load_test/svgx/hixie-007.xml diff --git a/testing/talos/talos/page_load_test/v8_7/v8.manifest b/testing/talos/talos/page_load_test/v8_7/v8.manifest deleted file mode 100644 index b19e646abd83..000000000000 --- a/testing/talos/talos/page_load_test/v8_7/v8.manifest +++ /dev/null @@ -1 +0,0 @@ -% http://localhost/page_load_test/v8_7/run.html diff --git a/testing/talos/talos/page_load_test/webgl/glterrain.manifest b/testing/talos/talos/page_load_test/webgl/glterrain.manifest deleted file mode 100644 index 4acf70055616..000000000000 --- a/testing/talos/talos/page_load_test/webgl/glterrain.manifest +++ /dev/null @@ -1 +0,0 @@ -% http://localhost/page_load_test/webgl/benchmarks/terrain/perftest.html diff --git a/testing/talos/talos/pageloader/chrome/Profiler.js b/testing/talos/talos/pageloader/chrome/Profiler.js index d21dce8e39c1..bffd50427b83 100644 --- a/testing/talos/talos/pageloader/chrome/Profiler.js +++ b/testing/talos/talos/pageloader/chrome/Profiler.js @@ -5,8 +5,8 @@ // - NOTE: This file is duplicated verbatim at: // - talos/scripts/Profiler.js // - talos/pageloader/chrome/Profiler.js -// - talos/page_load_test/devtools/addon/content/Profiler.js -// - talos/page_load_test/tart/addon/content/Profiler.js +// - talos/tests/devtools/addon/content/Profiler.js +// - talos/tests/tart/addon/content/Profiler.js // - talos/startup_test/tresize/addon/content/Profiler.js // // - Please keep these copies in sync. diff --git a/testing/talos/talos/pageloader/chrome/tscroll.js b/testing/talos/talos/pageloader/chrome/tscroll.js index 22f4fb0d26d4..232c32363326 100644 --- a/testing/talos/talos/pageloader/chrome/tscroll.js +++ b/testing/talos/talos/pageloader/chrome/tscroll.js @@ -1,5 +1,5 @@ // Note: The content from here upto '// End scroll test' is duplicated at: -// - talos/page_load_test/scroll/scroll-test.js +// - talos/tests/scroll/scroll-test.js // - inside talos/pageloader/chrome/tscroll.js // // - Please keep these copies in sync. diff --git a/testing/talos/talos/run_tests.py b/testing/talos/talos/run_tests.py index 53d97a138031..9aff92fa8d7f 100755 --- a/testing/talos/talos/run_tests.py +++ b/testing/talos/talos/run_tests.py @@ -12,8 +12,8 @@ import sys import time import traceback import urllib -import urlparse import utils +import mozhttpd from talos.results import TalosResults from talos.ttest import TTest @@ -68,28 +68,10 @@ def buildCommandLine(test): def setup_webserver(webserver): """use mozhttpd to setup a webserver""" + logging.info("starting webserver on %r" % webserver) - scheme = "http://" - if (webserver.startswith('http://') or - webserver.startswith('chrome://') or - webserver.startswith('file:///')): # noqa - - scheme = "" - elif '://' in webserver: - print "Unable to parse user defined webserver: '%s'" % (webserver) - sys.exit(2) - - url = urlparse.urlparse('%s%s' % (scheme, webserver)) - port = url.port - - if port: - import mozhttpd - return mozhttpd.MozHttpd(host=url.hostname, port=int(port), - docroot=here) - else: - print ("WARNING: unable to start web server without custom port" - " configured") - return None + host, port = webserver.split(':') + return mozhttpd.MozHttpd(host=host, port=int(port), docroot=here) def run_tests(config, browser_config): @@ -120,6 +102,8 @@ def run_tests(config, browser_config): test['cleanup'] = utils.interpolate(test['cleanup']) # pass --no-remote to firefox launch, if --develop is specified + # we do that to allow locally the user to have another running firefox + # instance if browser_config['develop']: browser_config['extra_args'] = '--no-remote' @@ -195,52 +179,41 @@ def run_tests(config, browser_config): ) talos_results.check_output_formats(results_urls) - # setup a webserver, if --develop is specified - httpd = None - if browser_config['develop']: - httpd = setup_webserver(browser_config['webserver']) - if httpd: - httpd.start() + httpd = setup_webserver(browser_config['webserver']) + httpd.start() - # run the tests - timer = utils.Timer() - logging.info("Starting test suite %s", title) - for test in tests: - testname = test['name'] - testtimer = utils.Timer() - logging.info("Starting test %s", testname) + testname = None + try: + # run the tests + timer = utils.Timer() + logging.info("Starting test suite %s", title) + for test in tests: + testname = test['name'] + testtimer = utils.Timer() + logging.info("Starting test %s", testname) - try: mytest = TTest() - if mytest: - talos_results.add(mytest.runTest(browser_config, test)) - else: - logging.error("Error found while running %s", testname) - except TalosRegression: - logging.error("Detected a regression for %s", testname) - if httpd: - httpd.stop() - # by returning 1, we report an orange to buildbot - # http://docs.buildbot.net/latest/developer/results.html - return 1 - except (TalosCrash, TalosError): - # NOTE: if we get into this condition, talos has an internal - # problem and cannot continue - # this will prevent future tests from running - traceback.print_exception(*sys.exc_info()) - if httpd: - httpd.stop() - # indicate a failure to buildbot, turn the job red - return 2 + talos_results.add(mytest.runTest(browser_config, test)) - logging.info("Completed test %s (%s)", testname, testtimer.elapsed()) + logging.info("Completed test %s (%s)", testname, testtimer.elapsed()) + + except TalosRegression: + logging.error("Detected a regression for %s", testname) + # by returning 1, we report an orange to buildbot + # http://docs.buildbot.net/latest/developer/results.html + return 1 + except (TalosCrash, TalosError): + # NOTE: if we get into this condition, talos has an internal + # problem and cannot continue + # this will prevent future tests from running + traceback.print_exception(*sys.exc_info()) + # indicate a failure to buildbot, turn the job red + return 2 + finally: + httpd.stop() logging.info("Completed test suite (%s)", timer.elapsed()) - # stop the webserver if running - if httpd: - httpd.stop() - # output results if results_urls: talos_results.output(results_urls) diff --git a/testing/talos/talos/scripts/Profiler.js b/testing/talos/talos/scripts/Profiler.js index d21dce8e39c1..bffd50427b83 100644 --- a/testing/talos/talos/scripts/Profiler.js +++ b/testing/talos/talos/scripts/Profiler.js @@ -5,8 +5,8 @@ // - NOTE: This file is duplicated verbatim at: // - talos/scripts/Profiler.js // - talos/pageloader/chrome/Profiler.js -// - talos/page_load_test/devtools/addon/content/Profiler.js -// - talos/page_load_test/tart/addon/content/Profiler.js +// - talos/tests/devtools/addon/content/Profiler.js +// - talos/tests/tart/addon/content/Profiler.js // - talos/startup_test/tresize/addon/content/Profiler.js // // - Please keep these copies in sync. diff --git a/testing/talos/talos/startup_test/media/html/media_tests.html b/testing/talos/talos/startup_test/media/html/media_tests.html index 1c58ac8692ec..f8e18df0b247 100644 --- a/testing/talos/talos/startup_test/media/html/media_tests.html +++ b/testing/talos/talos/startup_test/media/html/media_tests.html @@ -17,7 +17,7 @@
- + diff --git a/testing/talos/talos/startup_test/sessionrestore/index.html b/testing/talos/talos/startup_test/sessionrestore/index.html index 3a6320bf9e1a..06ab78933618 100755 --- a/testing/talos/talos/startup_test/sessionrestore/index.html +++ b/testing/talos/talos/startup_test/sessionrestore/index.html @@ -5,7 +5,7 @@ Session Restore Regression Test - + diff --git a/testing/talos/talos/startup_test/tpaint.html b/testing/talos/talos/startup_test/tpaint.html index 1943e5a4d7c0..de8ab614f43a 100644 --- a/testing/talos/talos/startup_test/tpaint.html +++ b/testing/talos/talos/startup_test/tpaint.html @@ -2,7 +2,7 @@ - + - + - +