зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to b2g-inbound
This commit is contained in:
Коммит
649363b3d7
|
@ -1188,6 +1188,10 @@ pref("security.sandbox.windows.log", false);
|
|||
pref("dom.ipc.plugins.sandbox.default", false);
|
||||
pref("dom.ipc.plugins.sandbox.flash", true);
|
||||
|
||||
// This controls whether the Windows NPAPI process sandbox is using a more
|
||||
// strict sandboxing policy. This will require a restart.
|
||||
pref("dom.ipc.plugins.moreStrictSandbox", false);
|
||||
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
// This controls whether the Windows content process sandbox is using a more
|
||||
// strict sandboxing policy. This will require a restart.
|
||||
|
|
|
@ -16,7 +16,7 @@ Cu.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommon);
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
|
||||
const PREF_SYNC_SHOW_CUSTOMIZATION = "services.sync.ui.showCustomizationDialog";
|
||||
const PREF_SYNC_SHOW_CUSTOMIZATION = "services.sync-setup.ui.showCustomizationDialog";
|
||||
|
||||
const ACTION_URL_PARAM = "action";
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#endif
|
||||
|
||||
Cu.import("resource://gre/modules/NewTabUtils.jsm");
|
||||
|
||||
/**
|
||||
* Keeps thumbnails of open web pages up-to-date.
|
||||
*/
|
||||
|
|
|
@ -3872,7 +3872,10 @@ function OpenBrowserWindow(options)
|
|||
}
|
||||
|
||||
if (options && options.remote) {
|
||||
let omtcEnabled = gPrefService.getBoolPref("layers.offmainthreadcomposition.enabled");
|
||||
// If we're using remote tabs by default, then OMTC will be force-enabled,
|
||||
// despite the preference returning as false.
|
||||
let omtcEnabled = gPrefService.getBoolPref("layers.offmainthreadcomposition.enabled")
|
||||
|| Services.appinfo.browserTabsRemoteAutostart;
|
||||
if (!omtcEnabled) {
|
||||
alert("To use out-of-process tabs, you must set the layers.offmainthreadcomposition.enabled preference and restart. Opening a normal window instead.");
|
||||
} else {
|
||||
|
|
|
@ -33,8 +33,12 @@ const PREF_LOG_LEVEL = "browser.uitour.loglevel";
|
|||
const PREF_SEENPAGEIDS = "browser.uitour.seenPageIDs";
|
||||
|
||||
const BACKGROUND_PAGE_ACTIONS_ALLOWED = new Set([
|
||||
"endUrlbarCapture",
|
||||
"getConfiguration",
|
||||
"getTreatmentTag",
|
||||
"hideHighlight",
|
||||
"hideInfo",
|
||||
"hideMenu",
|
||||
"ping",
|
||||
"registerPageID",
|
||||
"setConfiguration",
|
||||
|
|
|
@ -505,7 +505,7 @@ IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
|
|||
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadlessNativeGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
WebGLContext* webgl)
|
||||
bool requireCompatProfile, WebGLContext* webgl)
|
||||
{
|
||||
if (!forceEnabled &&
|
||||
IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL))
|
||||
|
@ -515,7 +515,7 @@ CreateHeadlessNativeGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<GLContext> gl = gl::GLContextProvider::CreateHeadless();
|
||||
nsRefPtr<GLContext> gl = gl::GLContextProvider::CreateHeadless(requireCompatProfile);
|
||||
if (!gl) {
|
||||
webgl->GenerateWarning("Error during native OpenGL init.");
|
||||
return nullptr;
|
||||
|
@ -530,7 +530,7 @@ CreateHeadlessNativeGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
|||
// Eventually, we want to be able to pick ANGLE-EGL or native EGL.
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadlessANGLE(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
WebGLContext* webgl)
|
||||
bool requireCompatProfile, WebGLContext* webgl)
|
||||
{
|
||||
nsRefPtr<GLContext> gl;
|
||||
|
||||
|
@ -543,7 +543,7 @@ CreateHeadlessANGLE(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless();
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless(requireCompatProfile);
|
||||
if (!gl) {
|
||||
webgl->GenerateWarning("Error during ANGLE OpenGL init.");
|
||||
return nullptr;
|
||||
|
@ -555,13 +555,13 @@ CreateHeadlessANGLE(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
|||
}
|
||||
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadlessEGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
CreateHeadlessEGL(bool forceEnabled, bool requireCompatProfile,
|
||||
WebGLContext* webgl)
|
||||
{
|
||||
nsRefPtr<GLContext> gl;
|
||||
|
||||
#ifdef ANDROID
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless();
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless(requireCompatProfile);
|
||||
if (!gl) {
|
||||
webgl->GenerateWarning("Error during EGL OpenGL init.");
|
||||
return nullptr;
|
||||
|
@ -583,16 +583,22 @@ CreateHeadlessGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
|||
if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"))
|
||||
disableANGLE = true;
|
||||
|
||||
bool requireCompatProfile = webgl->IsWebGL2() ? false : true;
|
||||
|
||||
nsRefPtr<GLContext> gl;
|
||||
|
||||
if (preferEGL)
|
||||
gl = CreateHeadlessEGL(forceEnabled, gfxInfo, webgl);
|
||||
gl = CreateHeadlessEGL(forceEnabled, requireCompatProfile, webgl);
|
||||
|
||||
if (!gl && !disableANGLE)
|
||||
gl = CreateHeadlessANGLE(forceEnabled, gfxInfo, webgl);
|
||||
if (!gl && !disableANGLE) {
|
||||
gl = CreateHeadlessANGLE(forceEnabled, gfxInfo, requireCompatProfile,
|
||||
webgl);
|
||||
}
|
||||
|
||||
if (!gl)
|
||||
gl = CreateHeadlessNativeGL(forceEnabled, gfxInfo, webgl);
|
||||
if (!gl) {
|
||||
gl = CreateHeadlessNativeGL(forceEnabled, gfxInfo,
|
||||
requireCompatProfile, webgl);
|
||||
}
|
||||
|
||||
return gl.forget();
|
||||
}
|
||||
|
|
|
@ -1209,9 +1209,10 @@ protected:
|
|||
|
||||
// -------------------------------------------------------------------------
|
||||
// WebGL 2 specifics (implemented in WebGL2Context.cpp)
|
||||
|
||||
public:
|
||||
virtual bool IsWebGL2() const = 0;
|
||||
|
||||
protected:
|
||||
bool InitWebGL2();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
@ -2107,7 +2107,6 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
|
|||
|
||||
// if we're reading alpha, we may need to do fixup. Note that we don't allow
|
||||
// GL_ALPHA to readpixels currently, but we had the code written for it already.
|
||||
|
||||
const bool formatHasAlpha = format == LOCAL_GL_ALPHA ||
|
||||
format == LOCAL_GL_RGBA;
|
||||
if (!formatHasAlpha)
|
||||
|
|
|
@ -1117,11 +1117,12 @@ WebGLContext::AssertCachedState()
|
|||
AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_CLEAR_VALUE, mStencilClearValue);
|
||||
|
||||
GLint stencilBits = 0;
|
||||
gl->fGetIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits);
|
||||
const GLuint stencilRefMask = (1 << stencilBits) - 1;
|
||||
if (GetStencilBits(&stencilBits)) {
|
||||
const GLuint stencilRefMask = (1 << stencilBits) - 1;
|
||||
|
||||
AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_REF, stencilRefMask, mStencilRefFront);
|
||||
AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_REF, stencilRefMask, mStencilRefBack);
|
||||
AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_REF, stencilRefMask, mStencilRefFront);
|
||||
AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_REF, stencilRefMask, mStencilRefBack);
|
||||
}
|
||||
|
||||
AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK, mStencilValueMaskFront);
|
||||
AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, mStencilValueMaskBack);
|
||||
|
|
|
@ -1779,8 +1779,8 @@ WebGLContext::InitAndValidateGL()
|
|||
|
||||
MakeContextCurrent();
|
||||
|
||||
// on desktop OpenGL, we always keep vertex attrib 0 array enabled
|
||||
if (!gl->IsGLES())
|
||||
// For OpenGL compat. profiles, we always keep vertex attrib 0 array enabled.
|
||||
if (gl->IsCompatibilityProfile())
|
||||
gl->fEnableVertexAttribArray(0);
|
||||
|
||||
if (MinCapabilityMode())
|
||||
|
@ -1889,7 +1889,7 @@ WebGLContext::InitAndValidateGL()
|
|||
// Always 1 for GLES2
|
||||
mMaxFramebufferColorAttachments = 1;
|
||||
|
||||
if (!gl->IsGLES()) {
|
||||
if (gl->IsCompatibilityProfile()) {
|
||||
// gl_PointSize is always available in ES2 GLSL, but has to be
|
||||
// specifically enabled on desktop GLSL.
|
||||
gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
|
|
@ -3303,6 +3303,15 @@ bool HTMLMediaElement::ShouldCheckAllowOrigin()
|
|||
return mCORSMode != CORS_NONE;
|
||||
}
|
||||
|
||||
bool HTMLMediaElement::IsCORSSameOrigin()
|
||||
{
|
||||
bool subsumes;
|
||||
nsRefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
|
||||
return
|
||||
(NS_SUCCEEDED(NodePrincipal()->Subsumes(principal, &subsumes)) && subsumes) ||
|
||||
ShouldCheckAllowOrigin();
|
||||
}
|
||||
|
||||
void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame)
|
||||
{
|
||||
mLastNextFrameStatus = aNextFrame;
|
||||
|
@ -3648,22 +3657,13 @@ void HTMLMediaElement::NotifyDecoderPrincipalChanged()
|
|||
{
|
||||
nsRefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
|
||||
|
||||
bool subsumes;
|
||||
mDecoder->UpdateSameOriginStatus(
|
||||
!principal ||
|
||||
(NS_SUCCEEDED(NodePrincipal()->Subsumes(principal, &subsumes)) && subsumes) ||
|
||||
mCORSMode != CORS_NONE);
|
||||
mDecoder->UpdateSameOriginStatus(!principal || IsCORSSameOrigin());
|
||||
|
||||
for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
|
||||
OutputMediaStream* ms = &mOutputStreams[i];
|
||||
ms->mStream->SetCORSMode(mCORSMode);
|
||||
ms->mStream->CombineWithPrincipal(principal);
|
||||
}
|
||||
#ifdef MOZ_EME
|
||||
if (mMediaKeys && NS_FAILED(mMediaKeys->CheckPrincipals())) {
|
||||
mMediaKeys->Shutdown();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HTMLMediaElement::UpdateMediaSize(nsIntSize size)
|
||||
|
@ -4307,8 +4307,6 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
|
|||
if (mDecoder) {
|
||||
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
|
||||
}
|
||||
// Update the same-origin status.
|
||||
NotifyDecoderPrincipalChanged();
|
||||
}
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return promise.forget();
|
||||
|
@ -4341,8 +4339,13 @@ void
|
|||
HTMLMediaElement::DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
|
||||
const nsAString& aInitDataType)
|
||||
{
|
||||
nsRefPtr<MediaEncryptedEvent> event(
|
||||
MediaEncryptedEvent::Constructor(this, aInitDataType, aInitData));
|
||||
nsRefPtr<MediaEncryptedEvent> event;
|
||||
if (IsCORSSameOrigin()) {
|
||||
event = MediaEncryptedEvent::Constructor(this, aInitDataType, aInitData);
|
||||
} else {
|
||||
event = MediaEncryptedEvent::Constructor(this);
|
||||
}
|
||||
|
||||
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
|
||||
new AsyncEventDispatcher(this, event);
|
||||
asyncDispatcher->PostDOMEvent();
|
||||
|
|
|
@ -245,6 +245,10 @@ public:
|
|||
// Check if the media element had crossorigin set when loading started
|
||||
bool ShouldCheckAllowOrigin();
|
||||
|
||||
// Returns true if the currently loaded resource is CORS same-origin with
|
||||
// respect to the document.
|
||||
bool IsCORSSameOrigin();
|
||||
|
||||
// Is the media element potentially playing as defined by the HTML 5 specification.
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#potentially-playing
|
||||
bool IsPotentiallyPlaying() const;
|
||||
|
|
|
@ -4,7 +4,18 @@ BlockMixedDisplayContent = Blocked loading mixed display content "%1$S"
|
|||
BlockMixedActiveContent = Blocked loading mixed active content "%1$S"
|
||||
|
||||
# CORS
|
||||
CrossSiteRequestBlocked=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. This can be fixed by moving the resource to the same domain or enabling CORS.
|
||||
# LOCALIZATION NOTE: Do not translate "Access-Control-Allow-Origin", Access-Control-Allow-Credentials, Access-Control-Allow-Methods, Access-Control-Allow-Headers
|
||||
CORSDisabled=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS disabled).
|
||||
CORSRequestFailed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request failed).
|
||||
CORSRequestNotHttp=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request not http).
|
||||
CORSMissingAllowOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header 'Access-Control-Allow-Origin' missing).
|
||||
CORSAllowOriginNotMatchingOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header 'Access-Control-Allow-Origin' does not match '%2$S').
|
||||
CORSMethodNotFound=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: Did not find method in CORS header 'Access-Control-Allow-Methods').
|
||||
CORSMissingAllowCredentials=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: expected 'true' in CORS header 'Access-Control-Allow-Credentials').
|
||||
CORSPreflightDidNotSucceed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS preflight channel did not succeed).
|
||||
CORSInvalidAllowMethod=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: invalid token '%2$S' in CORS header 'Access-Control-Allow-Methods').
|
||||
CORSInvalidAllowHeader=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: invalid token '%2$S' in CORS header 'Access-Control-Allow-Headers').
|
||||
CORSMissingAllowHeaderFromPreflight=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: missing token '%2$S' in CORS header 'Access-Control-Allow-Headers' from CORS preflight channel"));
|
||||
|
||||
# LOCALIZATION NOTE: Do not translate "Strict-Transport-Security" or "HSTS"
|
||||
InvalidSTSHeaders=The site specified an invalid Strict-Transport-Security header.
|
||||
|
|
|
@ -1578,9 +1578,9 @@ void MediaDecoderStateMachine::StartDecoding()
|
|||
|
||||
void MediaDecoderStateMachine::StartWaitForResources()
|
||||
{
|
||||
NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
|
||||
"Should be on state machine or decode thread.");
|
||||
AssertCurrentThreadInMonitor();
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(OnDecodeThread(),
|
||||
"Should be on decode thread.");
|
||||
SetState(DECODER_STATE_WAIT_FOR_RESOURCES);
|
||||
DECODER_LOG("StartWaitForResources");
|
||||
}
|
||||
|
@ -1967,7 +1967,7 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
|
|||
SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%d",
|
||||
IsAudioDecoding(), mAudioRequestStatus);
|
||||
|
||||
if (mState >= DECODER_STATE_COMPLETED) {
|
||||
if (mState >= DECODER_STATE_COMPLETED || mState == DECODER_STATE_DORMANT) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2012,7 +2012,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
|
|||
NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
|
||||
"Should be on state machine or decode thread.");
|
||||
|
||||
if (mState >= DECODER_STATE_COMPLETED) {
|
||||
if (mState >= DECODER_STATE_COMPLETED || mState == DECODER_STATE_DORMANT) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2179,28 +2179,28 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
|||
MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
|
||||
DECODER_LOG("Decoding Media Headers");
|
||||
|
||||
mReader->PreReadMetadata();
|
||||
|
||||
if (mReader->IsWaitingMediaResources()) {
|
||||
StartWaitForResources();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult res;
|
||||
MediaInfo info;
|
||||
bool isAwaitingResources = false;
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
res = mReader->ReadMetadata(&info, getter_Transfers(mMetadataTags));
|
||||
}
|
||||
mReader->PreReadMetadata();
|
||||
|
||||
if (NS_SUCCEEDED(res)) {
|
||||
if (mState == DECODER_STATE_DECODING_METADATA &&
|
||||
mReader->IsWaitingMediaResources()) {
|
||||
// change state to DECODER_STATE_WAIT_FOR_RESOURCES
|
||||
if (mReader->IsWaitingMediaResources()) {
|
||||
StartWaitForResources();
|
||||
// affect values only if ReadMetadata succeeds
|
||||
return NS_OK;
|
||||
}
|
||||
res = mReader->ReadMetadata(&info, getter_Transfers(mMetadataTags));
|
||||
isAwaitingResources = mReader->IsWaitingMediaResources();
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(res) &&
|
||||
mState == DECODER_STATE_DECODING_METADATA &&
|
||||
isAwaitingResources) {
|
||||
// change state to DECODER_STATE_WAIT_FOR_RESOURCES
|
||||
StartWaitForResources();
|
||||
// affect values only if ReadMetadata succeeds
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(res)) {
|
||||
|
@ -2766,12 +2766,11 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
|||
// Now that those threads are stopped, there's no possibility of
|
||||
// mPendingWakeDecoder being needed again. Revoke it.
|
||||
mPendingWakeDecoder = nullptr;
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
// Wait for the thread decoding, if any, to exit.
|
||||
DecodeTaskQueue()->AwaitIdle();
|
||||
mReader->ReleaseMediaResources();
|
||||
}
|
||||
DebugOnly<nsresult> rv = DecodeTaskQueue()->Dispatch(
|
||||
NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
mAudioRequestStatus = RequestStatus::Idle;
|
||||
mVideoRequestStatus = RequestStatus::Idle;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,15 @@ MediaEncryptedEvent::WrapObjectInternal(JSContext* aCx)
|
|||
return MediaEncryptedEventBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
already_AddRefed<MediaEncryptedEvent>
|
||||
MediaEncryptedEvent::Constructor(EventTarget* aOwner)
|
||||
{
|
||||
nsRefPtr<MediaEncryptedEvent> e = new MediaEncryptedEvent(aOwner);
|
||||
e->InitEvent(NS_LITERAL_STRING("encrypted"), false, false);
|
||||
e->SetTrusted(true);
|
||||
return e.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaEncryptedEvent>
|
||||
MediaEncryptedEvent::Constructor(EventTarget* aOwner,
|
||||
const nsAString& aInitDataType,
|
||||
|
|
|
@ -38,6 +38,9 @@ public:
|
|||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
static already_AddRefed<MediaEncryptedEvent>
|
||||
Constructor(EventTarget* aOwner);
|
||||
|
||||
static already_AddRefed<MediaEncryptedEvent>
|
||||
Constructor(EventTarget* aOwner,
|
||||
const nsAString& aInitDataType,
|
||||
|
|
|
@ -433,33 +433,6 @@ MediaKeys::Bind(HTMLMediaElement* aElement)
|
|||
}
|
||||
|
||||
mElement = aElement;
|
||||
nsresult rv = CheckPrincipals();
|
||||
if (NS_FAILED(rv)) {
|
||||
mElement = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaKeys::CheckPrincipals()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!IsBoundToMediaElement()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<nsIPrincipal> elementPrincipal(mElement->GetCurrentPrincipal());
|
||||
nsRefPtr<nsIPrincipal> elementTopLevelPrincipal(mElement->GetTopLevelPrincipal());
|
||||
if (!elementPrincipal ||
|
||||
!mPrincipal ||
|
||||
!elementPrincipal->Equals(mPrincipal) ||
|
||||
!elementTopLevelPrincipal ||
|
||||
!mTopLevelPrincipal ||
|
||||
!elementTopLevelPrincipal->Equals(mTopLevelPrincipal)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -117,10 +117,6 @@ public:
|
|||
// Returns true if this MediaKeys has been bound to a media element.
|
||||
bool IsBoundToMediaElement() const;
|
||||
|
||||
// Return NS_OK if the principals are the same as when the MediaKeys
|
||||
// was created, failure otherwise.
|
||||
nsresult CheckPrincipals();
|
||||
|
||||
private:
|
||||
|
||||
bool IsInPrivateBrowsing();
|
||||
|
|
|
@ -117,6 +117,9 @@ MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
|
|||
, mIsEncrypted(false)
|
||||
, mIndexReady(false)
|
||||
, mDemuxerMonitor("MP4 Demuxer")
|
||||
#if defined(XP_WIN)
|
||||
, mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_CTOR(MP4Reader);
|
||||
|
@ -252,15 +255,15 @@ private:
|
|||
#endif
|
||||
|
||||
void MP4Reader::RequestCodecResource() {
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
if(mVideo.mDecoder) {
|
||||
#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
|
||||
if (mVideo.mDecoder) {
|
||||
mVideo.mDecoder->AllocateMediaResources();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MP4Reader::IsWaitingOnCodecResource() {
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
|
||||
return mVideo.mDecoder && mVideo.mDecoder->IsWaitingMediaResources();
|
||||
#endif
|
||||
return false;
|
||||
|
@ -446,7 +449,8 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
|
|||
mVideo.mCallback = new DecoderCallback(this, kVideo);
|
||||
if (mSharedDecoderManager) {
|
||||
mVideo.mDecoder =
|
||||
mSharedDecoderManager->CreateVideoDecoder(video,
|
||||
mSharedDecoderManager->CreateVideoDecoder(mPlatform,
|
||||
video,
|
||||
mLayersBackendType,
|
||||
mDecoder->GetImageContainer(),
|
||||
mVideo.mTaskQueue,
|
||||
|
@ -1001,15 +1005,20 @@ MP4Reader::GetBuffered(dom::TimeRanges* aBuffered)
|
|||
|
||||
bool MP4Reader::IsDormantNeeded()
|
||||
{
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
return mVideo.mDecoder && mVideo.mDecoder->IsDormantNeeded();
|
||||
#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
|
||||
return
|
||||
#if defined(XP_WIN)
|
||||
mDormantEnabled &&
|
||||
#endif
|
||||
mVideo.mDecoder &&
|
||||
mVideo.mDecoder->IsDormantNeeded();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void MP4Reader::ReleaseMediaResources()
|
||||
{
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
|
||||
// Before freeing a video codec, all video buffers needed to be released
|
||||
// even from graphics pipeline.
|
||||
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
|
||||
|
@ -1024,7 +1033,7 @@ void MP4Reader::ReleaseMediaResources()
|
|||
|
||||
void MP4Reader::NotifyResourcesStatusChanged()
|
||||
{
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
|
||||
if (mDecoder) {
|
||||
mDecoder->NotifyWaitingForResourcesStatusChanged();
|
||||
}
|
||||
|
@ -1043,7 +1052,7 @@ MP4Reader::SetIdle()
|
|||
void
|
||||
MP4Reader::SetSharedDecoderManager(SharedDecoderManager* aManager)
|
||||
{
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
|
||||
mSharedDecoderManager = aManager;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -267,6 +267,10 @@ private:
|
|||
bool mIndexReady;
|
||||
Monitor mDemuxerMonitor;
|
||||
nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
|
||||
|
||||
#if defined(XP_WIN)
|
||||
const bool mDormantEnabled;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -55,11 +55,14 @@ public:
|
|||
};
|
||||
|
||||
SharedDecoderManager::SharedDecoderManager()
|
||||
: mActiveProxy(nullptr)
|
||||
: mTaskQueue(new MediaTaskQueue(GetMediaDecodeThreadPool()))
|
||||
, mActiveProxy(nullptr)
|
||||
, mActiveCallback(nullptr)
|
||||
, mWaitForInternalDrain(false)
|
||||
, mMonitor("SharedDecoderProxy")
|
||||
, mDecoderReleasedResources(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread.
|
||||
mCallback = new SharedDecoderCallback(this);
|
||||
}
|
||||
|
||||
|
@ -67,14 +70,18 @@ SharedDecoderManager::~SharedDecoderManager() {}
|
|||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
SharedDecoderManager::CreateVideoDecoder(
|
||||
PlatformDecoderModule* aPDM,
|
||||
const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aVideoTaskQueue, MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (!mDecoder) {
|
||||
nsRefPtr<PlatformDecoderModule> platform(PlatformDecoderModule::Create());
|
||||
mDecoder = platform->CreateVideoDecoder(
|
||||
aConfig, aLayersBackend, aImageContainer, aVideoTaskQueue, mCallback);
|
||||
// We use the manager's task queue for the decoder, rather than the one
|
||||
// passed in, so that none of the objects sharing the decoder can shutdown
|
||||
// the task queue while we're potentially still using it for a *different*
|
||||
// object also sharing the decoder.
|
||||
mDecoder = aPDM->CreateVideoDecoder(
|
||||
aConfig, aLayersBackend, aImageContainer, mTaskQueue, mCallback);
|
||||
if (!mDecoder) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -96,6 +103,11 @@ SharedDecoderManager::Select(SharedDecoderProxy* aProxy)
|
|||
|
||||
mActiveProxy = aProxy;
|
||||
mActiveCallback = aProxy->mCallback;
|
||||
|
||||
if (mDecoderReleasedResources) {
|
||||
mDecoder->AllocateMediaResources();
|
||||
mDecoderReleasedResources = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -125,6 +137,28 @@ SharedDecoderManager::DrainComplete()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedDecoderManager::ReleaseMediaResources()
|
||||
{
|
||||
mDecoderReleasedResources = true;
|
||||
mDecoder->ReleaseMediaResources();
|
||||
mActiveProxy = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
SharedDecoderManager::Shutdown()
|
||||
{
|
||||
if (mDecoder) {
|
||||
mDecoder->Shutdown();
|
||||
mDecoder = nullptr;
|
||||
}
|
||||
if (mTaskQueue) {
|
||||
mTaskQueue->BeginShutdown();
|
||||
mTaskQueue->AwaitShutdownAndIdle();
|
||||
mTaskQueue = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SharedDecoderProxy::SharedDecoderProxy(
|
||||
SharedDecoderManager* aManager, MediaDataDecoderCallback* aCallback)
|
||||
: mManager(aManager), mCallback(aCallback)
|
||||
|
@ -146,7 +180,6 @@ SharedDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
|
|||
mManager->Select(this);
|
||||
}
|
||||
return mManager->mDecoder->Input(aSample);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -193,7 +226,7 @@ void
|
|||
SharedDecoderProxy::ReleaseMediaResources()
|
||||
{
|
||||
if (mManager->mActiveProxy == this) {
|
||||
mManager->mDecoder->ReleaseMediaResources();
|
||||
mManager->ReleaseMediaResources();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
SharedDecoderManager();
|
||||
|
||||
already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
|
||||
PlatformDecoderModule* aPDM,
|
||||
const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer, MediaTaskQueue* aVideoTaskQueue,
|
||||
|
@ -33,6 +34,8 @@ public:
|
|||
void SetReader(MediaDecoderReader* aReader);
|
||||
void Select(SharedDecoderProxy* aProxy);
|
||||
void SetIdle(MediaDataDecoder* aProxy);
|
||||
void ReleaseMediaResources();
|
||||
void Shutdown();
|
||||
|
||||
friend class SharedDecoderProxy;
|
||||
friend class SharedDecoderCallback;
|
||||
|
@ -42,11 +45,13 @@ private:
|
|||
void DrainComplete();
|
||||
|
||||
nsRefPtr<MediaDataDecoder> mDecoder;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
SharedDecoderProxy* mActiveProxy;
|
||||
MediaDataDecoderCallback* mActiveCallback;
|
||||
nsAutoPtr<MediaDataDecoderCallback> mCallback;
|
||||
bool mWaitForInternalDrain;
|
||||
Monitor mMonitor;
|
||||
bool mDecoderReleasedResources;
|
||||
};
|
||||
|
||||
class SharedDecoderProxy : public MediaDataDecoder
|
||||
|
|
|
@ -173,7 +173,7 @@ protected:
|
|||
return true;
|
||||
}
|
||||
|
||||
mGLContext = GLContextProvider::CreateHeadless();
|
||||
mGLContext = GLContextProvider::CreateHeadless(false);
|
||||
return mGLContext;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,13 @@ WMFMediaDataDecoder::Init()
|
|||
nsresult
|
||||
WMFMediaDataDecoder::Shutdown()
|
||||
{
|
||||
mTaskQueue->FlushAndDispatch(NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown));
|
||||
DebugOnly<nsresult> rv = mTaskQueue->FlushAndDispatch(
|
||||
NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown));
|
||||
#ifdef DEBUG
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("WMFMediaDataDecoder::Shutdown() dispatch of task failed!");
|
||||
}
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -60,6 +66,13 @@ WMFMediaDataDecoder::ProcessShutdown()
|
|||
mDecoder = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
WMFMediaDataDecoder::ProcessReleaseDecoder()
|
||||
{
|
||||
mMFTManager->Shutdown();
|
||||
mDecoder = nullptr;
|
||||
}
|
||||
|
||||
// Inserts data into the decoder's pipeline.
|
||||
nsresult
|
||||
WMFMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
|
@ -142,4 +155,28 @@ WMFMediaDataDecoder::Drain()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
WMFMediaDataDecoder::AllocateMediaResources()
|
||||
{
|
||||
mDecoder = mMFTManager->Init();
|
||||
}
|
||||
|
||||
void
|
||||
WMFMediaDataDecoder::ReleaseMediaResources()
|
||||
{
|
||||
DebugOnly<nsresult> rv = mTaskQueue->FlushAndDispatch(
|
||||
NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessReleaseDecoder));
|
||||
#ifdef DEBUG
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("WMFMediaDataDecoder::ReleaseMediaResources() dispatch of task failed!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
WMFMediaDataDecoder::ReleaseDecoder()
|
||||
{
|
||||
ReleaseMediaResources();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -70,6 +70,12 @@ public:
|
|||
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool IsWaitingMediaResources() { return false; };
|
||||
virtual bool IsDormantNeeded() { return true; };
|
||||
virtual void AllocateMediaResources() MOZ_OVERRIDE;
|
||||
virtual void ReleaseMediaResources() MOZ_OVERRIDE;
|
||||
virtual void ReleaseDecoder() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
// Called on the task queue. Inserts the sample into the decoder, and
|
||||
|
@ -85,6 +91,7 @@ private:
|
|||
void ProcessDrain();
|
||||
|
||||
void ProcessShutdown();
|
||||
void ProcessReleaseDecoder();
|
||||
|
||||
RefPtr<MediaTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
|
|
@ -100,7 +100,9 @@ WMFVideoMFTManager::~WMFVideoMFTManager()
|
|||
{
|
||||
MOZ_COUNT_DTOR(WMFVideoMFTManager);
|
||||
// Ensure DXVA/D3D9 related objects are released on the main thread.
|
||||
DeleteOnMainThread(mDXVA2Manager);
|
||||
if (mDXVA2Manager) {
|
||||
DeleteOnMainThread(mDXVA2Manager);
|
||||
}
|
||||
}
|
||||
|
||||
const GUID&
|
||||
|
@ -140,6 +142,8 @@ public:
|
|||
bool
|
||||
WMFVideoMFTManager::InitializeDXVA()
|
||||
{
|
||||
MOZ_ASSERT(!mDXVA2Manager);
|
||||
|
||||
// If we use DXVA but aren't running with a D3D layer manager then the
|
||||
// readback of decoded video frames from GPU to CPU memory grinds painting
|
||||
// to a halt, and makes playback performance *worse*.
|
||||
|
@ -150,7 +154,8 @@ WMFVideoMFTManager::InitializeDXVA()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (gfxWindowsPlatform::GetPlatform()->IsWARP()) {
|
||||
if (gfxWindowsPlatform::GetPlatform()->IsWARP() ||
|
||||
!gfxPlatform::CanUseDXVA()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -487,6 +492,7 @@ void
|
|||
WMFVideoMFTManager::Shutdown()
|
||||
{
|
||||
mDecoder = nullptr;
|
||||
DeleteOnMainThread(mDXVA2Manager);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -411,6 +411,11 @@ MediaSourceReader::ContinueShutdown()
|
|||
mVideoTrack = nullptr;
|
||||
mVideoReader = nullptr;
|
||||
|
||||
if (mSharedDecoderManager) {
|
||||
mSharedDecoderManager->Shutdown();
|
||||
mSharedDecoderManager = nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mAudioPromise.IsEmpty());
|
||||
MOZ_ASSERT(mVideoPromise.IsEmpty());
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "VideoUtils.h"
|
||||
|
||||
using namespace android;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -806,11 +807,49 @@ MediaCodecReader::TextureClientRecycleCallback(TextureClient* aClient)
|
|||
if (!mTextureClientIndexes.Get(aClient, &index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
|
||||
sp<Fence> fence = aClient->GetReleaseFenceHandle().mFence;
|
||||
if (fence.get() && fence->isValid()) {
|
||||
mPendingReleaseItems.AppendElement(ReleaseItem(index, fence));
|
||||
} else {
|
||||
mPendingReleaseItems.AppendElement(ReleaseItem(index, nullptr));
|
||||
}
|
||||
#else
|
||||
mPendingReleaseItems.AppendElement(ReleaseItem(index));
|
||||
#endif
|
||||
mTextureClientIndexes.Remove(aClient);
|
||||
}
|
||||
|
||||
if (mVideoTrack.mCodec != nullptr) {
|
||||
mVideoTrack.mCodec->releaseOutputBuffer(index);
|
||||
if (mVideoTrack.mReleaseBufferTaskQueue->IsEmpty()) {
|
||||
RefPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this,
|
||||
&MediaCodecReader::WaitFenceAndReleaseOutputBuffer);
|
||||
mVideoTrack.mReleaseBufferTaskQueue->Dispatch(task);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::WaitFenceAndReleaseOutputBuffer()
|
||||
{
|
||||
nsTArray<ReleaseItem> releasingItems;
|
||||
{
|
||||
MutexAutoLock autoLock(mTextureClientIndexesLock);
|
||||
releasingItems.AppendElements(mPendingReleaseItems);
|
||||
mPendingReleaseItems.Clear();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < releasingItems.Length(); i++) {
|
||||
#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
|
||||
sp<Fence> fence;
|
||||
fence = releasingItems[i].mReleaseFence;
|
||||
if (fence.get() && fence->isValid()) {
|
||||
fence->waitForever("MediaCodecReader");
|
||||
}
|
||||
#endif
|
||||
if (mVideoTrack.mCodec != nullptr) {
|
||||
mVideoTrack.mCodec->releaseOutputBuffer(releasingItems[i].mReleaseIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1250,16 +1289,21 @@ MediaCodecReader::DestroyMediaSources()
|
|||
void
|
||||
MediaCodecReader::ShutdownTaskQueues()
|
||||
{
|
||||
if(mAudioTrack.mTaskQueue) {
|
||||
if (mAudioTrack.mTaskQueue) {
|
||||
mAudioTrack.mTaskQueue->BeginShutdown();
|
||||
mAudioTrack.mTaskQueue->AwaitShutdownAndIdle();
|
||||
mAudioTrack.mTaskQueue = nullptr;
|
||||
}
|
||||
if(mVideoTrack.mTaskQueue) {
|
||||
if (mVideoTrack.mTaskQueue) {
|
||||
mVideoTrack.mTaskQueue->BeginShutdown();
|
||||
mVideoTrack.mTaskQueue->AwaitShutdownAndIdle();
|
||||
mVideoTrack.mTaskQueue = nullptr;
|
||||
}
|
||||
if (mVideoTrack.mReleaseBufferTaskQueue) {
|
||||
mVideoTrack.mReleaseBufferTaskQueue->BeginShutdown();
|
||||
mVideoTrack.mReleaseBufferTaskQueue->AwaitShutdownAndIdle();
|
||||
mVideoTrack.mReleaseBufferTaskQueue = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1274,6 +1318,8 @@ MediaCodecReader::CreateTaskQueues()
|
|||
!mVideoTrack.mTaskQueue) {
|
||||
mVideoTrack.mTaskQueue = CreateMediaDecodeTaskQueue();
|
||||
NS_ENSURE_TRUE(mVideoTrack.mTaskQueue, false);
|
||||
mVideoTrack.mReleaseBufferTaskQueue = CreateMediaDecodeTaskQueue();
|
||||
NS_ENSURE_TRUE(mVideoTrack.mReleaseBufferTaskQueue, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
#include "I420ColorConverterHelper.h"
|
||||
#include "MediaCodecProxy.h"
|
||||
#include "MediaOmxCommonReader.h"
|
||||
#include "mozilla/layers/FenceUtils.h"
|
||||
#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
|
||||
#include <ui/Fence.h>
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
struct ALooper;
|
||||
|
@ -47,6 +51,7 @@ class TextureClient;
|
|||
class MediaCodecReader : public MediaOmxCommonReader
|
||||
{
|
||||
typedef mozilla::layers::TextureClient TextureClient;
|
||||
typedef mozilla::layers::FenceHandle FenceHandle;
|
||||
|
||||
public:
|
||||
MediaCodecReader(AbstractMediaDecoder* aDecoder);
|
||||
|
@ -264,6 +269,7 @@ private:
|
|||
// Protected by mTrackMonitor.
|
||||
MediaPromiseHolder<VideoDataPromise> mVideoPromise;
|
||||
|
||||
nsRefPtr<MediaTaskQueue> mReleaseBufferTaskQueue;
|
||||
private:
|
||||
// Forbidden
|
||||
VideoTrack(const VideoTrack &rhs) = delete;
|
||||
|
@ -415,6 +421,7 @@ private:
|
|||
static void TextureClientRecycleCallback(TextureClient* aClient,
|
||||
void* aClosure);
|
||||
void TextureClientRecycleCallback(TextureClient* aClient);
|
||||
void WaitFenceAndReleaseOutputBuffer();
|
||||
|
||||
void ReleaseRecycledTextureClients();
|
||||
static PLDHashOperator ReleaseTextureClient(TextureClient* aClient,
|
||||
|
@ -450,6 +457,26 @@ private:
|
|||
int64_t mNextParserPosition;
|
||||
int64_t mParsedDataLength;
|
||||
nsAutoPtr<MP3FrameParser> mMP3FrameParser;
|
||||
#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
|
||||
// mReleaseIndex corresponding to a graphic buffer, and the mReleaseFence is
|
||||
// the graohic buffer's fence. We must wait for the fence signaled by
|
||||
// compositor, otherwise we will see the flicker because the HW decoder and
|
||||
// compositor use the buffer concurrently.
|
||||
struct ReleaseItem {
|
||||
ReleaseItem(size_t aIndex, const android::sp<android::Fence>& aFence)
|
||||
: mReleaseIndex(aIndex)
|
||||
, mReleaseFence(aFence) {}
|
||||
size_t mReleaseIndex;
|
||||
android::sp<android::Fence> mReleaseFence;
|
||||
};
|
||||
#else
|
||||
struct ReleaseItem {
|
||||
ReleaseItem(size_t aIndex)
|
||||
: mReleaseIndex(aIndex) {}
|
||||
size_t mReleaseIndex;
|
||||
};
|
||||
#endif
|
||||
nsTArray<ReleaseItem> mPendingReleaseItems;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -13,6 +13,9 @@ function parseQuery(request, key) {
|
|||
}
|
||||
|
||||
var types = {
|
||||
js: "text/javascript",
|
||||
m4s: "video/mp4",
|
||||
mp4: "video/mp4",
|
||||
ogg: "video/ogg",
|
||||
ogv: "video/ogg",
|
||||
oga: "audio/ogg",
|
||||
|
|
|
@ -114,6 +114,15 @@ function UpdateSessionFunc(test, token, sessionType) {
|
|||
}
|
||||
}
|
||||
|
||||
function MaybeCrossOriginURI(test, uri)
|
||||
{
|
||||
if (test.crossOrigin) {
|
||||
return "http://test2.mochi.test:8888/tests/dom/media/test/allowed.sjs?" + uri;
|
||||
} else {
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
||||
function PlayFragmented(test, elem, token)
|
||||
{
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
@ -140,7 +149,7 @@ function PlayFragmented(test, elem, token)
|
|||
return;
|
||||
}
|
||||
|
||||
var fragmentFile = test.fragments[curFragment++];
|
||||
var fragmentFile = MaybeCrossOriginURI(test, test.fragments[curFragment++]);
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", fragmentFile);
|
||||
|
@ -179,7 +188,7 @@ function LoadTest(test, elem, token)
|
|||
|
||||
// This file isn't fragmented; set the media source normally.
|
||||
return new Promise(function(resolve, reject) {
|
||||
elem.src = test.name;
|
||||
elem.src = MaybeCrossOriginURI(test, test.name);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
@ -187,6 +196,7 @@ function LoadTest(test, elem, token)
|
|||
function SetupEME(test, token, params)
|
||||
{
|
||||
var v = document.createElement("video");
|
||||
v.crossOrigin = test.crossOrigin || false;
|
||||
|
||||
// Log events dispatched to make debugging easier...
|
||||
[ "canplay", "canplaythrough", "ended", "error", "loadeddata",
|
||||
|
|
|
@ -652,6 +652,18 @@ var gEMETests = [
|
|||
sessionType:"temporary",
|
||||
duration:0.47
|
||||
},
|
||||
{
|
||||
name:"short-cenc.mp4",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
keys: {
|
||||
// "keyid" : "key"
|
||||
"7e571d017e571d017e571d017e571d01" : "7e5711117e5711117e5711117e571111",
|
||||
"7e571d027e571d027e571d027e571d02" : "7e5722227e5722227e5722227e572222",
|
||||
},
|
||||
sessionType:"temporary",
|
||||
duration:0.47,
|
||||
crossOrigin:true,
|
||||
},
|
||||
{
|
||||
name:"gizmo-frag-cencinit.mp4",
|
||||
fragments: [ "gizmo-frag-cencinit.mp4", "gizmo-frag-cenc1.m4s", "gizmo-frag-cenc2.m4s" ],
|
||||
|
@ -664,6 +676,19 @@ var gEMETests = [
|
|||
sessionType:"temporary",
|
||||
duration:2.00,
|
||||
},
|
||||
{
|
||||
name:"gizmo-frag-cencinit.mp4",
|
||||
fragments: [ "gizmo-frag-cencinit.mp4", "gizmo-frag-cenc1.m4s", "gizmo-frag-cenc2.m4s" ],
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
keys: {
|
||||
// "keyid" : "key"
|
||||
"7e571d037e571d037e571d037e571d03" : "7e5733337e5733337e5733337e573333",
|
||||
"7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
|
||||
},
|
||||
sessionType:"temporary",
|
||||
duration:2.00,
|
||||
crossOrigin:true,
|
||||
},
|
||||
];
|
||||
|
||||
function checkMetadata(msg, e, test) {
|
||||
|
|
|
@ -364,6 +364,8 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
|||
[test_defaultMuted.html]
|
||||
[test_delay_load.html]
|
||||
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
|
||||
[test_eme_access_control.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||
[test_eme_canvas_blocked.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||
[test_eme_persistent_sessions.html]
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test EME blocked cross-origin</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function TestNoCORS(test, token)
|
||||
{
|
||||
var token = token + "_nocors";
|
||||
|
||||
manager.started(token);
|
||||
|
||||
var v = document.createElement("video");
|
||||
|
||||
v.addEventListener("encrypted", function(ev) {
|
||||
is(ev.initDataType, "", "initDataType should be empty for CORS cross-origin media");
|
||||
is(ev.initData, null, "initData should be null for CORS cross-origin media");
|
||||
|
||||
manager.finished(token);
|
||||
});
|
||||
|
||||
v.addEventListener("error", function() {
|
||||
ok(false, "Should not receive error loading cross-origin media without crossorigin attribute");
|
||||
});
|
||||
|
||||
v.src = test.uri;
|
||||
}
|
||||
|
||||
function TestCORSFailure(test, token)
|
||||
{
|
||||
var token = token + "_corsfail";
|
||||
|
||||
manager.started(token);
|
||||
|
||||
var v = document.createElement("video");
|
||||
v.crossOrigin = true;
|
||||
|
||||
v.addEventListener("error", function(ev) {
|
||||
ok(true, "Should get error loading cross-origin media");
|
||||
manager.finished(token);
|
||||
});
|
||||
|
||||
v.addEventListener("encrypted", function() {
|
||||
ok(false, "Should not receive encrypted event loading cross-origin media");
|
||||
});
|
||||
|
||||
v.src = test.uri;
|
||||
}
|
||||
|
||||
function TestCORSSuccess(test, token)
|
||||
{
|
||||
var token = token + "_corsok";
|
||||
|
||||
manager.started(token);
|
||||
|
||||
var v = document.createElement("video");
|
||||
v.crossOrigin = true;
|
||||
|
||||
v.addEventListener("error", function(ev) {
|
||||
ok(false, "Should not get error loading cross-origin media");
|
||||
});
|
||||
|
||||
v.addEventListener("encrypted", function(ev) {
|
||||
ok(ev.initData.byteLength > 0, "Should get encryption initData loading cross-origin media");
|
||||
is(ev.initDataType, "cenc", "Should get correct encryption initDataType loading cross-origin media");
|
||||
manager.finished(token);
|
||||
});
|
||||
|
||||
v.src = test.uri;
|
||||
}
|
||||
|
||||
function startTest(test, token)
|
||||
{
|
||||
test.uri = "http://test1.mochi.test:8888/tests/dom/media/test/" + test.name;
|
||||
TestNoCORS(test, token);
|
||||
TestCORSFailure(test, token);
|
||||
|
||||
test.uri = "http://test1.mochi.test:8888/tests/dom/media/test/allowed.sjs?" + test.name;
|
||||
TestCORSSuccess(test, token);
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
manager.runTests(gEMETests.filter(t => t.crossOrigin), startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "media.mediasource.enabled", true ],
|
||||
[ "media.mediasource.youtubeonly", false ],
|
||||
[ "media.mediasource.mp4.enabled", true ],
|
||||
];
|
||||
|
||||
if (/Linux/.test(navigator.userAgent) ||
|
||||
!document.createElement('video').canPlayType("video/mp4")) {
|
||||
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -5,7 +5,7 @@
|
|||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
<script type="text/javascript" src="http://test1.mochi.test:8888/tests/dom/media/test/eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function checkDrawImage(vout) {
|
||||
|
@ -27,6 +25,8 @@ function startTest(test, token) {
|
|||
var v = document.createElement('video');
|
||||
var vout = document.createElement('video');
|
||||
vout.token = token;
|
||||
v.name = token + "(v)";
|
||||
vout.name = token + "(vout)";
|
||||
|
||||
v.src = test.name;
|
||||
var stream = v.mozCaptureStreamUntilEnded();
|
||||
|
@ -53,6 +53,19 @@ function startTest(test, token) {
|
|||
document.body.appendChild(vout);
|
||||
v.play();
|
||||
vout.play();
|
||||
|
||||
// Log events for debugging.
|
||||
var events = ["suspend", "play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
|
||||
"loadeddata", "playing", "ended", "error", "stalled", "emptied", "abort",
|
||||
"waiting", "pause"];
|
||||
function logEvent(e) {
|
||||
info(e.target.name + ": got " + e.type);
|
||||
}
|
||||
events.forEach(function(e) {
|
||||
v.addEventListener(e, logEvent, false);
|
||||
vout.addEventListener(e, logEvent, false);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
manager.runTests(gSmallTests, startTest);
|
||||
|
|
|
@ -111,7 +111,8 @@ WMFReader::InitializeDXVA()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (gfxWindowsPlatform::GetPlatform()->IsWARP()) {
|
||||
if (gfxWindowsPlatform::GetPlatform()->IsWARP() ||
|
||||
!gfxPlatform::CanUseDXVA()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,9 +91,8 @@ static nsRefPtr<GLContext> sPluginContext = nullptr;
|
|||
static bool EnsureGLContext()
|
||||
{
|
||||
if (!sPluginContext) {
|
||||
gfxIntSize dummySize(16, 16);
|
||||
sPluginContext = GLContextProvider::CreateOffscreen(dummySize,
|
||||
SurfaceCaps::Any());
|
||||
bool requireCompatProfile = true;
|
||||
sPluginContext = GLContextProvider::CreateHeadless(requireCompatProfile);
|
||||
}
|
||||
|
||||
return sPluginContext != nullptr;
|
||||
|
|
|
@ -72,6 +72,9 @@ BrowserStreamParent::RecvAsyncNPP_NewStreamResult(const NPError& rv,
|
|||
}
|
||||
|
||||
if (error != NPERR_NO_ERROR) {
|
||||
// streamListener was suspended during async init. We must resume the stream
|
||||
// request prior to calling _destroystream for cleanup to work correctly.
|
||||
streamListener->ResumeRequest();
|
||||
// We need to clean up the stream
|
||||
parent::_destroystream(mNPP->GetNPP(), mStream, NPRES_DONE);
|
||||
unused << PBrowserStreamParent::Send__delete__(this);
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
#include "mozilla/Telemetry.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
#include "mozilla/Preferences.h"
|
||||
#endif
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
|
@ -48,6 +52,8 @@ PluginProcessParent::Launch(mozilla::UniquePtr<LaunchCompleteTask> aLaunchComple
|
|||
{
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
mEnableNPAPISandbox = aEnableSandbox;
|
||||
mMoreStrictSandbox =
|
||||
Preferences::GetBool("dom.ipc.plugins.moreStrictSandbox");
|
||||
#else
|
||||
if (aEnableSandbox) {
|
||||
MOZ_ASSERT(false,
|
||||
|
|
|
@ -42,16 +42,25 @@ using namespace mozilla;
|
|||
static bool gDisableCORS = false;
|
||||
static bool gDisableCORSPrivateData = false;
|
||||
|
||||
static nsresult
|
||||
LogBlockedRequest(nsIRequest* aRequest)
|
||||
static void
|
||||
LogBlockedRequest(nsIRequest* aRequest,
|
||||
const char* aProperty,
|
||||
const char16_t* aParam)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Get the innerWindowID associated with the XMLHTTPRequest
|
||||
uint64_t innerWindowID = nsContentUtils::GetInnerWindowID(aRequest);
|
||||
// Build the error object and log it to the console
|
||||
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no console)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!innerWindowID) {
|
||||
return NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsIScriptError> scriptError =
|
||||
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no scriptError)");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
|
@ -65,32 +74,47 @@ LogBlockedRequest(nsIRequest* aRequest)
|
|||
// Generate the error message
|
||||
nsXPIDLString blockedMessage;
|
||||
NS_ConvertUTF8toUTF16 specUTF16(spec);
|
||||
const char16_t* params[] = { specUTF16.get() };
|
||||
const char16_t* params[] = { specUTF16.get(), aParam };
|
||||
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
|
||||
"CrossSiteRequestBlocked",
|
||||
aProperty,
|
||||
params,
|
||||
blockedMessage);
|
||||
|
||||
// Build the error object and log it to the console
|
||||
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIScriptError> scriptError = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no formalizedStr");
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString msg(blockedMessage.get());
|
||||
rv = scriptError->InitWithWindowID(msg,
|
||||
NS_ConvertUTF8toUTF16(spec),
|
||||
EmptyString(),
|
||||
0,
|
||||
0,
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS",
|
||||
innerWindowID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = console->LogMessage(scriptError);
|
||||
return rv;
|
||||
// query innerWindowID and log to web console, otherwise log to
|
||||
// the error to the browser console.
|
||||
uint64_t innerWindowID = nsContentUtils::GetInnerWindowID(aRequest);
|
||||
|
||||
if (innerWindowID > 0) {
|
||||
rv = scriptError->InitWithWindowID(msg,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS",
|
||||
innerWindowID);
|
||||
}
|
||||
else {
|
||||
rv = scriptError->Init(msg,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS");
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (scriptError init failed)");
|
||||
return;
|
||||
}
|
||||
console->LogMessage(scriptError);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -470,8 +494,6 @@ nsCORSListenerProxy::OnStartRequest(nsIRequest* aRequest,
|
|||
nsresult rv = CheckRequestApproved(aRequest);
|
||||
mRequestApproved = NS_SUCCEEDED(rv);
|
||||
if (!mRequestApproved) {
|
||||
rv = LogBlockedRequest(aRequest);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to log blocked cross-site request");
|
||||
if (sPreflightCache) {
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
if (channel) {
|
||||
|
@ -503,31 +525,45 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
}
|
||||
|
||||
if (gDisableCORS) {
|
||||
LogBlockedRequest(aRequest, "CORSDisabled", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// Check if the request failed
|
||||
nsresult status;
|
||||
nsresult rv = aRequest->GetStatus(&status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(status, status);
|
||||
if (NS_FAILED(rv)) {
|
||||
LogBlockedRequest(aRequest, "CORSRequestFailed", nullptr);
|
||||
return rv;
|
||||
}
|
||||
if (NS_FAILED(status)) {
|
||||
LogBlockedRequest(aRequest, "CORSRequestFailed", nullptr);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Test that things worked on a HTTP level
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
|
||||
NS_ENSURE_TRUE(http, NS_ERROR_DOM_BAD_URI);
|
||||
if (!http) {
|
||||
LogBlockedRequest(aRequest, "CORSRequestNotHttp", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// Check the Access-Control-Allow-Origin header
|
||||
nsAutoCString allowedOriginHeader;
|
||||
rv = http->GetResponseHeader(
|
||||
NS_LITERAL_CSTRING("Access-Control-Allow-Origin"), allowedOriginHeader);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowOrigin", nullptr);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*")) {
|
||||
nsAutoCString origin;
|
||||
rv = nsContentUtils::GetASCIIOrigin(mOriginHeaderPrincipal, origin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsContentUtils::GetASCIIOrigin(mOriginHeaderPrincipal, origin);
|
||||
|
||||
if (!allowedOriginHeader.Equals(origin)) {
|
||||
LogBlockedRequest(aRequest, "CORSAllowOriginNotMatchingOrigin",
|
||||
NS_ConvertUTF8toUTF16(allowedOriginHeader).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
@ -537,9 +573,9 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
nsAutoCString allowCredentialsHeader;
|
||||
rv = http->GetResponseHeader(
|
||||
NS_LITERAL_CSTRING("Access-Control-Allow-Credentials"), allowCredentialsHeader);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!allowCredentialsHeader.EqualsLiteral("true")) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowCredentials", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
@ -547,8 +583,8 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
if (mIsPreflight) {
|
||||
bool succeedded;
|
||||
rv = http->GetRequestSucceeded(&succeedded);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!succeedded) {
|
||||
if (NS_FAILED(rv) || !succeedded) {
|
||||
LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -567,11 +603,16 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
continue;
|
||||
}
|
||||
if (!NS_IsValidHTTPToken(method)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowMethod",
|
||||
NS_ConvertUTF8toUTF16(method).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
foundMethod |= mPreflightMethod.Equals(method);
|
||||
}
|
||||
NS_ENSURE_TRUE(foundMethod, NS_ERROR_DOM_BAD_URI);
|
||||
if (!foundMethod) {
|
||||
LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// The "Access-Control-Allow-Headers" header contains a comma separated
|
||||
// list of header names.
|
||||
|
@ -586,6 +627,8 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
continue;
|
||||
}
|
||||
if (!NS_IsValidHTTPToken(header)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowHeader",
|
||||
NS_ConvertUTF8toUTF16(header).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
headers.AppendElement(header);
|
||||
|
@ -593,6 +636,8 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
|
||||
if (!headers.Contains(mPreflightHeaders[i],
|
||||
nsCaseInsensitiveCStringArrayComparator())) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
|
||||
NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
@ -656,9 +701,6 @@ nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
|||
if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
|
||||
rv = CheckRequestApproved(aOldChannel);
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = LogBlockedRequest(aOldChannel);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to log blocked cross-site request");
|
||||
|
||||
if (sPreflightCache) {
|
||||
nsCOMPtr<nsIURI> oldURI;
|
||||
NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <vector>
|
||||
|
||||
#include "GLContext.h"
|
||||
#include "GLBlitHelper.h"
|
||||
|
@ -156,8 +157,7 @@ static const char *sExtensionNames[] = {
|
|||
"GL_OES_texture_half_float",
|
||||
"GL_OES_texture_half_float_linear",
|
||||
"GL_OES_texture_npot",
|
||||
"GL_OES_vertex_array_object",
|
||||
nullptr
|
||||
"GL_OES_vertex_array_object"
|
||||
};
|
||||
|
||||
static bool
|
||||
|
@ -317,6 +317,8 @@ GLContext::~GLContext() {
|
|||
ReportOutstandingNames();
|
||||
}
|
||||
#endif
|
||||
|
||||
DeleteAndClearIterable(mDriverExtensionList);
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
|
@ -501,6 +503,8 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
|||
mInitialized = LoadSymbols(&symbols[0], trygl, prefix);
|
||||
MakeCurrent();
|
||||
if (mInitialized) {
|
||||
MOZ_ASSERT(mProfile != ContextProfile::Unknown);
|
||||
|
||||
uint32_t version = 0;
|
||||
ParseGLVersion(this, &version);
|
||||
|
||||
|
@ -656,6 +660,15 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
|||
}
|
||||
}
|
||||
|
||||
if (IsFeatureProvidedByCoreSymbols(GLFeature::get_string_indexed)) {
|
||||
SymLoadStruct moreSymbols[] = {
|
||||
{ (PRFuncPtr*) &mSymbols.fGetStringi, { "GetStringi", nullptr } },
|
||||
END_SYMBOLS
|
||||
};
|
||||
|
||||
MOZ_ALWAYS_TRUE(LoadSymbols(moreSymbols, trygl, prefix));
|
||||
}
|
||||
|
||||
InitExtensions();
|
||||
InitFeatures();
|
||||
|
||||
|
@ -670,12 +683,6 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
|||
MarkUnsupported(GLFeature::standard_derivatives);
|
||||
}
|
||||
|
||||
if (Vendor() == GLVendor::Imagination &&
|
||||
Renderer() == GLRenderer::SGX540) {
|
||||
// Bug 980048
|
||||
MarkExtensionUnsupported(OES_EGL_sync);
|
||||
}
|
||||
|
||||
if (Renderer() == GLRenderer::MicrosoftBasicRenderDriver) {
|
||||
// Bug 978966: on Microsoft's "Basic Render Driver" (software renderer)
|
||||
// multisampling hardcodes blending with the default blendfunc, which breaks WebGL.
|
||||
|
@ -1468,10 +1475,13 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
|||
// We're ready for final setup.
|
||||
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
|
||||
|
||||
if (mCaps.any)
|
||||
DetermineCaps();
|
||||
// TODO: Remove SurfaceCaps::any.
|
||||
if (mCaps.any) {
|
||||
mCaps.any = false;
|
||||
mCaps.color = true;
|
||||
mCaps.alpha = false;
|
||||
}
|
||||
|
||||
UpdatePixelFormat();
|
||||
UpdateGLFormats(mCaps);
|
||||
|
||||
mTexGarbageBin = new TextureGarbageBin(this);
|
||||
|
@ -1593,61 +1603,101 @@ GLContext::DebugCallback(GLenum source,
|
|||
void
|
||||
GLContext::InitExtensions()
|
||||
{
|
||||
MakeCurrent();
|
||||
const char* extensions = (const char*)fGetString(LOCAL_GL_EXTENSIONS);
|
||||
if (!extensions)
|
||||
return;
|
||||
MOZ_ASSERT(IsCurrent());
|
||||
|
||||
InitializeExtensionsBitSet(mAvailableExtensions, extensions,
|
||||
sExtensionNames);
|
||||
if (IsFeatureProvidedByCoreSymbols(GLFeature::get_string_indexed)) {
|
||||
GLuint count = 0;
|
||||
GetUIntegerv(LOCAL_GL_NUM_EXTENSIONS, &count);
|
||||
for (GLuint i = 0; i < count; i++) {
|
||||
// This is UTF-8.
|
||||
const char* rawExt = (const char*)fGetStringi(LOCAL_GL_EXTENSIONS,
|
||||
i);
|
||||
nsACString* ext = new nsDependentCString(rawExt);
|
||||
mDriverExtensionList.push_back(ext);
|
||||
}
|
||||
} else {
|
||||
MOZ_ALWAYS_TRUE(!fGetError());
|
||||
const char* rawExts = (const char*)fGetString(LOCAL_GL_EXTENSIONS);
|
||||
MOZ_ALWAYS_TRUE(!fGetError());
|
||||
|
||||
if (WorkAroundDriverBugs() &&
|
||||
Vendor() == GLVendor::Qualcomm) {
|
||||
|
||||
// Some Adreno drivers do not report GL_OES_EGL_sync, but they really do support it.
|
||||
MarkExtensionSupported(OES_EGL_sync);
|
||||
if (rawExts) {
|
||||
nsDependentCString exts(rawExts);
|
||||
SplitByChar(exts, ' ', &mDriverExtensionList);
|
||||
}
|
||||
}
|
||||
|
||||
if (WorkAroundDriverBugs() &&
|
||||
Renderer() == GLRenderer::AndroidEmulator) {
|
||||
// the Android emulator, which we use to run B2G reftests on,
|
||||
// doesn't expose the OES_rgb8_rgba8 extension, but it seems to
|
||||
// support it (tautologically, as it only runs on desktop GL).
|
||||
MarkExtensionSupported(OES_rgb8_rgba8);
|
||||
const bool shouldDumpExts = ShouldDumpExts();
|
||||
if (shouldDumpExts) {
|
||||
printf_stderr("%i GL driver extensions:\n",
|
||||
(uint32_t)mDriverExtensionList.size());
|
||||
}
|
||||
|
||||
if (WorkAroundDriverBugs() &&
|
||||
Vendor() == GLVendor::VMware &&
|
||||
Renderer() == GLRenderer::GalliumLlvmpipe)
|
||||
{
|
||||
// The llvmpipe driver that is used on linux try servers appears to have
|
||||
// buggy support for s3tc/dxt1 compressed textures.
|
||||
// See Bug 975824.
|
||||
MarkExtensionUnsupported(EXT_texture_compression_s3tc);
|
||||
MarkExtensionUnsupported(EXT_texture_compression_dxt1);
|
||||
MarkExtensionUnsupported(ANGLE_texture_compression_dxt3);
|
||||
MarkExtensionUnsupported(ANGLE_texture_compression_dxt5);
|
||||
}
|
||||
MarkBitfieldByStrings(mDriverExtensionList, shouldDumpExts, sExtensionNames,
|
||||
mAvailableExtensions);
|
||||
|
||||
if (WorkAroundDriverBugs()) {
|
||||
if (Vendor() == GLVendor::Qualcomm) {
|
||||
// Some Adreno drivers do not report GL_OES_EGL_sync, but they really do support it.
|
||||
MarkExtensionSupported(OES_EGL_sync);
|
||||
}
|
||||
|
||||
if (Vendor() == GLVendor::Imagination &&
|
||||
Renderer() == GLRenderer::SGX540)
|
||||
{
|
||||
// Bug 980048
|
||||
MarkExtensionUnsupported(OES_EGL_sync);
|
||||
}
|
||||
|
||||
if (Renderer() == GLRenderer::AndroidEmulator) {
|
||||
// the Android emulator, which we use to run B2G reftests on,
|
||||
// doesn't expose the OES_rgb8_rgba8 extension, but it seems to
|
||||
// support it (tautologically, as it only runs on desktop GL).
|
||||
MarkExtensionSupported(OES_rgb8_rgba8);
|
||||
}
|
||||
|
||||
if (Vendor() == GLVendor::VMware &&
|
||||
Renderer() == GLRenderer::GalliumLlvmpipe)
|
||||
{
|
||||
// The llvmpipe driver that is used on linux try servers appears to have
|
||||
// buggy support for s3tc/dxt1 compressed textures.
|
||||
// See Bug 975824.
|
||||
MarkExtensionUnsupported(EXT_texture_compression_s3tc);
|
||||
MarkExtensionUnsupported(EXT_texture_compression_dxt1);
|
||||
MarkExtensionUnsupported(ANGLE_texture_compression_dxt3);
|
||||
MarkExtensionUnsupported(ANGLE_texture_compression_dxt5);
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Bug 1009642: On OSX Mavericks (10.9), the driver for Intel HD
|
||||
// 3000 appears to be buggy WRT updating sub-images of S3TC
|
||||
// textures with glCompressedTexSubImage2D. Works on Intel HD 4000
|
||||
// and Intel HD 5000/Iris that I tested.
|
||||
if (WorkAroundDriverBugs() &&
|
||||
nsCocoaFeatures::OSXVersionMajor() == 10 &&
|
||||
nsCocoaFeatures::OSXVersionMinor() == 9 &&
|
||||
Renderer() == GLRenderer::IntelHD3000)
|
||||
{
|
||||
MarkExtensionUnsupported(EXT_texture_compression_s3tc);
|
||||
}
|
||||
// Bug 1009642: On OSX Mavericks (10.9), the driver for Intel HD
|
||||
// 3000 appears to be buggy WRT updating sub-images of S3TC
|
||||
// textures with glCompressedTexSubImage2D. Works on Intel HD 4000
|
||||
// and Intel HD 5000/Iris that I tested.
|
||||
if (nsCocoaFeatures::OSXVersionMajor() == 10 &&
|
||||
nsCocoaFeatures::OSXVersionMinor() == 9 &&
|
||||
Renderer() == GLRenderer::IntelHD3000)
|
||||
{
|
||||
MarkExtensionUnsupported(EXT_texture_compression_s3tc);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (shouldDumpExts) {
|
||||
printf_stderr("\nActivated extensions:\n");
|
||||
|
||||
for (size_t i = 0; i < mAvailableExtensions.size(); i++) {
|
||||
if (!mAvailableExtensions[i])
|
||||
continue;
|
||||
|
||||
const char* ext = sExtensionNames[i];
|
||||
printf_stderr("[%i] %s\n", (uint32_t)i, ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GLContext::PlatformStartup()
|
||||
{
|
||||
RegisterStrongMemoryReporter(new GfxTexturesReporter());
|
||||
RegisterStrongMemoryReporter(new GfxTexturesReporter());
|
||||
}
|
||||
|
||||
// Common code for checking for both GL extensions and GLX extensions.
|
||||
|
@ -1688,66 +1738,6 @@ GLContext::ListHasExtension(const GLubyte *extensions, const char *extension)
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
GLContext::DetermineCaps()
|
||||
{
|
||||
PixelBufferFormat format = QueryPixelFormat();
|
||||
|
||||
SurfaceCaps caps;
|
||||
caps.color = !!format.red && !!format.green && !!format.blue;
|
||||
caps.bpp16 = caps.color && format.ColorBits() == 16;
|
||||
caps.alpha = !!format.alpha;
|
||||
caps.depth = !!format.depth;
|
||||
caps.stencil = !!format.stencil;
|
||||
caps.antialias = format.samples > 1;
|
||||
caps.preserve = true;
|
||||
|
||||
mCaps = caps;
|
||||
}
|
||||
|
||||
PixelBufferFormat
|
||||
GLContext::QueryPixelFormat()
|
||||
{
|
||||
PixelBufferFormat format;
|
||||
|
||||
ScopedBindFramebuffer autoFB(this, 0);
|
||||
|
||||
fGetIntegerv(LOCAL_GL_RED_BITS , &format.red );
|
||||
fGetIntegerv(LOCAL_GL_GREEN_BITS, &format.green);
|
||||
fGetIntegerv(LOCAL_GL_BLUE_BITS , &format.blue );
|
||||
fGetIntegerv(LOCAL_GL_ALPHA_BITS, &format.alpha);
|
||||
|
||||
fGetIntegerv(LOCAL_GL_DEPTH_BITS, &format.depth);
|
||||
fGetIntegerv(LOCAL_GL_STENCIL_BITS, &format.stencil);
|
||||
|
||||
fGetIntegerv(LOCAL_GL_SAMPLES, &format.samples);
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
void
|
||||
GLContext::UpdatePixelFormat()
|
||||
{
|
||||
PixelBufferFormat format = QueryPixelFormat();
|
||||
#ifdef MOZ_GL_DEBUG
|
||||
const SurfaceCaps& caps = Caps();
|
||||
MOZ_ASSERT(!caps.any, "Did you forget to DetermineCaps()?");
|
||||
|
||||
MOZ_ASSERT(caps.color == !!format.red);
|
||||
MOZ_ASSERT(caps.color == !!format.green);
|
||||
MOZ_ASSERT(caps.color == !!format.blue);
|
||||
|
||||
// These we either must have if they're requested, or
|
||||
// we can have if they're not.
|
||||
MOZ_ASSERT(caps.alpha == !!format.alpha || !caps.alpha);
|
||||
MOZ_ASSERT(caps.depth == !!format.depth || !caps.depth);
|
||||
MOZ_ASSERT(caps.stencil == !!format.stencil || !caps.stencil);
|
||||
|
||||
MOZ_ASSERT(caps.antialias == (format.samples > 1));
|
||||
#endif
|
||||
mPixelFormat = new PixelBufferFormat(format);
|
||||
}
|
||||
|
||||
GLFormats
|
||||
GLContext::ChooseGLFormats(const SurfaceCaps& caps) const
|
||||
{
|
||||
|
@ -2417,6 +2407,12 @@ GLContext::FlushIfHeavyGLCallsSinceLastFlush()
|
|||
fFlush();
|
||||
}
|
||||
|
||||
/*static*/ bool
|
||||
GLContext::ShouldDumpExts()
|
||||
{
|
||||
return PR_GetEnv("MOZ_GL_DUMP_EXTS");
|
||||
}
|
||||
|
||||
bool
|
||||
DoesStringMatch(const char* aString, const char *aWantedString)
|
||||
{
|
||||
|
@ -2448,5 +2444,27 @@ GLContext::ShouldSpew()
|
|||
return spew;
|
||||
}
|
||||
|
||||
void
|
||||
SplitByChar(const nsACString& str, const char delim,
|
||||
std::vector<nsACString*>* out)
|
||||
{
|
||||
uint32_t start = 0;
|
||||
while (true) {
|
||||
int32_t end = str.FindChar(' ', start);
|
||||
if (end == -1)
|
||||
break;
|
||||
|
||||
uint32_t len = (uint32_t)end - start;
|
||||
nsACString* substr = new nsDependentCSubstring(str, start, len);
|
||||
out->push_back(substr);
|
||||
|
||||
start = end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
nsACString* substr = new nsDependentCSubstring(str, start);
|
||||
out->push_back(substr);
|
||||
}
|
||||
|
||||
} /* namespace gl */
|
||||
} /* namespace mozilla */
|
||||
|
|
|
@ -104,6 +104,7 @@ enum class GLFeature {
|
|||
get_integer_indexed,
|
||||
get_integer64_indexed,
|
||||
get_query_object_iv,
|
||||
get_string_indexed,
|
||||
gpu_shader4,
|
||||
instanced_arrays,
|
||||
instanced_non_arrays,
|
||||
|
@ -308,7 +309,6 @@ public:
|
|||
virtual bool IsCurrent() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
bool mInitialized;
|
||||
bool mIsOffscreen;
|
||||
bool mIsGlobalSharedContext;
|
||||
|
@ -325,9 +325,12 @@ protected:
|
|||
GLVendor mVendor;
|
||||
GLRenderer mRenderer;
|
||||
|
||||
inline void SetProfileVersion(ContextProfile profile, unsigned int version) {
|
||||
MOZ_ASSERT(!mInitialized, "SetProfileVersion can only be called before initialization!");
|
||||
MOZ_ASSERT(profile != ContextProfile::Unknown && profile != ContextProfile::OpenGL, "Invalid `profile` for SetProfileVersion");
|
||||
void SetProfileVersion(ContextProfile profile, uint32_t version) {
|
||||
MOZ_ASSERT(!mInitialized, "SetProfileVersion can only be called before"
|
||||
" initialization!");
|
||||
MOZ_ASSERT(profile != ContextProfile::Unknown &&
|
||||
profile != ContextProfile::OpenGL,
|
||||
"Invalid `profile` for SetProfileVersion");
|
||||
MOZ_ASSERT(version >= 100, "Invalid `version` for SetProfileVersion");
|
||||
|
||||
mVersion = version;
|
||||
|
@ -457,6 +460,7 @@ public:
|
|||
return mAvailableExtensions[aKnownExtension];
|
||||
}
|
||||
|
||||
protected:
|
||||
void MarkExtensionUnsupported(GLExtensions aKnownExtension) {
|
||||
mAvailableExtensions[aKnownExtension] = 0;
|
||||
}
|
||||
|
@ -465,42 +469,6 @@ public:
|
|||
mAvailableExtensions[aKnownExtension] = 1;
|
||||
}
|
||||
|
||||
public:
|
||||
template<size_t N>
|
||||
static void InitializeExtensionsBitSet(std::bitset<N>& extensionsBitset,
|
||||
const char* extStr,
|
||||
const char** extList)
|
||||
{
|
||||
char* exts = ::strdup(extStr);
|
||||
|
||||
if (ShouldSpew())
|
||||
printf_stderr("Extensions: %s\n", exts);
|
||||
|
||||
char* cur = exts;
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
char* space = strchr(cur, ' ');
|
||||
if (space) {
|
||||
*space = '\0';
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
|
||||
for (int i = 0; extList[i]; ++i) {
|
||||
if (PL_strcasecmp(cur, extList[i]) == 0) {
|
||||
if (ShouldSpew())
|
||||
printf_stderr("Found extension %s\n", cur);
|
||||
extensionsBitset[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
cur = space + 1;
|
||||
}
|
||||
|
||||
free(exts);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::bitset<Extensions_Max> mAvailableExtensions;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -3180,6 +3148,17 @@ public:
|
|||
AFTER_GL_CALL;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// get_string_indexed
|
||||
|
||||
const GLubyte* fGetStringi(GLenum name, GLuint index) {
|
||||
BEFORE_GL_CALL;
|
||||
ASSERT_SYMBOL_PRESENT(fGetStringi);
|
||||
const GLubyte* ret = mSymbols.fGetStringi(name, index);
|
||||
AFTER_GL_CALL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constructor
|
||||
protected:
|
||||
|
@ -3448,11 +3427,9 @@ public:
|
|||
fViewport(0, 0, size.width, size.height);
|
||||
|
||||
mCaps = mScreen->mCaps;
|
||||
if (mCaps.any)
|
||||
DetermineCaps();
|
||||
MOZ_ASSERT(!mCaps.any);
|
||||
|
||||
UpdateGLFormats(mCaps);
|
||||
UpdatePixelFormat();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3475,10 +3452,8 @@ public:
|
|||
protected:
|
||||
SurfaceCaps mCaps;
|
||||
nsAutoPtr<GLFormats> mGLFormats;
|
||||
nsAutoPtr<PixelBufferFormat> mPixelFormat;
|
||||
|
||||
public:
|
||||
void DetermineCaps();
|
||||
const SurfaceCaps& Caps() const {
|
||||
return mCaps;
|
||||
}
|
||||
|
@ -3494,14 +3469,6 @@ public:
|
|||
return *mGLFormats;
|
||||
}
|
||||
|
||||
PixelBufferFormat QueryPixelFormat();
|
||||
void UpdatePixelFormat();
|
||||
|
||||
const PixelBufferFormat& GetPixelFormat() const {
|
||||
MOZ_ASSERT(mPixelFormat);
|
||||
return *mPixelFormat;
|
||||
}
|
||||
|
||||
bool IsFramebufferComplete(GLuint fb, GLenum* status = nullptr);
|
||||
|
||||
// Does not check completeness.
|
||||
|
@ -3541,7 +3508,7 @@ public:
|
|||
}
|
||||
|
||||
bool IsOffscreen() const {
|
||||
return mScreen;
|
||||
return mIsOffscreen;
|
||||
}
|
||||
|
||||
GLScreenBuffer* Screen() const {
|
||||
|
@ -3578,6 +3545,8 @@ protected:
|
|||
|
||||
void InitExtensions();
|
||||
|
||||
std::vector<nsACString*> mDriverExtensionList;
|
||||
|
||||
GLint mViewportRect[4];
|
||||
GLint mScissorRect[4];
|
||||
|
||||
|
@ -3696,10 +3665,55 @@ protected:
|
|||
public:
|
||||
void FlushIfHeavyGLCallsSinceLastFlush();
|
||||
static bool ShouldSpew();
|
||||
static bool ShouldDumpExts();
|
||||
};
|
||||
|
||||
bool DoesStringMatch(const char* aString, const char *aWantedString);
|
||||
|
||||
void SplitByChar(const nsACString& str, const char delim,
|
||||
std::vector<nsACString*>* out);
|
||||
|
||||
template<size_t N>
|
||||
bool
|
||||
MarkBitfieldByString(const nsACString& str, const char* (&markStrList)[N],
|
||||
std::bitset<N>& markList)
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (str.Equals(markStrList[i])) {
|
||||
markList[i] = 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
void
|
||||
MarkBitfieldByStrings(const std::vector<nsACString*> strList,
|
||||
bool dumpStrings, const char* (&markStrList)[N],
|
||||
std::bitset<N>& markList)
|
||||
{
|
||||
for (auto itr = strList.begin(); itr != strList.end(); ++itr) {
|
||||
const nsACString& str = **itr;
|
||||
const bool wasMarked = MarkBitfieldByString(str, markStrList,
|
||||
markList);
|
||||
if (dumpStrings) {
|
||||
nsCString nullTermed(str);
|
||||
printf_stderr(" %s%s\n", nullTermed.BeginReading(),
|
||||
wasMarked ? "(*)" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
void
|
||||
DeleteAndClearIterable(C& cont)
|
||||
{
|
||||
for(auto itr = cont.begin(); itr != cont.end(); ++itr) {
|
||||
delete *itr;
|
||||
}
|
||||
cont.clear();
|
||||
}
|
||||
|
||||
} /* namespace gl */
|
||||
} /* namespace mozilla */
|
||||
|
|
|
@ -28,10 +28,8 @@ class GLContextCGL : public GLContext
|
|||
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextCGL, MOZ_OVERRIDE)
|
||||
GLContextCGL(const SurfaceCaps& caps,
|
||||
GLContext *shareContext,
|
||||
NSOpenGLContext *context,
|
||||
bool isOffscreen = false);
|
||||
GLContextCGL(const SurfaceCaps& caps, NSOpenGLContext* context,
|
||||
bool isOffscreen, ContextProfile profile);
|
||||
|
||||
~GLContextCGL();
|
||||
|
||||
|
|
|
@ -274,6 +274,16 @@ static const FeatureInfo sFeatureInfoArr[] = {
|
|||
* ARB_occlusion_query (added by OpenGL 2.0).
|
||||
*/
|
||||
},
|
||||
{
|
||||
"get_string_indexed",
|
||||
GLVersion::GL3,
|
||||
GLESVersion::ES3,
|
||||
GLContext::Extension_None,
|
||||
{
|
||||
GLContext::Extensions_End
|
||||
}
|
||||
// glGetStringi
|
||||
},
|
||||
{
|
||||
"gpu_shader4",
|
||||
GLVersion::GL3,
|
||||
|
|
|
@ -21,16 +21,14 @@ namespace gl {
|
|||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
static bool gUseDoubleBufferedWindows = true;
|
||||
|
||||
class CGLLibrary
|
||||
{
|
||||
public:
|
||||
CGLLibrary()
|
||||
: mInitialized(false),
|
||||
mOGLLibrary(nullptr),
|
||||
mPixelFormat(nullptr)
|
||||
{ }
|
||||
: mInitialized(false)
|
||||
, mUseDoubleBufferedWindows(true)
|
||||
, mOGLLibrary(nullptr)
|
||||
{}
|
||||
|
||||
bool EnsureInitialized()
|
||||
{
|
||||
|
@ -46,48 +44,33 @@ public:
|
|||
}
|
||||
|
||||
const char* db = PR_GetEnv("MOZ_CGL_DB");
|
||||
gUseDoubleBufferedWindows = (!db || *db != '0');
|
||||
if (db) {
|
||||
mUseDoubleBufferedWindows = *db != '0';
|
||||
}
|
||||
|
||||
mInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
NSOpenGLPixelFormat *PixelFormat()
|
||||
{
|
||||
if (mPixelFormat == nullptr) {
|
||||
NSOpenGLPixelFormatAttribute attribs[] = {
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFAAllowOfflineRenderers,
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
0
|
||||
};
|
||||
|
||||
if (!gUseDoubleBufferedWindows) {
|
||||
attribs[2] = 0;
|
||||
}
|
||||
|
||||
mPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
|
||||
}
|
||||
|
||||
return mPixelFormat;
|
||||
bool UseDoubleBufferedWindows() const {
|
||||
MOZ_ASSERT(mInitialized);
|
||||
return mUseDoubleBufferedWindows;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mInitialized;
|
||||
bool mUseDoubleBufferedWindows;
|
||||
PRLibrary *mOGLLibrary;
|
||||
NSOpenGLPixelFormat *mPixelFormat;
|
||||
};
|
||||
|
||||
CGLLibrary sCGLLibrary;
|
||||
|
||||
GLContextCGL::GLContextCGL(
|
||||
const SurfaceCaps& caps,
|
||||
GLContext *shareContext,
|
||||
NSOpenGLContext *context,
|
||||
bool isOffscreen)
|
||||
: GLContext(caps, shareContext, isOffscreen),
|
||||
mContext(context)
|
||||
GLContextCGL::GLContextCGL(const SurfaceCaps& caps, NSOpenGLContext* context,
|
||||
bool isOffscreen, ContextProfile profile)
|
||||
: GLContext(caps, nullptr, isOffscreen)
|
||||
, mContext(context)
|
||||
{
|
||||
SetProfileVersion(ContextProfile::OpenGLCompatibility, 210);
|
||||
SetProfileVersion(profile, 210);
|
||||
}
|
||||
|
||||
GLContextCGL::~GLContextCGL()
|
||||
|
@ -162,7 +145,7 @@ GLContextCGL::SetupLookupFunction()
|
|||
bool
|
||||
GLContextCGL::IsDoubleBuffered() const
|
||||
{
|
||||
return gUseDoubleBufferedWindows;
|
||||
return sCGLLibrary.UseDoubleBufferedWindows();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -182,26 +165,66 @@ GLContextCGL::SwapBuffers()
|
|||
}
|
||||
|
||||
|
||||
static GLContextCGL *
|
||||
GetGlobalContextCGL()
|
||||
{
|
||||
return static_cast<GLContextCGL*>(GLContextProviderCGL::GetGlobalContext());
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderCGL::CreateWrappingExisting(void*, void*)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = {
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFAAllowOfflineRenderers,
|
||||
0
|
||||
};
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered[] = {
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFAAllowOfflineRenderers,
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
0
|
||||
};
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_offscreen[] = {
|
||||
NSOpenGLPFAPixelBuffer,
|
||||
0
|
||||
};
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_coreProfile[] = {
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
||||
0
|
||||
};
|
||||
|
||||
static NSOpenGLContext*
|
||||
CreateWithFormat(const NSOpenGLPixelFormatAttribute* attribs)
|
||||
{
|
||||
NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc]
|
||||
initWithAttributes:attribs];
|
||||
if (!format)
|
||||
return nullptr;
|
||||
|
||||
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:format
|
||||
shareContext:nullptr];
|
||||
|
||||
[format release];
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget)
|
||||
{
|
||||
GLContextCGL *shareContext = GetGlobalContextCGL();
|
||||
if (!sCGLLibrary.EnsureInitialized()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NSOpenGLContext *context = [[NSOpenGLContext alloc]
|
||||
initWithFormat:sCGLLibrary.PixelFormat()
|
||||
shareContext:(shareContext ? shareContext->mContext : NULL)];
|
||||
const NSOpenGLPixelFormatAttribute* attribs;
|
||||
if (sCGLLibrary.UseDoubleBufferedWindows()) {
|
||||
attribs = kAttribs_doubleBuffered;
|
||||
} else {
|
||||
attribs = kAttribs_singleBuffered;
|
||||
}
|
||||
NSOpenGLContext* context = CreateWithFormat(attribs);
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -211,10 +234,13 @@ GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget)
|
|||
[context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
|
||||
|
||||
SurfaceCaps caps = SurfaceCaps::ForRGBA();
|
||||
nsRefPtr<GLContextCGL> glContext = new GLContextCGL(caps,
|
||||
shareContext,
|
||||
context);
|
||||
ContextProfile profile = ContextProfile::OpenGLCompatibility;
|
||||
nsRefPtr<GLContextCGL> glContext = new GLContextCGL(caps, context, false,
|
||||
profile);
|
||||
|
||||
if (!glContext->Init()) {
|
||||
glContext = nullptr;
|
||||
[context release];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -222,49 +248,54 @@ GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget)
|
|||
}
|
||||
|
||||
static already_AddRefed<GLContextCGL>
|
||||
CreateOffscreenFBOContext(bool aShare = true)
|
||||
CreateOffscreenFBOContext(bool requireCompatProfile)
|
||||
{
|
||||
if (!sCGLLibrary.EnsureInitialized()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GLContextCGL *shareContext = aShare ? GetGlobalContextCGL() : nullptr;
|
||||
if (aShare && !shareContext) {
|
||||
// if there is no share context, then we can't use FBOs.
|
||||
return nullptr;
|
||||
}
|
||||
ContextProfile profile;
|
||||
NSOpenGLContext* context = nullptr;
|
||||
|
||||
NSOpenGLContext *context = [[NSOpenGLContext alloc]
|
||||
initWithFormat:sCGLLibrary.PixelFormat()
|
||||
shareContext:shareContext ? shareContext->GetNSOpenGLContext() : NULL];
|
||||
if (!requireCompatProfile) {
|
||||
profile = ContextProfile::OpenGLCore;
|
||||
context = CreateWithFormat(kAttribs_offscreen_coreProfile);
|
||||
}
|
||||
if (!context) {
|
||||
profile = ContextProfile::OpenGLCompatibility;
|
||||
context = CreateWithFormat(kAttribs_offscreen);
|
||||
}
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SurfaceCaps dummyCaps = SurfaceCaps::Any();
|
||||
nsRefPtr<GLContextCGL> glContext = new GLContextCGL(dummyCaps, shareContext, context, true);
|
||||
nsRefPtr<GLContextCGL> glContext = new GLContextCGL(dummyCaps, context,
|
||||
true, profile);
|
||||
|
||||
return glContext.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderCGL::CreateHeadless()
|
||||
GLContextProviderCGL::CreateHeadless(bool requireCompatProfile)
|
||||
{
|
||||
nsRefPtr<GLContextCGL> glContext = CreateOffscreenFBOContext();
|
||||
if (!glContext)
|
||||
nsRefPtr<GLContextCGL> gl;
|
||||
gl = CreateOffscreenFBOContext(requireCompatProfile);
|
||||
if (!gl)
|
||||
return nullptr;
|
||||
|
||||
if (!glContext->Init())
|
||||
if (!gl->Init())
|
||||
return nullptr;
|
||||
|
||||
return glContext.forget();
|
||||
return gl.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderCGL::CreateOffscreen(const gfxIntSize& size,
|
||||
const SurfaceCaps& caps)
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile)
|
||||
{
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless();
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
|
||||
if (!glContext->InitOffscreen(ToIntSize(size), caps))
|
||||
return nullptr;
|
||||
|
||||
|
@ -299,7 +330,7 @@ GLContextProviderCGL::GetGlobalContext()
|
|||
void
|
||||
GLContextProviderCGL::Shutdown()
|
||||
{
|
||||
gGlobalContext = nullptr;
|
||||
gGlobalContext = nullptr;
|
||||
}
|
||||
|
||||
} /* namespace gl */
|
||||
|
|
|
@ -878,7 +878,7 @@ GLContextEGL::CreateEGLPixmapOffscreenContext(const gfxIntSize& size)
|
|||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderEGL::CreateHeadless()
|
||||
GLContextProviderEGL::CreateHeadless(bool)
|
||||
{
|
||||
if (!sEGLLibrary.EnsureInitialized()) {
|
||||
return nullptr;
|
||||
|
@ -897,9 +897,10 @@ GLContextProviderEGL::CreateHeadless()
|
|||
// often without the ability to texture from them directly.
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderEGL::CreateOffscreen(const gfxIntSize& size,
|
||||
const SurfaceCaps& caps)
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile)
|
||||
{
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless();
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
|
||||
if (!glContext)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -1213,7 +1213,7 @@ DONE_CREATING_PIXMAP:
|
|||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderGLX::CreateHeadless()
|
||||
GLContextProviderGLX::CreateHeadless(bool)
|
||||
{
|
||||
gfxIntSize dummySize = gfxIntSize(16, 16);
|
||||
nsRefPtr<GLContext> glContext = CreateOffscreenPixmapContext(dummySize);
|
||||
|
@ -1225,9 +1225,10 @@ GLContextProviderGLX::CreateHeadless()
|
|||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderGLX::CreateOffscreen(const gfxIntSize& size,
|
||||
const SurfaceCaps& caps)
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile)
|
||||
{
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless();
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
|
||||
if (!glContext)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -58,11 +58,12 @@ public:
|
|||
*/
|
||||
static already_AddRefed<GLContext>
|
||||
CreateOffscreen(const gfxIntSize& size,
|
||||
const SurfaceCaps& caps);
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile);
|
||||
|
||||
// Just create a context. We'll add offscreen stuff ourselves.
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadless();
|
||||
CreateHeadless(bool requireCompatProfile);
|
||||
|
||||
/**
|
||||
* Create wrapping Gecko GLContext for external gl context.
|
||||
|
|
|
@ -22,13 +22,14 @@ GLContextProviderNull::CreateWrappingExisting(void*, void*)
|
|||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderNull::CreateOffscreen(const gfxIntSize&,
|
||||
const SurfaceCaps&)
|
||||
const SurfaceCaps&,
|
||||
bool)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderNull::CreateHeadless()
|
||||
GLContextProviderNull::CreateHeadless(bool)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -607,7 +607,7 @@ CreateWindowOffscreenContext()
|
|||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderWGL::CreateHeadless()
|
||||
GLContextProviderWGL::CreateHeadless(bool)
|
||||
{
|
||||
if (!sWGLLib.EnsureInitialized()) {
|
||||
return nullptr;
|
||||
|
@ -641,9 +641,10 @@ GLContextProviderWGL::CreateHeadless()
|
|||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderWGL::CreateOffscreen(const gfxIntSize& size,
|
||||
const SurfaceCaps& caps)
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile)
|
||||
{
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless();
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
|
||||
if (!glContext)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -666,6 +666,10 @@ struct GLContextSymbols
|
|||
GLsizei width, GLsizei height, GLsizei depth,
|
||||
GLenum format, GLsizei imageSize, const GLvoid* data);
|
||||
PFNGLCOMPRESSEDTEXSUBIMAGE3D fCompressedTexSubImage3D;
|
||||
|
||||
// get_string_indexed
|
||||
typedef const GLubyte* (GLAPIENTRY * pfnGLGetStringiT)(GLenum name, GLuint index);
|
||||
pfnGLGetStringiT fGetStringi;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -12,8 +12,3 @@ GLFormats::GLFormats()
|
|||
{
|
||||
std::memset(this, 0, sizeof(GLFormats));
|
||||
}
|
||||
|
||||
PixelBufferFormat::PixelBufferFormat()
|
||||
{
|
||||
std::memset(this, 0, sizeof(PixelBufferFormat));
|
||||
}
|
||||
|
|
|
@ -43,19 +43,6 @@ struct GLFormats
|
|||
GLsizei samples;
|
||||
};
|
||||
|
||||
struct PixelBufferFormat
|
||||
{
|
||||
// Constructs a zeroed object:
|
||||
PixelBufferFormat();
|
||||
|
||||
int red, green, blue;
|
||||
int alpha;
|
||||
int depth, stencil;
|
||||
int samples;
|
||||
|
||||
int ColorBits() const { return red + green + blue; }
|
||||
};
|
||||
|
||||
} /* namespace gl */
|
||||
} /* namespace mozilla */
|
||||
|
||||
|
|
|
@ -35,8 +35,7 @@ static const char *sEGLExtensionNames[] = {
|
|||
"EGL_EXT_create_context_robustness",
|
||||
"EGL_KHR_image",
|
||||
"EGL_KHR_fence_sync",
|
||||
"EGL_ANDROID_native_fence_sync",
|
||||
nullptr
|
||||
"EGL_ANDROID_native_fence_sync"
|
||||
};
|
||||
|
||||
#if defined(ANDROID)
|
||||
|
@ -421,15 +420,23 @@ GLLibraryEGL::EnsureInitialized()
|
|||
void
|
||||
GLLibraryEGL::InitExtensions()
|
||||
{
|
||||
const char *extensions = (const char*)fQueryString(mEGLDisplay, LOCAL_EGL_EXTENSIONS);
|
||||
|
||||
if (!extensions) {
|
||||
const char* rawExts = (const char*)fQueryString(mEGLDisplay,
|
||||
LOCAL_EGL_EXTENSIONS);
|
||||
if (rawExts) {
|
||||
nsDependentCString exts(rawExts);
|
||||
SplitByChar(exts, ' ', &mDriverExtensionList);
|
||||
} else {
|
||||
NS_WARNING("Failed to load EGL extension list!");
|
||||
return;
|
||||
}
|
||||
|
||||
GLContext::InitializeExtensionsBitSet(mAvailableExtensions, extensions,
|
||||
sEGLExtensionNames);
|
||||
const bool shouldDumpExts = GLContext::ShouldDumpExts();
|
||||
if (shouldDumpExts) {
|
||||
printf_stderr("%i EGL driver extensions:\n",
|
||||
(uint32_t)mDriverExtensionList.size());
|
||||
}
|
||||
|
||||
MarkBitfieldByStrings(mDriverExtensionList, shouldDumpExts,
|
||||
sEGLExtensionNames, mAvailableExtensions);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "GeckoProfiler.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <vector>
|
||||
|
||||
#if defined(XP_WIN)
|
||||
|
||||
|
@ -142,6 +143,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
std::vector<nsACString*> mDriverExtensionList;
|
||||
std::bitset<Extensions_Max> mAvailableExtensions;
|
||||
|
||||
public:
|
||||
|
|
|
@ -39,7 +39,7 @@ GLImage::GetAsSourceSurface()
|
|||
MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread");
|
||||
|
||||
if (!sSnapshotContext) {
|
||||
sSnapshotContext = GLContextProvider::CreateHeadless();
|
||||
sSnapshotContext = GLContextProvider::CreateHeadless(false);
|
||||
if (!sSnapshotContext) {
|
||||
NS_WARNING("Failed to create snapshot GLContext");
|
||||
return nullptr;
|
||||
|
|
|
@ -364,6 +364,54 @@ TextureClientD3D11::BorrowDrawTarget()
|
|||
return mDrawTarget;
|
||||
}
|
||||
|
||||
static const GUID sD3D11TextureUsage =
|
||||
{ 0xd89275b0, 0x6c7d, 0x4038, { 0xb5, 0xfa, 0x4d, 0x87, 0x16, 0xd5, 0xcc, 0x4e } };
|
||||
|
||||
/* This class get's it's lifetime tied to a D3D texture
|
||||
* and increments memory usage on construction and decrements
|
||||
* on destruction */
|
||||
class TextureMemoryMeasurer : public IUnknown
|
||||
{
|
||||
public:
|
||||
TextureMemoryMeasurer(size_t aMemoryUsed)
|
||||
{
|
||||
mMemoryUsed = aMemoryUsed;
|
||||
gfxWindowsPlatform::sD3D11MemoryUsed += mMemoryUsed;
|
||||
mRefCnt = 0;
|
||||
}
|
||||
STDMETHODIMP_(ULONG) AddRef() {
|
||||
mRefCnt++;
|
||||
return mRefCnt;
|
||||
}
|
||||
STDMETHODIMP QueryInterface(REFIID riid,
|
||||
void **ppvObject)
|
||||
{
|
||||
IUnknown *punk = nullptr;
|
||||
if (riid == IID_IUnknown) {
|
||||
punk = this;
|
||||
}
|
||||
*ppvObject = punk;
|
||||
if (punk) {
|
||||
punk->AddRef();
|
||||
return S_OK;
|
||||
} else {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) Release() {
|
||||
int refCnt = --mRefCnt;
|
||||
if (refCnt == 0) {
|
||||
gfxWindowsPlatform::sD3D11MemoryUsed -= mMemoryUsed;
|
||||
delete this;
|
||||
}
|
||||
return refCnt;
|
||||
}
|
||||
private:
|
||||
int mRefCnt;
|
||||
int mMemoryUsed;
|
||||
};
|
||||
|
||||
bool
|
||||
TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
|
||||
{
|
||||
|
@ -386,6 +434,14 @@ TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlag
|
|||
newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
|
||||
|
||||
hr = d3d11device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture));
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
|
||||
return false;
|
||||
}
|
||||
mTexture->SetPrivateDataInterface(sD3D11TextureUsage,
|
||||
new TextureMemoryMeasurer(newDesc.Width * newDesc.Height *
|
||||
(mFormat == SurfaceFormat::A8 ?
|
||||
1 : 4)));
|
||||
} else
|
||||
{
|
||||
ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
|
||||
|
@ -397,11 +453,15 @@ TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlag
|
|||
newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED;
|
||||
|
||||
hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture10));
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D10] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
|
||||
return false;
|
||||
}
|
||||
mTexture10->SetPrivateDataInterface(sD3D11TextureUsage,
|
||||
new TextureMemoryMeasurer(newDesc.Width * newDesc.Height *
|
||||
(mFormat == SurfaceFormat::A8 ?
|
||||
1 : 4)));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Defer clearing to the next time we lock to avoid an extra (expensive) lock.
|
||||
|
|
|
@ -124,8 +124,11 @@ CompositorOGL::CreateContext()
|
|||
SurfaceCaps caps = SurfaceCaps::ForRGB();
|
||||
caps.preserve = false;
|
||||
caps.bpp16 = gfxPlatform::GetPlatform()->GetOffscreenFormat() == gfxImageFormat::RGB16_565;
|
||||
|
||||
bool requireCompatProfile = true;
|
||||
context = GLContextProvider::CreateOffscreen(gfxIntSize(mSurfaceSize.width,
|
||||
mSurfaceSize.height), caps);
|
||||
mSurfaceSize.height),
|
||||
caps, requireCompatProfile);
|
||||
}
|
||||
|
||||
if (!context)
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
caps.preserve = false;
|
||||
caps.bpp16 = false;
|
||||
nsRefPtr<GLContext> context = GLContextProvider::CreateOffscreen(
|
||||
gfxIntSize(gCompWidth, gCompHeight), caps);
|
||||
gfxIntSize(gCompWidth, gCompHeight), caps, true);
|
||||
return context.forget().take();
|
||||
}
|
||||
return nullptr;
|
||||
|
|
|
@ -1099,8 +1099,9 @@ gfxPlatform::GetSkiaGLGlue()
|
|||
* FIXME: This should be stored in TLS or something, since there needs to be one for each thread using it. As it
|
||||
* stands, this only works on the main thread.
|
||||
*/
|
||||
mozilla::gl::SurfaceCaps caps = mozilla::gl::SurfaceCaps::ForRGBA();
|
||||
nsRefPtr<mozilla::gl::GLContext> glContext = mozilla::gl::GLContextProvider::CreateOffscreen(gfxIntSize(16, 16), caps);
|
||||
bool requireCompatProfile = true;
|
||||
nsRefPtr<mozilla::gl::GLContext> glContext;
|
||||
glContext = mozilla::gl::GLContextProvider::CreateHeadless(requireCompatProfile);
|
||||
if (!glContext) {
|
||||
printf_stderr("Failed to create GLContext for SkiaGL!\n");
|
||||
return nullptr;
|
||||
|
@ -2164,6 +2165,7 @@ gfxPlatform::OptimalFormatForContent(gfxContentType aContent)
|
|||
*/
|
||||
static bool sLayersSupportsD3D9 = false;
|
||||
static bool sLayersSupportsD3D11 = false;
|
||||
static bool sLayersSupportsDXVA = false;
|
||||
static bool sBufferRotationCheckPref = true;
|
||||
static bool sPrefBrowserTabsRemoteAutostart = false;
|
||||
|
||||
|
@ -2205,6 +2207,11 @@ InitLayersAccelerationPrefs()
|
|||
// Always support D3D11 when WARP is allowed.
|
||||
sLayersSupportsD3D11 = true;
|
||||
}
|
||||
if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DXVA, &status))) {
|
||||
if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
|
||||
sLayersSupportsDXVA = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -2231,6 +2238,15 @@ gfxPlatform::CanUseDirect3D11()
|
|||
return sLayersSupportsD3D11;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxPlatform::CanUseDXVA()
|
||||
{
|
||||
// this function is called from the compositor thread, so it is not
|
||||
// safe to init the prefs etc. from here.
|
||||
MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
|
||||
return sLayersSupportsDXVA;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxPlatform::BufferRotationEnabled()
|
||||
{
|
||||
|
|
|
@ -481,6 +481,7 @@ public:
|
|||
|
||||
static bool CanUseDirect3D9();
|
||||
static bool CanUseDirect3D11();
|
||||
static bool CanUseDXVA();
|
||||
|
||||
/**
|
||||
* Is it possible to use buffer rotation. Note that these
|
||||
|
|
|
@ -650,9 +650,10 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
|
|||
if (LOG_ENABLED()) {
|
||||
nsAutoCString fontURI;
|
||||
mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
|
||||
LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
|
||||
LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) (%p) gen: %8.8x\n",
|
||||
mFontSet, mSrcIndex, fontURI.get(),
|
||||
NS_ConvertUTF16toUTF8(mFamilyName).get(),
|
||||
this,
|
||||
uint32_t(mFontSet->mGeneration)));
|
||||
}
|
||||
#endif
|
||||
|
@ -786,17 +787,6 @@ gfxUserFontSet::FindOrCreateUserFontEntry(
|
|||
aItalicStyle, aFeatureSettings,
|
||||
aLanguageOverride, aUnicodeRanges);
|
||||
entry->mFamilyName = aFamilyName;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
if (LOG_ENABLED()) {
|
||||
LOG(("userfonts (%p) created \"%s\" (%p) with style: %s weight: %d "
|
||||
"stretch: %d",
|
||||
this, NS_ConvertUTF16toUTF8(aFamilyName).get(), entry.get(),
|
||||
(aItalicStyle & NS_FONT_STYLE_ITALIC ? "italic" :
|
||||
(aItalicStyle & NS_FONT_STYLE_OBLIQUE ? "oblique" : "normal")),
|
||||
aWeight, aStretch));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return entry.forget();
|
||||
|
@ -865,8 +855,11 @@ gfxUserFontSet::AddUserFontEntry(const nsAString& aFamilyName,
|
|||
|
||||
#ifdef PR_LOGGING
|
||||
if (LOG_ENABLED()) {
|
||||
LOG(("userfonts (%p) added \"%s\" (%p)",
|
||||
this, NS_ConvertUTF16toUTF8(aFamilyName).get(), aUserFontEntry));
|
||||
LOG(("userfonts (%p) added to \"%s\" (%p) style: %s weight: %d "
|
||||
"stretch: %d",
|
||||
this, NS_ConvertUTF16toUTF8(aFamilyName).get(), aUserFontEntry,
|
||||
(aUserFontEntry->IsItalic() ? "italic" : "normal"),
|
||||
aUserFontEntry->Weight(), aUserFontEntry->Stretch()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -325,6 +325,25 @@ public:
|
|||
|
||||
NS_IMPL_ISUPPORTS(GPUAdapterReporter, nsIMemoryReporter)
|
||||
|
||||
|
||||
Atomic<size_t> gfxWindowsPlatform::sD3D11MemoryUsed;
|
||||
|
||||
class D3D11TextureReporter MOZ_FINAL : public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
|
||||
{
|
||||
return MOZ_COLLECT_REPORT("d3d11-shared-textures", KIND_OTHER, UNITS_BYTES,
|
||||
gfxWindowsPlatform::sD3D11MemoryUsed,
|
||||
"Memory used for D3D11 shared textures");
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(D3D11TextureReporter, nsIMemoryReporter)
|
||||
|
||||
gfxWindowsPlatform::gfxWindowsPlatform()
|
||||
: mD3D11DeviceInitialized(false)
|
||||
, mIsWARP(false)
|
||||
|
@ -352,6 +371,7 @@ gfxWindowsPlatform::gfxWindowsPlatform()
|
|||
UpdateRenderMode();
|
||||
|
||||
RegisterStrongMemoryReporter(new GPUAdapterReporter());
|
||||
RegisterStrongMemoryReporter(new D3D11TextureReporter());
|
||||
}
|
||||
|
||||
gfxWindowsPlatform::~gfxWindowsPlatform()
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "gfxPlatform.h"
|
||||
#include "gfxTypes.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
|
@ -252,6 +253,8 @@ public:
|
|||
|
||||
bool IsWARP() { return mIsWARP; }
|
||||
|
||||
static mozilla::Atomic<size_t> sD3D11MemoryUsed;
|
||||
|
||||
protected:
|
||||
RenderMode mRenderMode;
|
||||
|
||||
|
|
|
@ -98,9 +98,7 @@ GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
|
|||
#if defined(MOZ_SANDBOX) && defined(XP_WIN)
|
||||
mEnableSandboxLogging(false),
|
||||
mEnableNPAPISandbox(false),
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
mMoreStrictContentSandbox(false),
|
||||
#endif
|
||||
mMoreStrictSandbox(false),
|
||||
#endif
|
||||
mChildProcessHandle(0)
|
||||
#if defined(MOZ_WIDGET_COCOA)
|
||||
|
@ -273,7 +271,7 @@ GeckoChildProcessHost::PrepareLaunch()
|
|||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
// We need to get the pref here as the process is launched off main thread.
|
||||
if (mProcessType == GeckoProcessType_Content) {
|
||||
mMoreStrictContentSandbox =
|
||||
mMoreStrictSandbox =
|
||||
Preferences::GetBool("security.sandbox.windows.content.moreStrict");
|
||||
mEnableSandboxLogging =
|
||||
Preferences::GetBool("security.sandbox.windows.log");
|
||||
|
@ -807,7 +805,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
|
|||
case GeckoProcessType_Content:
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
if (!PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
|
||||
mSandboxBroker.SetSecurityLevelForContentProcess(mMoreStrictContentSandbox);
|
||||
mSandboxBroker.SetSecurityLevelForContentProcess(mMoreStrictSandbox);
|
||||
cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
|
||||
shouldSandboxCurrentProcess = true;
|
||||
}
|
||||
|
@ -816,7 +814,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
|
|||
case GeckoProcessType_Plugin:
|
||||
if (mEnableNPAPISandbox &&
|
||||
!PR_GetEnv("MOZ_DISABLE_NPAPI_SANDBOX")) {
|
||||
mSandboxBroker.SetSecurityLevelForPluginProcess();
|
||||
mSandboxBroker.SetSecurityLevelForPluginProcess(mMoreStrictSandbox);
|
||||
cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
|
||||
shouldSandboxCurrentProcess = true;
|
||||
}
|
||||
|
|
|
@ -177,9 +177,7 @@ protected:
|
|||
// sandboxing in this class at some point. Unfortunately it will take a bit
|
||||
// of reorganizing so I don't think this patch is the right time.
|
||||
bool mEnableNPAPISandbox;
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
bool mMoreStrictContentSandbox;
|
||||
#endif
|
||||
bool mMoreStrictSandbox;
|
||||
#endif
|
||||
#endif // XP_WIN
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
|
||||
function toint32() {
|
||||
|
||||
// The test case to trigger MToInt32 operation.
|
||||
var ToInteger = getSelfHostedValue("ToInteger");
|
||||
|
||||
// Case1: The input operand is constant int32.
|
||||
var result = ToInteger(1);
|
||||
assertEq(result, 1);
|
||||
|
||||
// Case2: The input operand is constant double.
|
||||
result = ToInteger(0.12);
|
||||
assertEq(result, 0);
|
||||
|
||||
// Case3: The input operand is constant float.
|
||||
result = ToInteger(Math.fround(0.13));
|
||||
assertEq(result, 0);
|
||||
|
||||
// Case4: The input operand is constant boolean.
|
||||
result = ToInteger(true);
|
||||
assertEq(result, 1);
|
||||
|
||||
// Case5: The input operand is null.
|
||||
result = ToInteger(null);
|
||||
assertEq(result, 0);
|
||||
}
|
||||
|
||||
toint32();
|
||||
toint32();
|
|
@ -3806,10 +3806,20 @@ CodeGenerator::generateBody()
|
|||
if (!addNativeToBytecodeEntry(iter->mirRaw()->trackedSite()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Track the start native offset of optimizations.
|
||||
if (iter->mirRaw()->trackedOptimizations()) {
|
||||
if (!addTrackedOptimizationsEntry(iter->mirRaw()->trackedOptimizations()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
iter->accept(this);
|
||||
|
||||
// Track the end native offset of optimizations.
|
||||
if (iter->mirRaw() && iter->mirRaw()->trackedOptimizations())
|
||||
extendTrackedOptimizationsEntry(iter->mirRaw()->trackedOptimizations());
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!counts)
|
||||
emitDebugResultChecks(*iter);
|
||||
|
@ -7235,6 +7245,28 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
|
|||
// nativeToBytecodeScriptList_ is no longer needed.
|
||||
js_free(nativeToBytecodeScriptList_);
|
||||
|
||||
// Generate the tracked optimizations map.
|
||||
if (isOptimizationTrackingEnabled()) {
|
||||
// Treat OOMs and failures as if optimization tracking were turned off.
|
||||
types::TypeSet::TypeList *allTypes = cx->new_<types::TypeSet::TypeList>();
|
||||
if (allTypes && generateCompactTrackedOptimizationsMap(cx, code, allTypes)) {
|
||||
const uint8_t *optsRegionTableAddr = trackedOptimizationsMap_ +
|
||||
trackedOptimizationsRegionTableOffset_;
|
||||
const IonTrackedOptimizationsRegionTable *optsRegionTable =
|
||||
(const IonTrackedOptimizationsRegionTable *) optsRegionTableAddr;
|
||||
const uint8_t *optsTypesTableAddr = trackedOptimizationsMap_ +
|
||||
trackedOptimizationsTypesTableOffset_;
|
||||
const IonTrackedOptimizationsTypesTable *optsTypesTable =
|
||||
(const IonTrackedOptimizationsTypesTable *) optsTypesTableAddr;
|
||||
const uint8_t *optsAttemptsTableAddr = trackedOptimizationsMap_ +
|
||||
trackedOptimizationsAttemptsTableOffset_;
|
||||
const IonTrackedOptimizationsAttemptsTable *optsAttemptsTable =
|
||||
(const IonTrackedOptimizationsAttemptsTable *) optsAttemptsTableAddr;
|
||||
entry.initTrackedOptimizations(optsRegionTable, optsTypesTable, optsAttemptsTable,
|
||||
allTypes);
|
||||
}
|
||||
}
|
||||
|
||||
// Add entry to the global table.
|
||||
JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
|
||||
if (!globalTable->addEntry(entry, cx->runtime())) {
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class TrackedOptimizations;
|
||||
|
||||
inline unsigned
|
||||
StartArgSlot(JSScript *script)
|
||||
{
|
||||
|
@ -130,13 +132,16 @@ class BytecodeSite : public TempObject
|
|||
// Bytecode address within innermost active function.
|
||||
jsbytecode *pc_;
|
||||
|
||||
// Optimization information at the pc.
|
||||
TrackedOptimizations *optimizations_;
|
||||
|
||||
public:
|
||||
BytecodeSite()
|
||||
: tree_(nullptr), pc_(nullptr)
|
||||
: tree_(nullptr), pc_(nullptr), optimizations_(nullptr)
|
||||
{}
|
||||
|
||||
BytecodeSite(InlineScriptTree *tree, jsbytecode *pc)
|
||||
: tree_(tree), pc_(pc)
|
||||
: tree_(tree), pc_(pc), optimizations_(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(tree_ != nullptr);
|
||||
MOZ_ASSERT(pc_ != nullptr);
|
||||
|
@ -153,6 +158,19 @@ class BytecodeSite : public TempObject
|
|||
JSScript *script() const {
|
||||
return tree_ ? tree_->script() : nullptr;
|
||||
}
|
||||
|
||||
bool hasOptimizations() const {
|
||||
return !!optimizations_;
|
||||
}
|
||||
|
||||
TrackedOptimizations *optimizations() const {
|
||||
MOZ_ASSERT(hasOptimizations());
|
||||
return optimizations_;
|
||||
}
|
||||
|
||||
void setOptimizations(TrackedOptimizations *optimizations) {
|
||||
optimizations_ = optimizations;
|
||||
}
|
||||
};
|
||||
|
||||
enum AnalysisMode {
|
||||
|
|
|
@ -1717,6 +1717,30 @@ TrackPropertiesForSingletonScopes(JSContext *cx, JSScript *script, BaselineFrame
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
TrackIonAbort(JSContext *cx, JSScript *script, jsbytecode *pc, const char *message)
|
||||
{
|
||||
if (!cx->runtime()->jitRuntime()->isOptimizationTrackingEnabled(cx->runtime()))
|
||||
return;
|
||||
|
||||
// Only bother tracking aborts of functions we're attempting to
|
||||
// Ion-compile after successfully running in Baseline.
|
||||
if (!script->hasBaselineScript())
|
||||
return;
|
||||
|
||||
JitcodeGlobalTable *table = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
|
||||
JitcodeGlobalEntry entry;
|
||||
table->lookupInfallible(script->baselineScript()->method()->raw(), &entry, cx->runtime());
|
||||
entry.baselineEntry().trackIonAbort(pc, message);
|
||||
}
|
||||
|
||||
static void
|
||||
TrackAndSpewIonAbort(JSContext *cx, JSScript *script, const char *message)
|
||||
{
|
||||
JitSpew(JitSpew_IonAbort, message);
|
||||
TrackIonAbort(cx, script, script->code(), message);
|
||||
}
|
||||
|
||||
static AbortReason
|
||||
IonCompile(JSContext *cx, JSScript *script,
|
||||
BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing,
|
||||
|
@ -1822,6 +1846,15 @@ IonCompile(JSContext *cx, JSScript *script,
|
|||
return AbortReason_Alloc;
|
||||
}
|
||||
}
|
||||
|
||||
if (builder->hadActionableAbort()) {
|
||||
JSScript *abortScript;
|
||||
jsbytecode *abortPc;
|
||||
const char *abortMessage;
|
||||
builder->actionableAbortLocationAndMessage(&abortScript, &abortPc, &abortMessage);
|
||||
TrackIonAbort(cx, abortScript, abortPc, abortMessage);
|
||||
}
|
||||
|
||||
return reason;
|
||||
}
|
||||
|
||||
|
@ -1861,7 +1894,7 @@ IonCompile(JSContext *cx, JSScript *script,
|
|||
}
|
||||
|
||||
static bool
|
||||
CheckFrame(BaselineFrame *frame)
|
||||
CheckFrame(JSContext *cx, BaselineFrame *frame)
|
||||
{
|
||||
MOZ_ASSERT(!frame->script()->isGenerator());
|
||||
MOZ_ASSERT(!frame->isDebuggerEvalFrame());
|
||||
|
@ -1869,12 +1902,12 @@ CheckFrame(BaselineFrame *frame)
|
|||
// This check is to not overrun the stack.
|
||||
if (frame->isFunctionFrame()) {
|
||||
if (TooManyActualArguments(frame->numActualArgs())) {
|
||||
JitSpew(JitSpew_IonAbort, "too many actual args");
|
||||
TrackAndSpewIonAbort(cx, frame->script(), "too many actual arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TooManyFormalArguments(frame->numFormalArgs())) {
|
||||
JitSpew(JitSpew_IonAbort, "too many args");
|
||||
TrackAndSpewIonAbort(cx, frame->script(), "too many arguments");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1889,12 +1922,12 @@ CheckScript(JSContext *cx, JSScript *script, bool osr)
|
|||
// Eval frames are not yet supported. Supporting this will require new
|
||||
// logic in pushBailoutFrame to deal with linking prev.
|
||||
// Additionally, JSOP_DEFVAR support will require baking in isEvalFrame().
|
||||
JitSpew(JitSpew_IonAbort, "eval script");
|
||||
TrackAndSpewIonAbort(cx, script, "eval script");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (script->isGenerator()) {
|
||||
JitSpew(JitSpew_IonAbort, "generator script");
|
||||
TrackAndSpewIonAbort(cx, script, "generator script");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1902,7 +1935,7 @@ CheckScript(JSContext *cx, JSScript *script, bool osr)
|
|||
// Support non-CNG functions but not other scripts. For global scripts,
|
||||
// IonBuilder currently uses the global object as scope chain, this is
|
||||
// not valid for non-CNG code.
|
||||
JitSpew(JitSpew_IonAbort, "not compile-and-go");
|
||||
TrackAndSpewIonAbort(cx, script, "not compile-and-go");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1923,6 +1956,7 @@ CheckScriptSize(JSContext *cx, JSScript* script)
|
|||
if (!OffThreadCompilationAvailable(cx)) {
|
||||
JitSpew(JitSpew_IonAbort, "Script too large (%u bytes) (%u locals/args)",
|
||||
script->length(), numLocalsAndArgs);
|
||||
TrackIonAbort(cx, script, script->code(), "too large");
|
||||
return Method_CantCompile;
|
||||
}
|
||||
}
|
||||
|
@ -1957,7 +1991,7 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
|
|||
return Method_Skipped;
|
||||
|
||||
if (script->isDebuggee() || (osrFrame && osrFrame->isDebuggee())) {
|
||||
JitSpew(JitSpew_IonAbort, "debugging");
|
||||
TrackAndSpewIonAbort(cx, script, "debugging");
|
||||
return Method_Skipped;
|
||||
}
|
||||
|
||||
|
@ -2045,7 +2079,7 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
|
|||
return Method_Skipped;
|
||||
|
||||
// Mark as forbidden if frame can't be handled.
|
||||
if (!CheckFrame(osrFrame)) {
|
||||
if (!CheckFrame(cx, osrFrame)) {
|
||||
ForbidCompilation(cx, script);
|
||||
return Method_CantCompile;
|
||||
}
|
||||
|
@ -2112,13 +2146,13 @@ jit::CanEnter(JSContext *cx, RunState &state)
|
|||
InvokeState &invoke = *state.asInvoke();
|
||||
|
||||
if (TooManyActualArguments(invoke.args().length())) {
|
||||
JitSpew(JitSpew_IonAbort, "too many actual args");
|
||||
TrackAndSpewIonAbort(cx, script, "too many actual args");
|
||||
ForbidCompilation(cx, script);
|
||||
return Method_CantCompile;
|
||||
}
|
||||
|
||||
if (TooManyFormalArguments(invoke.args().callee().as<JSFunction>().nargs())) {
|
||||
JitSpew(JitSpew_IonAbort, "too many args");
|
||||
TrackAndSpewIonAbort(cx, script, "too many args");
|
||||
ForbidCompilation(cx, script);
|
||||
return Method_CantCompile;
|
||||
}
|
||||
|
@ -2157,7 +2191,7 @@ jit::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFram
|
|||
MOZ_ASSERT(frame->isFunctionFrame());
|
||||
|
||||
// Mark as forbidden if frame can't be handled.
|
||||
if (!CheckFrame(frame)) {
|
||||
if (!CheckFrame(cx, frame)) {
|
||||
ForbidCompilation(cx, script);
|
||||
return Method_CantCompile;
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -18,6 +18,7 @@
|
|||
#include "jit/MIR.h"
|
||||
#include "jit/MIRGenerator.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "jit/OptimizationTracking.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
@ -234,6 +235,7 @@ class IonBuilder
|
|||
uint32_t readIndex(jsbytecode *pc);
|
||||
JSAtom *readAtom(jsbytecode *pc);
|
||||
bool abort(const char *message, ...);
|
||||
void trackActionableAbort(const char *message);
|
||||
void spew(const char *message);
|
||||
|
||||
JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes);
|
||||
|
@ -448,10 +450,10 @@ class IonBuilder
|
|||
bool isDOM);
|
||||
bool setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
|
||||
PropertyName *name, MDefinition *value,
|
||||
types::TemporaryTypeSet *objTypes);
|
||||
bool barrier, types::TemporaryTypeSet *objTypes);
|
||||
bool setPropTryInlineAccess(bool *emitted, MDefinition *obj,
|
||||
PropertyName *name, MDefinition *value,
|
||||
types::TemporaryTypeSet *objTypes);
|
||||
bool barrier, types::TemporaryTypeSet *objTypes);
|
||||
bool setPropTryTypedObject(bool *emitted, MDefinition *obj,
|
||||
PropertyName *name, MDefinition *value);
|
||||
bool setPropTryReferencePropOfTypedObject(bool *emitted,
|
||||
|
@ -518,6 +520,7 @@ class IonBuilder
|
|||
ReferenceTypeDescr::Type type,
|
||||
PropertyName *name);
|
||||
MDefinition *neuterCheck(MDefinition *obj);
|
||||
JSObject *getStaticTypedArrayObject(MDefinition *obj, MDefinition *index);
|
||||
|
||||
// jsop_setelem() helpers.
|
||||
bool setElemTryTypedArray(bool *emitted, MDefinition *object,
|
||||
|
@ -904,6 +907,14 @@ class IonBuilder
|
|||
// performed by FinishOffThreadBuilder().
|
||||
CodeGenerator *backgroundCodegen_;
|
||||
|
||||
// Some aborts are actionable (e.g., using an unsupported bytecode). When
|
||||
// optimization tracking is enabled, the location and message of the abort
|
||||
// are recorded here so they may be propagated to the script's
|
||||
// corresponding JitcodeGlobalEntry::BaselineEntry.
|
||||
JSScript *actionableAbortScript_;
|
||||
jsbytecode *actionableAbortPc_;
|
||||
const char *actionableAbortMessage_;
|
||||
|
||||
public:
|
||||
void clearForBackEnd();
|
||||
|
||||
|
@ -922,6 +933,21 @@ class IonBuilder
|
|||
|
||||
const JSAtomState &names() { return compartment->runtime()->names(); }
|
||||
|
||||
bool hadActionableAbort() const {
|
||||
MOZ_ASSERT(!actionableAbortScript_ ||
|
||||
(actionableAbortPc_ && actionableAbortMessage_));
|
||||
return actionableAbortScript_ != nullptr;
|
||||
}
|
||||
|
||||
void actionableAbortLocationAndMessage(JSScript **abortScript, jsbytecode **abortPc,
|
||||
const char **abortMessage)
|
||||
{
|
||||
MOZ_ASSERT(hadActionableAbort());
|
||||
*abortScript = actionableAbortScript_;
|
||||
*abortPc = actionableAbortPc_;
|
||||
*abortMessage = actionableAbortMessage_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool init();
|
||||
|
||||
|
@ -948,11 +974,20 @@ class IonBuilder
|
|||
MBasicBlock *current;
|
||||
uint32_t loopDepth_;
|
||||
|
||||
Vector<BytecodeSite *, 0, JitAllocPolicy> trackedOptimizationSites_;
|
||||
|
||||
BytecodeSite *bytecodeSite(jsbytecode *pc) {
|
||||
MOZ_ASSERT(info().inlineScriptTree()->script()->containsPC(pc));
|
||||
// See comment in maybeTrackedOptimizationSite.
|
||||
if (isOptimizationTrackingEnabled()) {
|
||||
if (BytecodeSite *site = maybeTrackedOptimizationSite(pc))
|
||||
return site;
|
||||
}
|
||||
return new(alloc()) BytecodeSite(info().inlineScriptTree(), pc);
|
||||
}
|
||||
|
||||
BytecodeSite *maybeTrackedOptimizationSite(jsbytecode *pc);
|
||||
|
||||
MDefinition *lexicalCheck_;
|
||||
|
||||
void setLexicalCheck(MDefinition *lexical) {
|
||||
|
@ -1051,6 +1086,59 @@ class IonBuilder
|
|||
}
|
||||
|
||||
MGetPropertyCache *maybeFallbackFunctionGetter_;
|
||||
|
||||
// Used in tracking outcomes of optimization strategies for devtools.
|
||||
void startTrackingOptimizations();
|
||||
|
||||
// The track* methods below are called often. Do not combine them with the
|
||||
// unchecked variants, despite the unchecked variants having no other
|
||||
// callers.
|
||||
void trackTypeInfo(TrackedTypeSite site, MIRType mirType,
|
||||
types::TemporaryTypeSet *typeSet)
|
||||
{
|
||||
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
|
||||
trackTypeInfoUnchecked(site, mirType, typeSet);
|
||||
}
|
||||
void trackTypeInfo(TrackedTypeSite site, JSObject *obj) {
|
||||
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
|
||||
trackTypeInfoUnchecked(site, obj);
|
||||
}
|
||||
void trackTypeInfo(CallInfo &callInfo) {
|
||||
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
|
||||
trackTypeInfoUnchecked(callInfo);
|
||||
}
|
||||
void trackOptimizationAttempt(TrackedStrategy strategy) {
|
||||
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
|
||||
trackOptimizationAttemptUnchecked(strategy);
|
||||
}
|
||||
void amendOptimizationAttempt(uint32_t index) {
|
||||
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
|
||||
amendOptimizationAttemptUnchecked(index);
|
||||
}
|
||||
void trackOptimizationOutcome(TrackedOutcome outcome) {
|
||||
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
|
||||
trackOptimizationOutcomeUnchecked(outcome);
|
||||
}
|
||||
void trackOptimizationSuccess() {
|
||||
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
|
||||
trackOptimizationSuccessUnchecked();
|
||||
}
|
||||
void trackInlineSuccess(InliningStatus status = InliningStatus_Inlined) {
|
||||
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
|
||||
trackInlineSuccessUnchecked(status);
|
||||
}
|
||||
|
||||
// Out-of-line variants that don't check if optimization tracking is
|
||||
// enabled.
|
||||
void trackTypeInfoUnchecked(TrackedTypeSite site, MIRType mirType,
|
||||
types::TemporaryTypeSet *typeSet);
|
||||
void trackTypeInfoUnchecked(TrackedTypeSite site, JSObject *obj);
|
||||
void trackTypeInfoUnchecked(CallInfo &callInfo);
|
||||
void trackOptimizationAttemptUnchecked(TrackedStrategy strategy);
|
||||
void amendOptimizationAttemptUnchecked(uint32_t index);
|
||||
void trackOptimizationOutcomeUnchecked(TrackedOutcome outcome);
|
||||
void trackOptimizationSuccessUnchecked();
|
||||
void trackInlineSuccessUnchecked(InliningStatus status);
|
||||
};
|
||||
|
||||
class CallInfo
|
||||
|
|
|
@ -402,6 +402,10 @@ class JitRuntime
|
|||
bool isProfilerInstrumentationEnabled(JSRuntime *rt) {
|
||||
return rt->spsProfiler.enabled();
|
||||
}
|
||||
|
||||
bool isOptimizationTrackingEnabled(JSRuntime *rt) {
|
||||
return isProfilerInstrumentationEnabled(rt);
|
||||
}
|
||||
};
|
||||
|
||||
class JitZone
|
||||
|
|
|
@ -255,6 +255,7 @@ jit::CheckLogging()
|
|||
" unroll Loop unrolling\n"
|
||||
" logs C1 and JSON visualization logging\n"
|
||||
" profiling Profiling-related information\n"
|
||||
" trackopts Optimization tracking information\n"
|
||||
" all Everything\n"
|
||||
"\n"
|
||||
" bl-aborts Baseline compiler abort messages\n"
|
||||
|
@ -315,6 +316,8 @@ jit::CheckLogging()
|
|||
EnableIonDebugLogging();
|
||||
if (ContainsFlag(env, "profiling"))
|
||||
EnableChannel(JitSpew_Profiling);
|
||||
if (ContainsFlag(env, "trackopts"))
|
||||
EnableChannel(JitSpew_OptimizationTracking);
|
||||
if (ContainsFlag(env, "all"))
|
||||
LoggingBits = uint32_t(-1);
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ namespace jit {
|
|||
_(Pools) \
|
||||
/* Profiling-related information */ \
|
||||
_(Profiling) \
|
||||
/* Information of tracked opt strats */ \
|
||||
_(OptimizationTracking) \
|
||||
/* Debug info about the I$ */ \
|
||||
_(CacheFlush) \
|
||||
\
|
||||
|
|
|
@ -109,6 +109,19 @@ JitcodeGlobalEntry::IonEntry::destroy()
|
|||
// Free the script list
|
||||
js_free(scriptList_);
|
||||
scriptList_ = nullptr;
|
||||
|
||||
// The optimizations region and attempts table is in the same block of
|
||||
// memory, the beginning of which is pointed to by
|
||||
// optimizationsRegionTable_->payloadStart().
|
||||
if (optsRegionTable_) {
|
||||
MOZ_ASSERT(optsAttemptsTable_);
|
||||
js_free((void *) optsRegionTable_->payloadStart());
|
||||
}
|
||||
optsRegionTable_ = nullptr;
|
||||
optsTypesTable_ = nullptr;
|
||||
optsAttemptsTable_ = nullptr;
|
||||
js_delete(optsAllTypes_);
|
||||
optsAllTypes_ = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "ds/SplayTree.h"
|
||||
#include "jit/CompactBuffer.h"
|
||||
#include "jit/CompileInfo.h"
|
||||
#include "jit/OptimizationTracking.h"
|
||||
#include "jit/shared/CodeGenerator-shared.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -106,6 +107,23 @@ class JitcodeGlobalEntry
|
|||
// of the memory space.
|
||||
JitcodeIonTable *regionTable_;
|
||||
|
||||
// optsRegionTable_ points to the table within the compact
|
||||
// optimizations map indexing all regions that have tracked
|
||||
// optimization attempts. optsTypesTable_ is the tracked typed info
|
||||
// associated with the attempts vectors; it is the same length as the
|
||||
// attempts table. optsAttemptsTable_ is the table indexing those
|
||||
// attempts vectors.
|
||||
//
|
||||
// All pointers point into the same block of memory; the beginning of
|
||||
// the block is optimizationRegionTable_->payloadStart().
|
||||
const IonTrackedOptimizationsRegionTable *optsRegionTable_;
|
||||
const IonTrackedOptimizationsTypesTable *optsTypesTable_;
|
||||
const IonTrackedOptimizationsAttemptsTable *optsAttemptsTable_;
|
||||
|
||||
// The types table above records type sets, which have been gathered
|
||||
// into one vector here.
|
||||
types::TypeSet::TypeList *optsAllTypes_;
|
||||
|
||||
struct ScriptNamePair {
|
||||
JSScript *script;
|
||||
char *str;
|
||||
|
@ -136,6 +154,21 @@ class JitcodeGlobalEntry
|
|||
BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr);
|
||||
regionTable_ = regionTable;
|
||||
scriptList_ = scriptList;
|
||||
optsRegionTable_ = nullptr;
|
||||
optsTypesTable_ = nullptr;
|
||||
optsAllTypes_ = nullptr;
|
||||
optsAttemptsTable_ = nullptr;
|
||||
}
|
||||
|
||||
void initTrackedOptimizations(const IonTrackedOptimizationsRegionTable *regionTable,
|
||||
const IonTrackedOptimizationsTypesTable *typesTable,
|
||||
const IonTrackedOptimizationsAttemptsTable *attemptsTable,
|
||||
types::TypeSet::TypeList *allTypes)
|
||||
{
|
||||
optsRegionTable_ = regionTable;
|
||||
optsTypesTable_ = typesTable;
|
||||
optsAttemptsTable_ = attemptsTable;
|
||||
optsAllTypes_ = allTypes;
|
||||
}
|
||||
|
||||
SizedScriptList *sizedScriptList() const {
|
||||
|
@ -176,6 +209,12 @@ class JitcodeGlobalEntry
|
|||
|
||||
uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results,
|
||||
uint32_t maxResults) const;
|
||||
|
||||
bool hasTrackedOptimizations() const {
|
||||
return !!optsRegionTable_;
|
||||
}
|
||||
|
||||
bool optimizationAttemptsAtAddr(void *ptr, mozilla::Maybe<AttemptsVector> &attempts);
|
||||
};
|
||||
|
||||
struct BaselineEntry : public BaseEntry
|
||||
|
@ -183,6 +222,12 @@ class JitcodeGlobalEntry
|
|||
JSScript *script_;
|
||||
const char *str_;
|
||||
|
||||
// Last location that caused Ion to abort compilation and the reason
|
||||
// therein, if any. Only actionable aborts are tracked. Internal
|
||||
// errors like OOMs are not.
|
||||
jsbytecode *ionAbortPc_;
|
||||
const char *ionAbortMessage_;
|
||||
|
||||
void init(void *nativeStartAddr, void *nativeEndAddr, JSScript *script, const char *str)
|
||||
{
|
||||
MOZ_ASSERT(script != nullptr);
|
||||
|
@ -199,6 +244,18 @@ class JitcodeGlobalEntry
|
|||
return str_;
|
||||
}
|
||||
|
||||
void trackIonAbort(jsbytecode *pc, const char *message) {
|
||||
MOZ_ASSERT(script_->containsPC(pc));
|
||||
MOZ_ASSERT(message);
|
||||
ionAbortPc_ = pc;
|
||||
ionAbortMessage_ = message;
|
||||
}
|
||||
|
||||
bool hadIonAbort() const {
|
||||
MOZ_ASSERT(!ionAbortPc_ || ionAbortMessage_);
|
||||
return ionAbortPc_ != nullptr;
|
||||
}
|
||||
|
||||
void destroy();
|
||||
|
||||
bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results,
|
||||
|
|
|
@ -33,8 +33,13 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
|
|||
MOZ_ASSERT(target->isNative());
|
||||
JSNative native = target->native();
|
||||
|
||||
if (!optimizationInfo().inlineNative())
|
||||
if (!optimizationInfo().inlineNative()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Default failure reason is observing an unsupported type.
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType);
|
||||
|
||||
// Atomic natives.
|
||||
if (native == atomics_compareExchange)
|
||||
|
@ -355,8 +360,10 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
|||
AllocatingBehaviour allocating = NewArray_Unallocating;
|
||||
|
||||
JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array);
|
||||
if (!templateObject)
|
||||
if (!templateObject) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
ArrayObject *templateArray = &templateObject->as<ArrayObject>();
|
||||
|
||||
// Multiple arguments imply array initialization, not just construction.
|
||||
|
@ -402,6 +409,9 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
|||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
// The next several checks all may fail due to range conditions.
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayRange);
|
||||
|
||||
// Negative lengths generate a RangeError, unhandled by the inline path.
|
||||
initLength = arg->constantValue().toInt32();
|
||||
if (initLength >= NativeObject::NELEMENTS_LIMIT)
|
||||
|
@ -476,8 +486,10 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
if (callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MIRType returnType = getInlineReturnType();
|
||||
if (returnType == MIRType_Undefined || returnType == MIRType_Null)
|
||||
|
@ -497,11 +509,15 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
|
|||
types::TemporaryTypeSet *thisTypes = obj->resultTypeSet();
|
||||
if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags))
|
||||
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (types::ArrayPrototypeHasIndexedProperty(constraints(), script()))
|
||||
if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
|
@ -533,8 +549,10 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArraySplice(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 2 || callInfo.constructing())
|
||||
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Ensure |this|, argument and result are objects.
|
||||
if (getInlineReturnType() != MIRType_Object)
|
||||
|
@ -550,8 +568,10 @@ IonBuilder::inlineArraySplice(CallInfo &callInfo)
|
|||
|
||||
// Specialize arr.splice(start, deleteCount) with unused return value and
|
||||
// avoid creating the result array in this case.
|
||||
if (!BytecodeIsPopped(pc))
|
||||
if (!BytecodeIsPopped(pc)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MArraySplice *ins = MArraySplice::New(alloc(),
|
||||
callInfo.thisArg(),
|
||||
|
@ -569,8 +589,10 @@ IonBuilder::inlineArraySplice(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArrayJoin(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_String)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -592,14 +614,17 @@ IonBuilder::inlineArrayJoin(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArrayPush(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MDefinition *obj = callInfo.thisArg();
|
||||
MDefinition *value = callInfo.getArg(0);
|
||||
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
|
||||
&obj, nullptr, &value, /* canModify = */ false))
|
||||
{
|
||||
trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
MOZ_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0));
|
||||
|
@ -615,16 +640,21 @@ IonBuilder::inlineArrayPush(CallInfo &callInfo)
|
|||
if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
|
||||
types::OBJECT_FLAG_LENGTH_OVERFLOW))
|
||||
{
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (types::ArrayPrototypeHasIndexedProperty(constraints(), script()))
|
||||
if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
types::TemporaryTypeSet::DoubleConversion conversion =
|
||||
thisTypes->convertDoubleElements(constraints());
|
||||
if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion)
|
||||
if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
value = callInfo.getArg(0);
|
||||
|
@ -654,8 +684,10 @@ IonBuilder::inlineArrayPush(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArrayConcat(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Ensure |this|, argument and result are objects.
|
||||
if (getInlineReturnType() != MIRType_Object)
|
||||
|
@ -676,6 +708,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo)
|
|||
if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
|
||||
types::OBJECT_FLAG_LENGTH_OVERFLOW))
|
||||
{
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
|
@ -684,12 +717,15 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo)
|
|||
if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
|
||||
types::OBJECT_FLAG_LENGTH_OVERFLOW))
|
||||
{
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Watch out for indexed properties on the prototype.
|
||||
if (types::ArrayPrototypeHasIndexedProperty(constraints(), script()))
|
||||
if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Require the 'this' types to have a specific type matching the current
|
||||
// global, so we can create the result object inline.
|
||||
|
@ -708,6 +744,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo)
|
|||
if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) &&
|
||||
argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED))
|
||||
{
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
|
@ -755,11 +792,10 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathAbs(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.argc() != 1)
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MIRType returnType = getInlineReturnType();
|
||||
MIRType argType = callInfo.getArg(0)->type();
|
||||
|
@ -790,11 +826,10 @@ IonBuilder::inlineMathAbs(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathFloor(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.argc() != 1)
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MIRType argType = callInfo.getArg(0)->type();
|
||||
MIRType returnType = getInlineReturnType();
|
||||
|
@ -835,11 +870,10 @@ IonBuilder::inlineMathFloor(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathCeil(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.argc() != 1)
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MIRType argType = callInfo.getArg(0)->type();
|
||||
MIRType returnType = getInlineReturnType();
|
||||
|
@ -880,11 +914,10 @@ IonBuilder::inlineMathCeil(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathClz32(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.argc() != 1)
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MIRType returnType = getInlineReturnType();
|
||||
if (returnType != MIRType_Int32)
|
||||
|
@ -905,11 +938,10 @@ IonBuilder::inlineMathClz32(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathRound(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.argc() != 1)
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MIRType returnType = getInlineReturnType();
|
||||
MIRType argType = callInfo.getArg(0)->type();
|
||||
|
@ -950,11 +982,10 @@ IonBuilder::inlineMathRound(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathSqrt(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.argc() != 1)
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MIRType argType = callInfo.getArg(0)->type();
|
||||
if (getInlineReturnType() != MIRType_Double)
|
||||
|
@ -973,11 +1004,10 @@ IonBuilder::inlineMathSqrt(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathAtan2(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.argc() != 2)
|
||||
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_Double)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -999,12 +1029,16 @@ IonBuilder::inlineMathAtan2(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathHypot(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
if (callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
uint32_t argc = callInfo.argc();
|
||||
if (argc < 2 || argc > 4)
|
||||
if (argc < 2 || argc > 4) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_Double)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -1034,11 +1068,10 @@ IonBuilder::inlineMathHypot(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathPow(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.argc() != 2)
|
||||
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Typechecking.
|
||||
MIRType baseType = callInfo.getArg(0)->type();
|
||||
|
@ -1140,8 +1173,10 @@ IonBuilder::inlineMathPow(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathRandom(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing())
|
||||
if (callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_Double)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -1157,8 +1192,10 @@ IonBuilder::inlineMathRandom(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathImul(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 2 || callInfo.constructing())
|
||||
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MIRType returnType = getInlineReturnType();
|
||||
if (returnType != MIRType_Int32)
|
||||
|
@ -1186,8 +1223,10 @@ IonBuilder::inlineMathImul(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathFRound(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// MIRType can't be Float32, as this point, as getInlineReturnType uses JSVal types
|
||||
// to infer the returned MIR type.
|
||||
|
@ -1217,8 +1256,10 @@ IonBuilder::inlineMathFRound(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineMathMinMax(CallInfo &callInfo, bool max)
|
||||
{
|
||||
if (callInfo.argc() < 1 || callInfo.constructing())
|
||||
if (callInfo.argc() < 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MIRType returnType = getInlineReturnType();
|
||||
if (!IsNumberType(returnType))
|
||||
|
@ -1286,8 +1327,10 @@ IonBuilder::inlineMathMinMax(CallInfo &callInfo, bool max)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineStringObject(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || !callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || !callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// ConvertToString doesn't support objects.
|
||||
if (callInfo.getArg(0)->mightBeType(MIRType_Object))
|
||||
|
@ -1313,8 +1356,11 @@ IonBuilder::inlineStringObject(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineStringSplit(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (callInfo.thisArg()->type() != MIRType_String)
|
||||
return InliningStatus_NotInlined;
|
||||
if (callInfo.getArg(0)->type() != MIRType_String)
|
||||
|
@ -1353,8 +1399,10 @@ IonBuilder::inlineStringSplit(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineStrCharCodeAt(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_Int32)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -1389,11 +1437,10 @@ IonBuilder::inlineStrCharCodeAt(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineConstantCharCodeAt(CallInfo &callInfo)
|
||||
{
|
||||
if (!callInfo.thisArg()->isConstantValue())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (!callInfo.getArg(0)->isConstantValue())
|
||||
if (!callInfo.thisArg()->isConstantValue() || !callInfo.getArg(0)->isConstantValue()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
const js::Value *strval = callInfo.thisArg()->constantVp();
|
||||
const js::Value *idxval = callInfo.getArg(0)->constantVp();
|
||||
|
@ -1402,12 +1449,16 @@ IonBuilder::inlineConstantCharCodeAt(CallInfo &callInfo)
|
|||
return InliningStatus_NotInlined;
|
||||
|
||||
JSString *str = strval->toString();
|
||||
if (!str->isLinear())
|
||||
if (!str->isLinear()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
int32_t idx = idxval->toInt32();
|
||||
if (idx < 0 || (uint32_t(idx) >= str->length()))
|
||||
if (idx < 0 || (uint32_t(idx) >= str->length())) {
|
||||
trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
|
@ -1422,8 +1473,10 @@ IonBuilder::inlineConstantCharCodeAt(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineStrFromCharCode(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_String)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -1444,8 +1497,10 @@ IonBuilder::inlineStrFromCharCode(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineStrCharAt(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_String)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -1478,8 +1533,10 @@ IonBuilder::inlineStrCharAt(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineRegExpExec(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (callInfo.thisArg()->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -1514,8 +1571,10 @@ IonBuilder::inlineRegExpExec(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineRegExpTest(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// TI can infer a nullptr return type of regexp_test with eager compilation.
|
||||
if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean)
|
||||
|
@ -1548,8 +1607,10 @@ IonBuilder::inlineRegExpTest(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineStrReplace(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 2 || callInfo.constructing())
|
||||
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Return: String.
|
||||
if (getInlineReturnType() != MIRType_String)
|
||||
|
@ -1666,8 +1727,10 @@ IonBuilder::InliningStatus
|
|||
IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
|
||||
{
|
||||
uint32_t argc = callInfo.argc();
|
||||
if (argc < 3 || (argc % 3) != 0 || callInfo.constructing())
|
||||
if (argc < 3 || (argc % 3) != 0 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
/* Important:
|
||||
*
|
||||
|
@ -1834,8 +1897,10 @@ IonBuilder::inlineHasClass(CallInfo &callInfo,
|
|||
const Class *clasp1, const Class *clasp2,
|
||||
const Class *clasp3, const Class *clasp4)
|
||||
{
|
||||
if (callInfo.constructing() || callInfo.argc() != 1)
|
||||
if (callInfo.constructing() || callInfo.argc() != 1) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (callInfo.getArg(0)->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -1942,8 +2007,10 @@ IonBuilder::inlineTypedArrayLength(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineObjectIsTypeDescr(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.constructing() || callInfo.argc() != 1)
|
||||
if (callInfo.constructing() || callInfo.argc() != 1) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (callInfo.getArg(0)->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -1979,8 +2046,10 @@ IonBuilder::inlineObjectIsTypeDescr(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineSetTypedObjectOffset(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 2 || callInfo.constructing())
|
||||
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MDefinition *typedObj = callInfo.getArg(0);
|
||||
MDefinition *offset = callInfo.getArg(1);
|
||||
|
@ -2021,8 +2090,10 @@ IonBuilder::inlineSetTypedObjectOffset(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineUnsafeSetReservedSlot(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 3 || callInfo.constructing())
|
||||
if (callInfo.argc() != 3 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
if (getInlineReturnType() != MIRType_Undefined)
|
||||
return InliningStatus_NotInlined;
|
||||
if (callInfo.getArg(0)->type() != MIRType_Object)
|
||||
|
@ -2051,8 +2122,10 @@ IonBuilder::inlineUnsafeSetReservedSlot(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo, MIRType knownValueType)
|
||||
{
|
||||
if (callInfo.argc() != 2 || callInfo.constructing())
|
||||
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
if (callInfo.getArg(0)->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
if (callInfo.getArg(1)->type() != MIRType_Int32)
|
||||
|
@ -2091,8 +2164,10 @@ IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo, MIRType knownValueTy
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineIsCallable(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_Boolean)
|
||||
return InliningStatus_NotInlined;
|
||||
|
@ -2134,8 +2209,10 @@ IonBuilder::inlineIsCallable(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineIsObject(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
if (getInlineReturnType() != MIRType_Boolean)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
|
@ -2153,8 +2230,10 @@ IonBuilder::inlineIsObject(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineToObject(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// If we know the input type is an object, nop ToObject.
|
||||
if (getInlineReturnType() != MIRType_Object)
|
||||
|
@ -2172,8 +2251,10 @@ IonBuilder::inlineToObject(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineToInteger(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MDefinition *input = callInfo.getArg(0);
|
||||
|
||||
|
@ -2253,8 +2334,10 @@ IonBuilder::inlineAssertFloat32(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineBoundFunction(CallInfo &nativeCallInfo, JSFunction *target)
|
||||
{
|
||||
if (!target->getBoundFunctionTarget()->is<JSFunction>())
|
||||
return InliningStatus_NotInlined;
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineBound);
|
||||
|
||||
if (!target->getBoundFunctionTarget()->is<JSFunction>())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
JSFunction *scriptedTarget = &(target->getBoundFunctionTarget()->as<JSFunction>());
|
||||
|
||||
|
@ -2307,8 +2390,10 @@ IonBuilder::inlineBoundFunction(CallInfo &nativeCallInfo, JSFunction *target)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsCompareExchange(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 4 || callInfo.constructing())
|
||||
if (callInfo.argc() != 4 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
Scalar::Type arrayType;
|
||||
if (!atomicsMeetsPreconditions(callInfo, &arrayType))
|
||||
|
@ -2353,8 +2438,10 @@ IonBuilder::inlineAtomicsCompareExchange(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsLoad(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 2 || callInfo.constructing())
|
||||
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
Scalar::Type arrayType;
|
||||
if (!atomicsMeetsPreconditions(callInfo, &arrayType))
|
||||
|
@ -2379,8 +2466,10 @@ IonBuilder::inlineAtomicsLoad(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsStore(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 3 || callInfo.constructing())
|
||||
if (callInfo.argc() != 3 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
Scalar::Type arrayType;
|
||||
if (!atomicsMeetsPreconditions(callInfo, &arrayType))
|
||||
|
@ -2413,8 +2502,10 @@ IonBuilder::inlineAtomicsStore(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsFence(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 0 || callInfo.constructing())
|
||||
if (callInfo.argc() != 0 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
|
@ -2428,8 +2519,10 @@ IonBuilder::inlineAtomicsFence(CallInfo &callInfo)
|
|||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsBinop(CallInfo &callInfo, JSFunction *target)
|
||||
{
|
||||
if (callInfo.argc() != 3 || callInfo.constructing())
|
||||
if (callInfo.argc() != 3 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
Scalar::Type arrayType;
|
||||
if (!atomicsMeetsPreconditions(callInfo, &arrayType))
|
||||
|
@ -2552,8 +2645,10 @@ IonBuilder::InliningStatus
|
|||
IonBuilder::inlineConstructTypedObject(CallInfo &callInfo, TypeDescr *descr)
|
||||
{
|
||||
// Only inline default constructors for now.
|
||||
if (callInfo.argc() != 0)
|
||||
if (callInfo.argc() != 0) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (size_t(descr->size()) > InlineTypedObject::MaximumSize)
|
||||
return InliningStatus_NotInlined;
|
||||
|
|
|
@ -2058,18 +2058,17 @@ MMinMax::foldsTo(TempAllocator &alloc)
|
|||
|
||||
// The folded MConstant should maintain the same MIRType with
|
||||
// the original MMinMax.
|
||||
MConstant *constant;
|
||||
if (type() == MIRType_Int32) {
|
||||
int32_t cast = static_cast<int32_t>(result);
|
||||
MOZ_ASSERT(cast == result);
|
||||
constant = MConstant::New(alloc, Int32Value(cast));
|
||||
int32_t cast;
|
||||
if (mozilla::NumberEqualsInt32(result, &cast))
|
||||
return MConstant::New(alloc, Int32Value(cast));
|
||||
} else {
|
||||
MOZ_ASSERT(IsFloatingPointType(type()));
|
||||
constant = MConstant::New(alloc, DoubleValue(result));
|
||||
MConstant *constant = MConstant::New(alloc, DoubleValue(result));
|
||||
if (type() == MIRType_Float32)
|
||||
constant->setResultType(MIRType_Float32);
|
||||
return constant;
|
||||
}
|
||||
return constant;
|
||||
}
|
||||
|
||||
MDefinition *operand = lhs()->isConstantValue() ? rhs() : lhs();
|
||||
|
@ -3045,6 +3044,32 @@ MDefinition *
|
|||
MToInt32::foldsTo(TempAllocator &alloc)
|
||||
{
|
||||
MDefinition *input = getOperand(0);
|
||||
|
||||
// Fold this operation if the input operand is constant.
|
||||
if (input->isConstant()) {
|
||||
Value val = input->toConstant()->value();
|
||||
DebugOnly<MacroAssembler::IntConversionInputKind> convert = conversion();
|
||||
switch (input->type()) {
|
||||
case MIRType_Null:
|
||||
MOZ_ASSERT(convert == MacroAssembler::IntConversion_Any);
|
||||
return MConstant::New(alloc, Int32Value(0));
|
||||
case MIRType_Boolean:
|
||||
MOZ_ASSERT(convert == MacroAssembler::IntConversion_Any ||
|
||||
convert == MacroAssembler::IntConversion_NumbersOrBoolsOnly);
|
||||
return MConstant::New(alloc, Int32Value(val.toBoolean()));
|
||||
case MIRType_Int32:
|
||||
return MConstant::New(alloc, Int32Value(val.toInt32()));
|
||||
case MIRType_Float32:
|
||||
case MIRType_Double:
|
||||
int32_t ival;
|
||||
// Only the value within the range of Int32 can be substitued as constant.
|
||||
if (mozilla::NumberEqualsInt32(val.toNumber(), &ival))
|
||||
return MConstant::New(alloc, Int32Value(ival));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (input->type() == MIRType_Int32)
|
||||
return input;
|
||||
return this;
|
||||
|
|
|
@ -456,6 +456,11 @@ class MDefinition : public MNode
|
|||
InlineScriptTree *trackedTree() const {
|
||||
return trackedSite_ ? trackedSite_->tree() : nullptr;
|
||||
}
|
||||
TrackedOptimizations *trackedOptimizations() const {
|
||||
return trackedSite_ && trackedSite_->hasOptimizations()
|
||||
? trackedSite_->optimizations()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
JSScript *profilerLeaveScript() const {
|
||||
return trackedTree()->outermostCaller()->script();
|
||||
|
|
|
@ -87,6 +87,10 @@ class MIRGenerator
|
|||
return !compilingAsmJS() && instrumentedProfiling();
|
||||
}
|
||||
|
||||
bool isOptimizationTrackingEnabled() {
|
||||
return isProfilerInstrumentationEnabled() && !info().isAnalysis();
|
||||
}
|
||||
|
||||
// Whether the main thread is trying to cancel this build.
|
||||
bool shouldCancel(const char *why) {
|
||||
maybePause();
|
||||
|
|
|
@ -204,7 +204,7 @@ MIRGraph::unmarkBlocks()
|
|||
|
||||
MBasicBlock *
|
||||
MBasicBlock::New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info,
|
||||
MBasicBlock *pred, const BytecodeSite *site, Kind kind)
|
||||
MBasicBlock *pred, BytecodeSite *site, Kind kind)
|
||||
{
|
||||
MOZ_ASSERT(site->pc() != nullptr);
|
||||
|
||||
|
@ -220,7 +220,7 @@ MBasicBlock::New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info,
|
|||
|
||||
MBasicBlock *
|
||||
MBasicBlock::NewPopN(MIRGraph &graph, CompileInfo &info,
|
||||
MBasicBlock *pred, const BytecodeSite *site, Kind kind, uint32_t popped)
|
||||
MBasicBlock *pred, BytecodeSite *site, Kind kind, uint32_t popped)
|
||||
{
|
||||
MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
|
||||
if (!block->init())
|
||||
|
@ -234,7 +234,7 @@ MBasicBlock::NewPopN(MIRGraph &graph, CompileInfo &info,
|
|||
|
||||
MBasicBlock *
|
||||
MBasicBlock::NewWithResumePoint(MIRGraph &graph, CompileInfo &info,
|
||||
MBasicBlock *pred, const BytecodeSite *site,
|
||||
MBasicBlock *pred, BytecodeSite *site,
|
||||
MResumePoint *resumePoint)
|
||||
{
|
||||
MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, NORMAL);
|
||||
|
@ -256,7 +256,7 @@ MBasicBlock::NewWithResumePoint(MIRGraph &graph, CompileInfo &info,
|
|||
|
||||
MBasicBlock *
|
||||
MBasicBlock::NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info,
|
||||
MBasicBlock *pred, const BytecodeSite *site,
|
||||
MBasicBlock *pred, BytecodeSite *site,
|
||||
unsigned stackPhiCount)
|
||||
{
|
||||
MOZ_ASSERT(site->pc() != nullptr);
|
||||
|
@ -324,7 +324,7 @@ MBasicBlock::NewAsmJS(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred, Kin
|
|||
return block;
|
||||
}
|
||||
|
||||
MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, const BytecodeSite *site, Kind kind)
|
||||
MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, BytecodeSite *site, Kind kind)
|
||||
: unreachable_(false),
|
||||
graph_(graph),
|
||||
info_(info),
|
||||
|
|
|
@ -46,7 +46,7 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
|||
};
|
||||
|
||||
private:
|
||||
MBasicBlock(MIRGraph &graph, CompileInfo &info, const BytecodeSite *site, Kind kind);
|
||||
MBasicBlock(MIRGraph &graph, CompileInfo &info, BytecodeSite *site, Kind kind);
|
||||
bool init();
|
||||
void copySlots(MBasicBlock *from);
|
||||
bool inherit(TempAllocator &alloc, BytecodeAnalysis *analysis, MBasicBlock *pred,
|
||||
|
@ -107,14 +107,14 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
|||
// Creates a new basic block for a MIR generator. If |pred| is not nullptr,
|
||||
// its slots and stack depth are initialized from |pred|.
|
||||
static MBasicBlock *New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info,
|
||||
MBasicBlock *pred, const BytecodeSite *site, Kind kind);
|
||||
MBasicBlock *pred, BytecodeSite *site, Kind kind);
|
||||
static MBasicBlock *NewPopN(MIRGraph &graph, CompileInfo &info,
|
||||
MBasicBlock *pred, const BytecodeSite *site, Kind kind, uint32_t popn);
|
||||
MBasicBlock *pred, BytecodeSite *site, Kind kind, uint32_t popn);
|
||||
static MBasicBlock *NewWithResumePoint(MIRGraph &graph, CompileInfo &info,
|
||||
MBasicBlock *pred, const BytecodeSite *site,
|
||||
MBasicBlock *pred, BytecodeSite *site,
|
||||
MResumePoint *resumePoint);
|
||||
static MBasicBlock *NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info,
|
||||
MBasicBlock *pred, const BytecodeSite *site,
|
||||
MBasicBlock *pred, BytecodeSite *site,
|
||||
unsigned loopStateSlots);
|
||||
static MBasicBlock *NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred);
|
||||
static MBasicBlock *NewAsmJS(MIRGraph &graph, CompileInfo &info,
|
||||
|
@ -602,13 +602,14 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
|||
void dump(FILE *fp);
|
||||
void dump();
|
||||
|
||||
// Track bailouts by storing the current pc in MIR instruction added at this
|
||||
// cycle. This is also used for tracking calls when profiling.
|
||||
void updateTrackedSite(const BytecodeSite *site) {
|
||||
// Track bailouts by storing the current pc in MIR instruction added at
|
||||
// this cycle. This is also used for tracking calls and optimizations when
|
||||
// profiling.
|
||||
void updateTrackedSite(BytecodeSite *site) {
|
||||
MOZ_ASSERT(site->tree() == trackedSite_->tree());
|
||||
trackedSite_ = site;
|
||||
}
|
||||
const BytecodeSite *trackedSite() const {
|
||||
BytecodeSite *trackedSite() const {
|
||||
return trackedSite_;
|
||||
}
|
||||
jsbytecode *trackedPc() const {
|
||||
|
@ -657,7 +658,7 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
|||
Vector<MBasicBlock *, 1, JitAllocPolicy> immediatelyDominated_;
|
||||
MBasicBlock *immediateDominator_;
|
||||
|
||||
const BytecodeSite *trackedSite_;
|
||||
BytecodeSite *trackedSite_;
|
||||
|
||||
#if defined(JS_ION_PERF) || defined(DEBUG)
|
||||
unsigned lineno_;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,764 @@
|
|||
/* -*- 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_OptimizationTracking_h
|
||||
#define jit_OptimizationTracking_h
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "jsinfer.h"
|
||||
#include "jit/CompactBuffer.h"
|
||||
#include "jit/CompileInfo.h"
|
||||
#include "jit/JitAllocPolicy.h"
|
||||
#include "jit/shared/CodeGenerator-shared.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace jit {
|
||||
|
||||
#define TRACKED_STRATEGY_LIST(_) \
|
||||
_(GetProp_ArgumentsLength, \
|
||||
"getprop arguments.length") \
|
||||
_(GetProp_ArgumentsCallee, \
|
||||
"getprop arguments.callee") \
|
||||
_(GetProp_InferredConstant, \
|
||||
"getprop inferred constant") \
|
||||
_(GetProp_Constant, \
|
||||
"getprop constant") \
|
||||
_(GetProp_TypedObject, \
|
||||
"getprop TypedObject") \
|
||||
_(GetProp_DefiniteSlot, \
|
||||
"getprop definite slot") \
|
||||
_(GetProp_CommonGetter, \
|
||||
"getprop common getter") \
|
||||
_(GetProp_InlineAccess, \
|
||||
"getprop inline access") \
|
||||
_(GetProp_Innerize, \
|
||||
"getprop innerize (access on global window)") \
|
||||
_(GetProp_InlineCache, \
|
||||
"getprop IC") \
|
||||
\
|
||||
_(SetProp_CommonSetter, \
|
||||
"setprop common setter") \
|
||||
_(SetProp_TypedObject, \
|
||||
"setprop TypedObject") \
|
||||
_(SetProp_DefiniteSlot, \
|
||||
"setprop definite slot") \
|
||||
_(SetProp_InlineAccess, \
|
||||
"setprop inline access") \
|
||||
\
|
||||
_(GetElem_TypedObject, \
|
||||
"getprop TypedObject") \
|
||||
_(GetElem_Dense, \
|
||||
"getelem dense") \
|
||||
_(GetElem_TypedStatic, \
|
||||
"getelem TypedArray static") \
|
||||
_(GetElem_TypedArray, \
|
||||
"getelem TypedArray") \
|
||||
_(GetElem_String, \
|
||||
"getelem string") \
|
||||
_(GetElem_Arguments, \
|
||||
"getelem arguments") \
|
||||
_(GetElem_ArgumentsInlined, \
|
||||
"getelem arguments inlined") \
|
||||
_(GetElem_InlineCache, \
|
||||
"getelem IC") \
|
||||
\
|
||||
_(SetElem_TypedObject, \
|
||||
"setelem TypedObject") \
|
||||
_(SetElem_TypedStatic, \
|
||||
"setelem TypedArray static") \
|
||||
_(SetElem_TypedArray, \
|
||||
"setelem TypedArray") \
|
||||
_(SetElem_Dense, \
|
||||
"setelem dense") \
|
||||
_(SetElem_Arguments, \
|
||||
"setelem arguments") \
|
||||
_(SetElem_InlineCache, \
|
||||
"setelem IC") \
|
||||
\
|
||||
_(Call_Inline, \
|
||||
"call inline")
|
||||
|
||||
|
||||
// Ordering is important below. All outcomes before GenericSuccess will be
|
||||
// considered failures, and all outcomes after GenericSuccess will be
|
||||
// considered successes.
|
||||
#define TRACKED_OUTCOME_LIST(_) \
|
||||
_(GenericFailure, \
|
||||
"failure") \
|
||||
_(Disabled, \
|
||||
"disabled") \
|
||||
_(NoTypeInfo, \
|
||||
"no type info") \
|
||||
_(NoAnalysisInfo, \
|
||||
"no newscript analysis") \
|
||||
_(NoShapeInfo, \
|
||||
"cannot determine shape") \
|
||||
_(UnknownObject, \
|
||||
"unknown object") \
|
||||
_(UnknownProperties, \
|
||||
"unknown properties") \
|
||||
_(Singleton, \
|
||||
"is singleton") \
|
||||
_(NotSingleton, \
|
||||
"is not singleton") \
|
||||
_(NotFixedSlot, \
|
||||
"property not in fixed slot") \
|
||||
_(NotObject, \
|
||||
"not definitely an object") \
|
||||
_(NotStruct, \
|
||||
"not definitely a TypedObject struct") \
|
||||
_(StructNoField, \
|
||||
"struct doesn't definitely have field") \
|
||||
_(NeedsTypeBarrier, \
|
||||
"needs type barrier") \
|
||||
_(InDictionaryMode, \
|
||||
"object in dictionary mode") \
|
||||
_(NoProtoFound, \
|
||||
"no proto found") \
|
||||
_(MultiProtoPaths, \
|
||||
"not all paths to property go through same proto") \
|
||||
_(NonWritableProperty, \
|
||||
"non-writable property") \
|
||||
_(ProtoIndexedProps, \
|
||||
"prototype has indexed properties") \
|
||||
_(ArrayBadFlags, \
|
||||
"array observed to be sparse, overflowed .length, or has been iterated") \
|
||||
_(ArrayDoubleConversion, \
|
||||
"array has ambiguous double conversion") \
|
||||
_(ArrayRange, \
|
||||
"array range issue (.length problems)") \
|
||||
_(ArraySeenNegativeIndex, \
|
||||
"has seen array access with negative index") \
|
||||
_(TypedObjectNeutered, \
|
||||
"TypedObject might have been neutered") \
|
||||
_(TypedObjectArrayRange, \
|
||||
"TypedObject array of unknown length") \
|
||||
_(AccessNotDense, \
|
||||
"access not on dense native (check receiver, index, and result types)") \
|
||||
_(AccessNotTypedObject, \
|
||||
"access not on typed array (check receiver and index types)") \
|
||||
_(AccessNotTypedArray, \
|
||||
"access not on typed array (check receiver, index, and result types)") \
|
||||
_(AccessNotString, \
|
||||
"getelem not on string (check receiver and index types)") \
|
||||
_(StaticTypedArrayUint32, \
|
||||
"static uint32 arrays currently cannot be optimized") \
|
||||
_(StaticTypedArrayCantComputeMask, \
|
||||
"can't compute mask for static typed array access (index isn't constant or not int32)") \
|
||||
_(OutOfBounds, \
|
||||
"observed out of bounds access") \
|
||||
_(GetElemStringNotCached, \
|
||||
"getelem on strings is not inline cached") \
|
||||
_(NonNativeReceiver, \
|
||||
"observed non-native receiver") \
|
||||
_(IndexType, \
|
||||
"index type must be int32, string, or symbol") \
|
||||
_(SetElemNonDenseNonTANotCached, \
|
||||
"setelem on non-dense non-TAs are not inline cached") \
|
||||
\
|
||||
_(CantInlineGeneric, \
|
||||
"can't inline") \
|
||||
_(CantInlineNoTarget, \
|
||||
"can't inline: no target") \
|
||||
_(CantInlineNotInterpreted, \
|
||||
"can't inline: not interpreted") \
|
||||
_(CantInlineNoBaseline, \
|
||||
"can't inline: no baseline code") \
|
||||
_(CantInlineLazy, \
|
||||
"can't inline: lazy script") \
|
||||
_(CantInlineNotConstructor, \
|
||||
"can't inline: calling non-constructor with 'new'") \
|
||||
_(CantInlineDisabledIon, \
|
||||
"can't inline: ion disabled for callee") \
|
||||
_(CantInlineTooManyArgs, \
|
||||
"can't inline: too many arguments") \
|
||||
_(CantInlineRecursive, \
|
||||
"can't inline: recursive") \
|
||||
_(CantInlineHeavyweight, \
|
||||
"can't inline: heavyweight") \
|
||||
_(CantInlineNeedsArgsObj, \
|
||||
"can't inline: needs arguments object") \
|
||||
_(CantInlineDebuggee, \
|
||||
"can't inline: debuggee") \
|
||||
_(CantInlineUnknownProps, \
|
||||
"can't inline: type has unknown properties") \
|
||||
_(CantInlineExceededDepth, \
|
||||
"can't inline: exceeded inlining depth") \
|
||||
_(CantInlineBigLoop, \
|
||||
"can't inline: big function with a loop") \
|
||||
_(CantInlineBigCaller, \
|
||||
"can't inline: big caller") \
|
||||
_(CantInlineBigCallee, \
|
||||
"can't inline: big callee") \
|
||||
_(CantInlineNotHot, \
|
||||
"can't inline: not hot enough") \
|
||||
_(CantInlineNotInDispatch, \
|
||||
"can't inline: not in dispatch table") \
|
||||
_(CantInlineNativeBadForm, \
|
||||
"can't inline native: bad form (arity mismatch/constructing)") \
|
||||
_(CantInlineNativeBadType, \
|
||||
"can't inline native: bad argument or return type observed") \
|
||||
_(CantInlineNativeNoTemplateObj, \
|
||||
"can't inline native: no template object") \
|
||||
_(CantInlineBound, \
|
||||
"can't inline bound function invocation") \
|
||||
\
|
||||
_(GenericSuccess, \
|
||||
"success") \
|
||||
_(Inlined, \
|
||||
"inlined") \
|
||||
_(DOM, \
|
||||
"DOM") \
|
||||
_(Monomorphic, \
|
||||
"monomorphic") \
|
||||
_(Polymorphic, \
|
||||
"polymorphic")
|
||||
|
||||
#define TRACKED_TYPESITE_LIST(_) \
|
||||
_(Receiver, \
|
||||
"receiver object") \
|
||||
_(Index, \
|
||||
"index") \
|
||||
_(Value, \
|
||||
"value") \
|
||||
_(Call_Target, \
|
||||
"call target") \
|
||||
_(Call_This, \
|
||||
"call 'this'") \
|
||||
_(Call_Arg, \
|
||||
"call argument") \
|
||||
_(Call_Return, \
|
||||
"call return")
|
||||
|
||||
enum class TrackedStrategy : uint32_t {
|
||||
#define STRATEGY_OP(name, msg) name,
|
||||
TRACKED_STRATEGY_LIST(STRATEGY_OP)
|
||||
#undef STRATEGY_OPT
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
enum class TrackedOutcome : uint32_t {
|
||||
#define OUTCOME_OP(name, msg) name,
|
||||
TRACKED_OUTCOME_LIST(OUTCOME_OP)
|
||||
#undef OUTCOME_OP
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
enum class TrackedTypeSite : uint32_t {
|
||||
#define TYPESITE_OP(name, msg) name,
|
||||
TRACKED_TYPESITE_LIST(TYPESITE_OP)
|
||||
#undef TYPESITE_OP
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
class OptimizationAttempt
|
||||
{
|
||||
TrackedStrategy strategy_;
|
||||
TrackedOutcome outcome_;
|
||||
|
||||
public:
|
||||
OptimizationAttempt(TrackedStrategy strategy, TrackedOutcome outcome)
|
||||
: strategy_(strategy),
|
||||
outcome_(outcome)
|
||||
{ }
|
||||
|
||||
void setOutcome(TrackedOutcome outcome) { outcome_ = outcome; }
|
||||
bool succeeded() const { return outcome_ >= TrackedOutcome::GenericSuccess; }
|
||||
bool failed() const { return outcome_ < TrackedOutcome::GenericSuccess; }
|
||||
TrackedStrategy strategy() const { return strategy_; }
|
||||
TrackedOutcome outcome() const { return outcome_; }
|
||||
|
||||
bool operator ==(const OptimizationAttempt &other) const {
|
||||
return strategy_ == other.strategy_ && outcome_ == other.outcome_;
|
||||
}
|
||||
bool operator !=(const OptimizationAttempt &other) const {
|
||||
return strategy_ != other.strategy_ || outcome_ != other.outcome_;
|
||||
}
|
||||
HashNumber hash() const {
|
||||
return (HashNumber(strategy_) << 8) + HashNumber(outcome_);
|
||||
}
|
||||
|
||||
explicit OptimizationAttempt(CompactBufferReader &reader);
|
||||
void writeCompact(CompactBufferWriter &writer) const;
|
||||
};
|
||||
|
||||
typedef Vector<OptimizationAttempt, 4, JitAllocPolicy> TempAttemptsVector;
|
||||
typedef Vector<OptimizationAttempt, 4, SystemAllocPolicy> AttemptsVector;
|
||||
|
||||
class UniqueTrackedTypes;
|
||||
|
||||
class TrackedTypeInfo
|
||||
{
|
||||
TrackedTypeSite site_;
|
||||
MIRType mirType_;
|
||||
types::TypeSet::TypeList types_;
|
||||
|
||||
public:
|
||||
TrackedTypeInfo(TrackedTypeInfo &&other)
|
||||
: site_(other.site_),
|
||||
mirType_(other.mirType_),
|
||||
types_(mozilla::Move(other.types_))
|
||||
{ }
|
||||
|
||||
TrackedTypeInfo(TrackedTypeSite site, MIRType mirType)
|
||||
: site_(site),
|
||||
mirType_(mirType)
|
||||
{ }
|
||||
|
||||
bool trackTypeSet(types::TemporaryTypeSet *typeSet);
|
||||
bool trackType(types::Type type);
|
||||
|
||||
TrackedTypeSite site() const { return site_; }
|
||||
MIRType mirType() const { return mirType_; }
|
||||
const types::TypeSet::TypeList &types() const { return types_; }
|
||||
|
||||
bool operator ==(const TrackedTypeInfo &other) const;
|
||||
bool operator !=(const TrackedTypeInfo &other) const;
|
||||
|
||||
HashNumber hash() const;
|
||||
|
||||
// This constructor is designed to be used in conjunction with readTypes
|
||||
// below it. The same reader must be passed to readTypes after
|
||||
// instantiating the TrackedTypeInfo.
|
||||
explicit TrackedTypeInfo(CompactBufferReader &reader);
|
||||
bool readTypes(CompactBufferReader &reader, const types::TypeSet::TypeList *allTypes);
|
||||
bool writeCompact(CompactBufferWriter &writer, UniqueTrackedTypes &uniqueTypes) const;
|
||||
};
|
||||
|
||||
typedef Vector<TrackedTypeInfo, 1, JitAllocPolicy> TempTrackedTypeInfoVector;
|
||||
typedef Vector<TrackedTypeInfo, 1, SystemAllocPolicy> TrackedTypeInfoVector;
|
||||
|
||||
// Tracks the optimization attempts made at a bytecode location.
|
||||
class TrackedOptimizations : public TempObject
|
||||
{
|
||||
friend class UniqueTrackedOptimizations;
|
||||
TempTrackedTypeInfoVector types_;
|
||||
TempAttemptsVector attempts_;
|
||||
uint32_t currentAttempt_;
|
||||
|
||||
public:
|
||||
explicit TrackedOptimizations(TempAllocator &alloc)
|
||||
: types_(alloc),
|
||||
attempts_(alloc),
|
||||
currentAttempt_(UINT32_MAX)
|
||||
{ }
|
||||
|
||||
bool trackTypeInfo(TrackedTypeInfo &&ty);
|
||||
|
||||
bool trackAttempt(TrackedStrategy strategy);
|
||||
void amendAttempt(uint32_t index);
|
||||
void trackOutcome(TrackedOutcome outcome);
|
||||
void trackSuccess();
|
||||
|
||||
bool matchTypes(const TempTrackedTypeInfoVector &other) const;
|
||||
bool matchAttempts(const TempAttemptsVector &other) const;
|
||||
|
||||
void spew() const;
|
||||
};
|
||||
|
||||
// Assigns each unique sequence of optimization attempts an index; outputs a
|
||||
// compact table.
|
||||
class UniqueTrackedOptimizations
|
||||
{
|
||||
public:
|
||||
struct SortEntry
|
||||
{
|
||||
const TempTrackedTypeInfoVector *types;
|
||||
const TempAttemptsVector *attempts;
|
||||
uint32_t frequency;
|
||||
};
|
||||
typedef Vector<SortEntry, 4> SortedVector;
|
||||
|
||||
private:
|
||||
struct Key
|
||||
{
|
||||
const TempTrackedTypeInfoVector *types;
|
||||
const TempAttemptsVector *attempts;
|
||||
|
||||
typedef Key Lookup;
|
||||
static HashNumber hash(const Lookup &lookup);
|
||||
static bool match(const Key &key, const Lookup &lookup);
|
||||
static void rekey(Key &key, const Key &newKey) {
|
||||
key = newKey;
|
||||
}
|
||||
};
|
||||
|
||||
struct Entry
|
||||
{
|
||||
uint8_t index;
|
||||
uint32_t frequency;
|
||||
};
|
||||
|
||||
// Map of unique (TempTrackedTypeInfoVector, TempAttemptsVector) pairs to
|
||||
// indices.
|
||||
typedef HashMap<Key, Entry, Key> AttemptsMap;
|
||||
AttemptsMap map_;
|
||||
|
||||
// TempAttemptsVectors sorted by frequency.
|
||||
SortedVector sorted_;
|
||||
|
||||
public:
|
||||
explicit UniqueTrackedOptimizations(JSContext *cx)
|
||||
: map_(cx),
|
||||
sorted_(cx)
|
||||
{ }
|
||||
|
||||
bool init() { return map_.init(); }
|
||||
bool add(const TrackedOptimizations *optimizations);
|
||||
|
||||
bool sortByFrequency(JSContext *cx);
|
||||
bool sorted() const { return !sorted_.empty(); }
|
||||
uint32_t count() const { MOZ_ASSERT(sorted()); return sorted_.length(); }
|
||||
const SortedVector &sortedVector() const { MOZ_ASSERT(sorted()); return sorted_; }
|
||||
uint8_t indexOf(const TrackedOptimizations *optimizations) const;
|
||||
};
|
||||
|
||||
// A compact table of tracked optimization information. Pictorially,
|
||||
//
|
||||
// +------------------------------------------------+
|
||||
// | Region 1 | |
|
||||
// |------------------------------------------------| |
|
||||
// | Region 2 | |
|
||||
// |------------------------------------------------| |-- PayloadR of list-of-list of
|
||||
// | ... | | range triples (see below)
|
||||
// |------------------------------------------------| |
|
||||
// | Region M | |
|
||||
// +================================================+ <- IonTrackedOptimizationsRegionTable
|
||||
// | uint32_t numRegions_ = M | |
|
||||
// +------------------------------------------------+ |
|
||||
// | Region 1 | |
|
||||
// | uint32_t regionOffset = size(PayloadR) | |
|
||||
// +------------------------------------------------+ |-- Table
|
||||
// | ... | |
|
||||
// +------------------------------------------------+ |
|
||||
// | Region M | |
|
||||
// | uint32_t regionOffset | |
|
||||
// +================================================+
|
||||
// | Optimization type info 1 | |
|
||||
// |------------------------------------------------| |
|
||||
// | Optimization type info 2 | |-- PayloadT of list of
|
||||
// |------------------------------------------------| | IonTrackedOptimizationTypeInfo in
|
||||
// | ... | | order of decreasing frequency
|
||||
// |------------------------------------------------| |
|
||||
// | Optimization type info N | |
|
||||
// +================================================+ <- IonTrackedOptimizationsTypesTable
|
||||
// | uint32_t numEntries_ = N | |
|
||||
// +------------------------------------------------+ |
|
||||
// | Optimization type info 1 | |
|
||||
// | uint32_t entryOffset = size(PayloadT) | |
|
||||
// +------------------------------------------------+ |-- Table
|
||||
// | ... | |
|
||||
// +------------------------------------------------+ |
|
||||
// | Optimization type info N | |
|
||||
// | uint32_t entryOffset | |
|
||||
// +================================================+
|
||||
// | Optimization attempts 1 | |
|
||||
// |------------------------------------------------| |
|
||||
// | Optimization attempts 2 | |-- PayloadA of list of
|
||||
// |------------------------------------------------| | IonTrackedOptimizationAttempts in
|
||||
// | ... | | order of decreasing frequency
|
||||
// |------------------------------------------------| |
|
||||
// | Optimization attempts N | |
|
||||
// +================================================+ <- IonTrackedOptimizationsAttemptsTable
|
||||
// | uint32_t numEntries_ = N | |
|
||||
// +------------------------------------------------+ |
|
||||
// | Optimization attempts 1 | |
|
||||
// | uint32_t entryOffset = size(PayloadA) | |
|
||||
// +------------------------------------------------+ |-- Table
|
||||
// | ... | |
|
||||
// +------------------------------------------------+ |
|
||||
// | Optimization attempts N | |
|
||||
// | uint32_t entryOffset | |
|
||||
// +------------------------------------------------+
|
||||
//
|
||||
// Abstractly, each region in the PayloadR section is a list of triples of the
|
||||
// following, in order of ascending startOffset:
|
||||
//
|
||||
// (startOffset, endOffset, optimization attempts index)
|
||||
//
|
||||
// The range of [startOffset, endOffset) is the native machine code offsets
|
||||
// for which the optimization attempts referred to by the index applies.
|
||||
//
|
||||
// Concretely, each region starts with a header of:
|
||||
//
|
||||
// { startOffset : 32, endOffset : 32 }
|
||||
//
|
||||
// followed by an (endOffset, index) pair, then by delta-encoded variants
|
||||
// triples described below.
|
||||
//
|
||||
// Each list of type infos in the PayloadT section is a list of triples:
|
||||
//
|
||||
// (kind, MIR type, type set)
|
||||
//
|
||||
// The type set is separately in another vector, and what is encoded instead
|
||||
// is the (offset, length) pair needed to index into that vector.
|
||||
//
|
||||
// Each list of optimization attempts in the PayloadA section is a list of
|
||||
// pairs:
|
||||
//
|
||||
// (strategy, outcome)
|
||||
//
|
||||
// Both tail tables for PayloadR and PayloadA use reverse offsets from the
|
||||
// table pointers.
|
||||
|
||||
class IonTrackedOptimizationsRegion
|
||||
{
|
||||
const uint8_t *start_;
|
||||
const uint8_t *end_;
|
||||
|
||||
// Unpacked state.
|
||||
uint32_t startOffset_;
|
||||
uint32_t endOffset_;
|
||||
const uint8_t *rangesStart_;
|
||||
|
||||
void unpackHeader();
|
||||
|
||||
public:
|
||||
IonTrackedOptimizationsRegion(const uint8_t *start, const uint8_t *end)
|
||||
: start_(start), end_(end),
|
||||
startOffset_(0), endOffset_(0), rangesStart_(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(start < end);
|
||||
unpackHeader();
|
||||
}
|
||||
|
||||
// Offsets for the entire range that this region covers.
|
||||
//
|
||||
// This, as well as the offsets for the deltas, is open at the ending
|
||||
// address: [startOffset, endOffset).
|
||||
uint32_t startOffset() const { return startOffset_; }
|
||||
uint32_t endOffset() const { return endOffset_; }
|
||||
|
||||
class RangeIterator {
|
||||
const uint8_t *cur_;
|
||||
const uint8_t *start_;
|
||||
const uint8_t *end_;
|
||||
|
||||
uint32_t firstStartOffset_;
|
||||
uint32_t prevEndOffset_;
|
||||
|
||||
public:
|
||||
RangeIterator(const uint8_t *start, const uint8_t *end, uint32_t startOffset)
|
||||
: cur_(start), start_(start), end_(end),
|
||||
firstStartOffset_(startOffset), prevEndOffset_(0)
|
||||
{ }
|
||||
|
||||
bool more() const { return cur_ < end_; }
|
||||
void readNext(uint32_t *startOffset, uint32_t *endOffset, uint8_t *index);
|
||||
};
|
||||
|
||||
RangeIterator ranges() const { return RangeIterator(rangesStart_, end_, startOffset_); }
|
||||
|
||||
mozilla::Maybe<uint8_t> findAttemptsIndex(uint32_t offset) const;
|
||||
|
||||
// For the variants below, S stands for startDelta, L for length, and I
|
||||
// for index. These were automatically generated from training on the
|
||||
// Octane benchmark.
|
||||
//
|
||||
// byte 1 byte 0
|
||||
// SSSS-SSSL LLLL-LII0
|
||||
// startDelta max 127, length max 63, index max 3
|
||||
|
||||
static const uint32_t ENC1_MASK = 0x1;
|
||||
static const uint32_t ENC1_MASK_VAL = 0x0;
|
||||
|
||||
static const uint32_t ENC1_START_DELTA_MAX = 0x7f;
|
||||
static const uint32_t ENC1_START_DELTA_SHIFT = 9;
|
||||
|
||||
static const uint32_t ENC1_LENGTH_MAX = 0x3f;
|
||||
static const uint32_t ENC1_LENGTH_SHIFT = 3;
|
||||
|
||||
static const uint32_t ENC1_INDEX_MAX = 0x3;
|
||||
static const uint32_t ENC1_INDEX_SHIFT = 1;
|
||||
|
||||
// byte 2 byte 1 byte 0
|
||||
// SSSS-SSSS SSSS-LLLL LLII-II01
|
||||
// startDelta max 4095, length max 63, index max 15
|
||||
|
||||
static const uint32_t ENC2_MASK = 0x3;
|
||||
static const uint32_t ENC2_MASK_VAL = 0x1;
|
||||
|
||||
static const uint32_t ENC2_START_DELTA_MAX = 0xfff;
|
||||
static const uint32_t ENC2_START_DELTA_SHIFT = 12;
|
||||
|
||||
static const uint32_t ENC2_LENGTH_MAX = 0x3f;
|
||||
static const uint32_t ENC2_LENGTH_SHIFT = 6;
|
||||
|
||||
static const uint32_t ENC2_INDEX_MAX = 0xf;
|
||||
static const uint32_t ENC2_INDEX_SHIFT = 2;
|
||||
|
||||
// byte 3 byte 2 byte 1 byte 0
|
||||
// SSSS-SSSS SSSL-LLLL LLLL-LIII IIII-I011
|
||||
// startDelta max 2047, length max 1023, index max 255
|
||||
|
||||
static const uint32_t ENC3_MASK = 0x7;
|
||||
static const uint32_t ENC3_MASK_VAL = 0x3;
|
||||
|
||||
static const uint32_t ENC3_START_DELTA_MAX = 0x7ff;
|
||||
static const uint32_t ENC3_START_DELTA_SHIFT = 21;
|
||||
|
||||
static const uint32_t ENC3_LENGTH_MAX = 0x3ff;
|
||||
static const uint32_t ENC3_LENGTH_SHIFT = 11;
|
||||
|
||||
static const uint32_t ENC3_INDEX_MAX = 0xff;
|
||||
static const uint32_t ENC3_INDEX_SHIFT = 3;
|
||||
|
||||
// byte 4 byte 3 byte 2 byte 1 byte 0
|
||||
// SSSS-SSSS SSSS-SSSL LLLL-LLLL LLLL-LIII IIII-I111
|
||||
// startDelta max 32767, length max 16383, index max 255
|
||||
|
||||
static const uint32_t ENC4_MASK = 0x7;
|
||||
static const uint32_t ENC4_MASK_VAL = 0x7;
|
||||
|
||||
static const uint32_t ENC4_START_DELTA_MAX = 0x7fff;
|
||||
static const uint32_t ENC4_START_DELTA_SHIFT = 25;
|
||||
|
||||
static const uint32_t ENC4_LENGTH_MAX = 0x3fff;
|
||||
static const uint32_t ENC4_LENGTH_SHIFT = 11;
|
||||
|
||||
static const uint32_t ENC4_INDEX_MAX = 0xff;
|
||||
static const uint32_t ENC4_INDEX_SHIFT = 3;
|
||||
|
||||
static bool IsDeltaEncodeable(uint32_t startDelta, uint32_t length) {
|
||||
MOZ_ASSERT(length != 0);
|
||||
return startDelta <= ENC4_START_DELTA_MAX && length <= ENC4_LENGTH_MAX;
|
||||
}
|
||||
|
||||
static const uint32_t MAX_RUN_LENGTH = 100;
|
||||
|
||||
typedef CodeGeneratorShared::NativeToTrackedOptimizations NativeToTrackedOptimizations;
|
||||
static uint32_t ExpectedRunLength(const NativeToTrackedOptimizations *start,
|
||||
const NativeToTrackedOptimizations *end);
|
||||
|
||||
static void ReadDelta(CompactBufferReader &reader, uint32_t *startDelta, uint32_t *length,
|
||||
uint8_t *index);
|
||||
static void WriteDelta(CompactBufferWriter &writer, uint32_t startDelta, uint32_t length,
|
||||
uint8_t index);
|
||||
static bool WriteRun(CompactBufferWriter &writer,
|
||||
const NativeToTrackedOptimizations *start,
|
||||
const NativeToTrackedOptimizations *end,
|
||||
const UniqueTrackedOptimizations &unique);
|
||||
};
|
||||
|
||||
class IonTrackedOptimizationsAttempts
|
||||
{
|
||||
const uint8_t *start_;
|
||||
const uint8_t *end_;
|
||||
|
||||
public:
|
||||
IonTrackedOptimizationsAttempts(const uint8_t *start, const uint8_t *end)
|
||||
: start_(start), end_(end)
|
||||
{
|
||||
// Cannot be empty.
|
||||
MOZ_ASSERT(start < end);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool readVector(T *attempts) {
|
||||
CompactBufferReader reader(start_, end_);
|
||||
const uint8_t *cur = start_;
|
||||
while (cur != end_) {
|
||||
if (!attempts->append(OptimizationAttempt(reader)))
|
||||
return false;
|
||||
cur = reader.currentPosition();
|
||||
MOZ_ASSERT(cur <= end_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class IonTrackedOptimizationsTypeInfo
|
||||
{
|
||||
const uint8_t *start_;
|
||||
const uint8_t *end_;
|
||||
|
||||
public:
|
||||
IonTrackedOptimizationsTypeInfo(const uint8_t *start, const uint8_t *end)
|
||||
: start_(start), end_(end)
|
||||
{
|
||||
// Can be empty; i.e., no type info was tracked.
|
||||
}
|
||||
|
||||
bool empty() const { return start_ == end_; }
|
||||
|
||||
template <class T>
|
||||
bool readVector(T *types, const types::TypeSet::TypeList *allTypes) {
|
||||
CompactBufferReader reader(start_, end_);
|
||||
const uint8_t *cur = start_;
|
||||
while (cur != end_) {
|
||||
TrackedTypeInfo ty(reader);
|
||||
if (!ty.readTypes(reader, allTypes))
|
||||
return false;
|
||||
if (!types->append(mozilla::Move(ty)))
|
||||
return false;
|
||||
cur = reader.currentPosition();
|
||||
MOZ_ASSERT(cur <= end_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Entry>
|
||||
class IonTrackedOptimizationsOffsetsTable
|
||||
{
|
||||
uint32_t padding_;
|
||||
uint32_t numEntries_;
|
||||
uint32_t entryOffsets_[1];
|
||||
|
||||
protected:
|
||||
const uint8_t *payloadEnd() const {
|
||||
return (uint8_t *)(this) - padding_;
|
||||
}
|
||||
|
||||
public:
|
||||
uint32_t numEntries() const { return numEntries_; }
|
||||
uint32_t entryOffset(uint32_t index) const {
|
||||
MOZ_ASSERT(index < numEntries());
|
||||
return entryOffsets_[index];
|
||||
}
|
||||
|
||||
Entry entry(uint32_t index) const {
|
||||
const uint8_t *start = payloadEnd() - entryOffset(index);
|
||||
const uint8_t *end = payloadEnd();
|
||||
if (index < numEntries() - 1)
|
||||
end -= entryOffset(index + 1);
|
||||
return Entry(start, end);
|
||||
}
|
||||
};
|
||||
|
||||
class IonTrackedOptimizationsRegionTable
|
||||
: public IonTrackedOptimizationsOffsetsTable<IonTrackedOptimizationsRegion>
|
||||
{
|
||||
public:
|
||||
mozilla::Maybe<IonTrackedOptimizationsRegion> findRegion(uint32_t offset) const;
|
||||
|
||||
const uint8_t *payloadStart() const { return payloadEnd() - entryOffset(0); }
|
||||
};
|
||||
|
||||
typedef IonTrackedOptimizationsOffsetsTable<IonTrackedOptimizationsAttempts>
|
||||
IonTrackedOptimizationsAttemptsTable;
|
||||
|
||||
typedef IonTrackedOptimizationsOffsetsTable<IonTrackedOptimizationsTypeInfo>
|
||||
IonTrackedOptimizationsTypesTable;
|
||||
|
||||
bool
|
||||
WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &writer,
|
||||
const CodeGeneratorShared::NativeToTrackedOptimizations *start,
|
||||
const CodeGeneratorShared::NativeToTrackedOptimizations *end,
|
||||
const UniqueTrackedOptimizations &unique,
|
||||
uint32_t *numRegions, uint32_t *regionTableOffsetp,
|
||||
uint32_t *typesTableOffsetp, uint32_t *attemptsTableOffsetp,
|
||||
types::TypeSet::TypeList *allTypes);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif // jit_OptimizationTracking_h
|
|
@ -15,6 +15,7 @@
|
|||
#include "jit/MacroAssembler.h"
|
||||
#include "jit/MIR.h"
|
||||
#include "jit/MIRGenerator.h"
|
||||
#include "jit/OptimizationTracking.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
|
@ -59,6 +60,11 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, Mac
|
|||
nativeToBytecodeNumRegions_(0),
|
||||
nativeToBytecodeScriptList_(nullptr),
|
||||
nativeToBytecodeScriptListLength_(0),
|
||||
trackedOptimizationsMap_(nullptr),
|
||||
trackedOptimizationsMapSize_(0),
|
||||
trackedOptimizationsRegionTableOffset_(0),
|
||||
trackedOptimizationsTypesTableOffset_(0),
|
||||
trackedOptimizationsAttemptsTableOffset_(0),
|
||||
osrEntryOffset_(0),
|
||||
skipArgCheckEntryOffset_(0),
|
||||
#ifdef CHECK_OSIPOINT_REGISTERS
|
||||
|
@ -261,6 +267,53 @@ CodeGeneratorShared::dumpNativeToBytecodeEntry(uint32_t idx)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorShared::addTrackedOptimizationsEntry(const TrackedOptimizations *optimizations)
|
||||
{
|
||||
if (!isOptimizationTrackingEnabled())
|
||||
return true;
|
||||
|
||||
MOZ_ASSERT(optimizations);
|
||||
|
||||
uint32_t nativeOffset = masm.currentOffset();
|
||||
|
||||
if (!trackedOptimizations_.empty()) {
|
||||
NativeToTrackedOptimizations &lastEntry = trackedOptimizations_.back();
|
||||
MOZ_ASSERT(nativeOffset >= lastEntry.endOffset.offset());
|
||||
|
||||
// If we're still generating code for the same set of optimizations,
|
||||
// we are done.
|
||||
if (lastEntry.optimizations == optimizations)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're generating code for a new set of optimizations, add a new
|
||||
// entry.
|
||||
NativeToTrackedOptimizations entry;
|
||||
entry.startOffset = CodeOffsetLabel(nativeOffset);
|
||||
entry.endOffset = CodeOffsetLabel(nativeOffset);
|
||||
entry.optimizations = optimizations;
|
||||
return trackedOptimizations_.append(entry);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorShared::extendTrackedOptimizationsEntry(const TrackedOptimizations *optimizations)
|
||||
{
|
||||
if (!isOptimizationTrackingEnabled())
|
||||
return;
|
||||
|
||||
uint32_t nativeOffset = masm.currentOffset();
|
||||
NativeToTrackedOptimizations &entry = trackedOptimizations_.back();
|
||||
MOZ_ASSERT(entry.optimizations == optimizations);
|
||||
MOZ_ASSERT(nativeOffset >= entry.endOffset.offset());
|
||||
|
||||
entry.endOffset = CodeOffsetLabel(nativeOffset);
|
||||
|
||||
// If we generated no code, remove the last entry.
|
||||
if (nativeOffset == entry.startOffset.offset())
|
||||
trackedOptimizations_.popBack();
|
||||
}
|
||||
|
||||
// see OffsetOfFrameSlot
|
||||
static inline int32_t
|
||||
ToStackIndex(LAllocation *a)
|
||||
|
@ -722,6 +775,159 @@ CodeGeneratorShared::verifyCompactNativeToBytecodeMap(JitCode *code)
|
|||
#endif // DEBUG
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorShared::generateCompactTrackedOptimizationsMap(JSContext *cx, JitCode *code,
|
||||
types::TypeSet::TypeList *allTypes)
|
||||
{
|
||||
MOZ_ASSERT(trackedOptimizationsMap_ == nullptr);
|
||||
MOZ_ASSERT(trackedOptimizationsMapSize_ == 0);
|
||||
MOZ_ASSERT(trackedOptimizationsRegionTableOffset_ == 0);
|
||||
MOZ_ASSERT(trackedOptimizationsTypesTableOffset_ == 0);
|
||||
MOZ_ASSERT(trackedOptimizationsAttemptsTableOffset_ == 0);
|
||||
|
||||
if (trackedOptimizations_.empty())
|
||||
return true;
|
||||
|
||||
UniqueTrackedOptimizations unique(cx);
|
||||
if (!unique.init())
|
||||
return false;
|
||||
|
||||
// Iterate through all entries, fix up their masm offsets and deduplicate
|
||||
// their optimization attempts.
|
||||
for (size_t i = 0; i < trackedOptimizations_.length(); i++) {
|
||||
NativeToTrackedOptimizations &entry = trackedOptimizations_[i];
|
||||
entry.startOffset = CodeOffsetLabel(masm.actualOffset(entry.startOffset.offset()));
|
||||
entry.endOffset = CodeOffsetLabel(masm.actualOffset(entry.endOffset.offset()));
|
||||
if (!unique.add(entry.optimizations))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort the unique optimization attempts by frequency to stabilize the
|
||||
// attempts' indices in the compact table we will write later.
|
||||
if (!unique.sortByFrequency(cx))
|
||||
return false;
|
||||
|
||||
// Write out the ranges and the table.
|
||||
CompactBufferWriter writer;
|
||||
uint32_t numRegions;
|
||||
uint32_t regionTableOffset;
|
||||
uint32_t typesTableOffset;
|
||||
uint32_t attemptsTableOffset;
|
||||
if (!WriteIonTrackedOptimizationsTable(cx, writer,
|
||||
trackedOptimizations_.begin(),
|
||||
trackedOptimizations_.end(),
|
||||
unique, &numRegions,
|
||||
®ionTableOffset, &typesTableOffset,
|
||||
&attemptsTableOffset, allTypes))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(regionTableOffset > 0);
|
||||
MOZ_ASSERT(typesTableOffset > 0);
|
||||
MOZ_ASSERT(attemptsTableOffset > 0);
|
||||
MOZ_ASSERT(typesTableOffset > regionTableOffset);
|
||||
MOZ_ASSERT(attemptsTableOffset > typesTableOffset);
|
||||
|
||||
// Copy over the table out of the writer's buffer.
|
||||
uint8_t *data = cx->runtime()->pod_malloc<uint8_t>(writer.length());
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
memcpy(data, writer.buffer(), writer.length());
|
||||
trackedOptimizationsMap_ = data;
|
||||
trackedOptimizationsMapSize_ = writer.length();
|
||||
trackedOptimizationsRegionTableOffset_ = regionTableOffset;
|
||||
trackedOptimizationsTypesTableOffset_ = typesTableOffset;
|
||||
trackedOptimizationsAttemptsTableOffset_ = attemptsTableOffset;
|
||||
|
||||
verifyCompactTrackedOptimizationsMap(code, numRegions, unique, allTypes);
|
||||
|
||||
JitSpew(JitSpew_OptimizationTracking,
|
||||
"== Compact Native To Optimizations Map [%p-%p] size %u",
|
||||
data, data + trackedOptimizationsMapSize_, trackedOptimizationsMapSize_);
|
||||
JitSpew(JitSpew_OptimizationTracking,
|
||||
" with type list of length %u, size %u",
|
||||
allTypes->length(), allTypes->length() * sizeof(types::Type));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorShared::verifyCompactTrackedOptimizationsMap(JitCode *code, uint32_t numRegions,
|
||||
const UniqueTrackedOptimizations &unique,
|
||||
const types::TypeSet::TypeList *allTypes)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(trackedOptimizationsMap_ != nullptr);
|
||||
MOZ_ASSERT(trackedOptimizationsMapSize_ > 0);
|
||||
MOZ_ASSERT(trackedOptimizationsRegionTableOffset_ > 0);
|
||||
MOZ_ASSERT(trackedOptimizationsTypesTableOffset_ > 0);
|
||||
MOZ_ASSERT(trackedOptimizationsAttemptsTableOffset_ > 0);
|
||||
|
||||
// Table pointers must all be 4-byte aligned.
|
||||
const uint8_t *regionTableAddr = trackedOptimizationsMap_ +
|
||||
trackedOptimizationsRegionTableOffset_;
|
||||
const uint8_t *typesTableAddr = trackedOptimizationsMap_ +
|
||||
trackedOptimizationsTypesTableOffset_;
|
||||
const uint8_t *attemptsTableAddr = trackedOptimizationsMap_ +
|
||||
trackedOptimizationsAttemptsTableOffset_;
|
||||
MOZ_ASSERT(uintptr_t(regionTableAddr) % sizeof(uint32_t) == 0);
|
||||
MOZ_ASSERT(uintptr_t(typesTableAddr) % sizeof(uint32_t) == 0);
|
||||
MOZ_ASSERT(uintptr_t(attemptsTableAddr) % sizeof(uint32_t) == 0);
|
||||
|
||||
// Assert that the number of entries matches up for the tables.
|
||||
const IonTrackedOptimizationsRegionTable *regionTable =
|
||||
(const IonTrackedOptimizationsRegionTable *) regionTableAddr;
|
||||
MOZ_ASSERT(regionTable->numEntries() == numRegions);
|
||||
const IonTrackedOptimizationsTypesTable *typesTable =
|
||||
(const IonTrackedOptimizationsTypesTable *) typesTableAddr;
|
||||
MOZ_ASSERT(typesTable->numEntries() == unique.count());
|
||||
const IonTrackedOptimizationsAttemptsTable *attemptsTable =
|
||||
(const IonTrackedOptimizationsAttemptsTable *) attemptsTableAddr;
|
||||
MOZ_ASSERT(attemptsTable->numEntries() == unique.count());
|
||||
|
||||
// Verify each region.
|
||||
uint32_t trackedIdx = 0;
|
||||
for (uint32_t regionIdx = 0; regionIdx < regionTable->numEntries(); regionIdx++) {
|
||||
// Check reverse offsets are within bounds.
|
||||
MOZ_ASSERT(regionTable->entryOffset(regionIdx) <= trackedOptimizationsRegionTableOffset_);
|
||||
MOZ_ASSERT_IF(regionIdx > 0, regionTable->entryOffset(regionIdx) <
|
||||
regionTable->entryOffset(regionIdx - 1));
|
||||
|
||||
IonTrackedOptimizationsRegion region = regionTable->entry(regionIdx);
|
||||
|
||||
// Check the region range is covered by jitcode.
|
||||
MOZ_ASSERT(region.startOffset() <= code->instructionsSize());
|
||||
MOZ_ASSERT(region.endOffset() <= code->instructionsSize());
|
||||
|
||||
IonTrackedOptimizationsRegion::RangeIterator iter = region.ranges();
|
||||
while (iter.more()) {
|
||||
// Assert that the offsets are correctly decoded from the delta.
|
||||
uint32_t startOffset, endOffset;
|
||||
uint8_t index;
|
||||
iter.readNext(&startOffset, &endOffset, &index);
|
||||
NativeToTrackedOptimizations &entry = trackedOptimizations_[trackedIdx++];
|
||||
MOZ_ASSERT(startOffset == entry.startOffset.offset());
|
||||
MOZ_ASSERT(endOffset == entry.endOffset.offset());
|
||||
MOZ_ASSERT(index == unique.indexOf(entry.optimizations));
|
||||
|
||||
// Assert that the type info and attempts vector are correctly
|
||||
// decoded. Since this is a DEBUG-only verification, crash on OOM.
|
||||
IonTrackedOptimizationsTypeInfo typeInfo = typesTable->entry(index);
|
||||
TempTrackedTypeInfoVector tvec(alloc());
|
||||
MOZ_ALWAYS_TRUE(typeInfo.readVector(&tvec, allTypes));
|
||||
MOZ_ASSERT(entry.optimizations->matchTypes(tvec));
|
||||
|
||||
IonTrackedOptimizationsAttempts attempts = attemptsTable->entry(index);
|
||||
TempAttemptsVector avec(alloc());
|
||||
MOZ_ALWAYS_TRUE(attempts.readVector(&avec));
|
||||
MOZ_ASSERT(entry.optimizations->matchAttempts(avec));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorShared::markSafepoint(LInstruction *ins)
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@ class OutOfLineCode;
|
|||
class CodeGenerator;
|
||||
class MacroAssembler;
|
||||
class IonCache;
|
||||
class UniqueTrackedOptimizations;
|
||||
|
||||
template <class ArgSeq, class StoreOutputTo>
|
||||
class OutOfLineCallVM;
|
||||
|
@ -114,6 +115,26 @@ class CodeGeneratorShared : public LElementVisitor
|
|||
return gen->isProfilerInstrumentationEnabled();
|
||||
}
|
||||
|
||||
public:
|
||||
struct NativeToTrackedOptimizations {
|
||||
// [startOffset, endOffset)
|
||||
CodeOffsetLabel startOffset;
|
||||
CodeOffsetLabel endOffset;
|
||||
const TrackedOptimizations *optimizations;
|
||||
};
|
||||
|
||||
protected:
|
||||
js::Vector<NativeToTrackedOptimizations, 0, SystemAllocPolicy> trackedOptimizations_;
|
||||
uint8_t *trackedOptimizationsMap_;
|
||||
uint32_t trackedOptimizationsMapSize_;
|
||||
uint32_t trackedOptimizationsRegionTableOffset_;
|
||||
uint32_t trackedOptimizationsTypesTableOffset_;
|
||||
uint32_t trackedOptimizationsAttemptsTableOffset_;
|
||||
|
||||
bool isOptimizationTrackingEnabled() {
|
||||
return gen->isOptimizationTrackingEnabled();
|
||||
}
|
||||
|
||||
protected:
|
||||
// The offset of the first instruction of the OSR entry block from the
|
||||
// beginning of the code buffer.
|
||||
|
@ -243,6 +264,9 @@ class CodeGeneratorShared : public LElementVisitor
|
|||
void dumpNativeToBytecodeEntries();
|
||||
void dumpNativeToBytecodeEntry(uint32_t idx);
|
||||
|
||||
bool addTrackedOptimizationsEntry(const TrackedOptimizations *optimizations);
|
||||
void extendTrackedOptimizationsEntry(const TrackedOptimizations *optimizations);
|
||||
|
||||
public:
|
||||
MIRGenerator &mirGen() const {
|
||||
return *gen;
|
||||
|
@ -313,6 +337,12 @@ class CodeGeneratorShared : public LElementVisitor
|
|||
bool generateCompactNativeToBytecodeMap(JSContext *cx, JitCode *code);
|
||||
void verifyCompactNativeToBytecodeMap(JitCode *code);
|
||||
|
||||
bool generateCompactTrackedOptimizationsMap(JSContext *cx, JitCode *code,
|
||||
types::TypeSet::TypeList *allTypes);
|
||||
void verifyCompactTrackedOptimizationsMap(JitCode *code, uint32_t numRegions,
|
||||
const UniqueTrackedOptimizations &unique,
|
||||
const types::TypeSet::TypeList *allTypes);
|
||||
|
||||
// Mark the safepoint on |ins| as corresponding to the current assembler location.
|
||||
// The location should be just after a call.
|
||||
void markSafepoint(LInstruction *ins);
|
||||
|
|
|
@ -2180,7 +2180,7 @@ GCRuntime::relocateArenas()
|
|||
|
||||
if (CanRelocateZone(rt, zone)) {
|
||||
zone->setGCState(Zone::Compact);
|
||||
StopAllOffThreadCompilations(zone);
|
||||
jit::StopAllOffThreadCompilations(zone);
|
||||
relocatedList = zone->arenas.relocateArenas(relocatedList);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -420,7 +420,7 @@ TypeSet::isSubset(const TypeSet *other) const
|
|||
}
|
||||
|
||||
bool
|
||||
TypeSet::enumerateTypes(TypeList *list)
|
||||
TypeSet::enumerateTypes(TypeList *list) const
|
||||
{
|
||||
/* If any type is possible, there's no need to worry about specifics. */
|
||||
if (flags & TYPE_FLAG_UNKNOWN)
|
||||
|
|
|
@ -511,7 +511,7 @@ class TypeSet
|
|||
|
||||
/* Get a list of all types in this set. */
|
||||
typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
|
||||
bool enumerateTypes(TypeList *list);
|
||||
bool enumerateTypes(TypeList *list) const;
|
||||
|
||||
/*
|
||||
* Iterate through the objects in this set. getObjectCount overapproximates
|
||||
|
|
|
@ -178,6 +178,7 @@ UNIFIED_SOURCES += [
|
|||
'jit/MIR.cpp',
|
||||
'jit/MIRGraph.cpp',
|
||||
'jit/MoveResolver.cpp',
|
||||
'jit/OptimizationTracking.cpp',
|
||||
'jit/PerfSpewer.cpp',
|
||||
'jit/RangeAnalysis.cpp',
|
||||
'jit/Recover.cpp',
|
||||
|
|
|
@ -1551,8 +1551,8 @@ js_InitArrayBufferClass(JSContext *cx, HandleObject obj)
|
|||
|
||||
RootedId byteLengthId(cx, NameToId(cx->names().byteLength));
|
||||
unsigned attrs = JSPROP_SHARED | JSPROP_GETTER;
|
||||
JSObject *getter = NewFunction(cx, NullPtr(), ArrayBufferObject::byteLengthGetter, 0,
|
||||
JSFunction::NATIVE_FUN, global, NullPtr());
|
||||
JSObject *getter = NewFunction(cx, js::NullPtr(), ArrayBufferObject::byteLengthGetter, 0,
|
||||
JSFunction::NATIVE_FUN, global, js::NullPtr());
|
||||
if (!getter)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
*
|
||||
* To find the Shape for a particular property of an object initially requires
|
||||
* a linear search. But if the number of searches starting at any particular
|
||||
* Shape in the property tree exceeds MAX_LINEAR_SEARCHES and the Shape's
|
||||
* Shape in the property tree exceeds LINEAR_SEARCHES_MAX and the Shape's
|
||||
* lineage has (excluding the EmptyShape) at least MIN_ENTRIES, we create an
|
||||
* auxiliary hash table -- the ShapeTable -- that allows faster lookup.
|
||||
* Furthermore, a ShapeTable is always created for dictionary mode lists,
|
||||
|
|
|
@ -410,16 +410,14 @@ FontFaceSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
if (PR_LOG_TEST(nsFontFaceLoader::GetFontDownloaderLog(),
|
||||
PR_LOG_DEBUG)) {
|
||||
if (LOG_ENABLED()) {
|
||||
nsAutoCString fontURI, referrerURI;
|
||||
aFontFaceSrc->mURI->GetSpec(fontURI);
|
||||
if (aFontFaceSrc->mReferrer)
|
||||
aFontFaceSrc->mReferrer->GetSpec(referrerURI);
|
||||
PR_LOG(nsFontFaceLoader::GetFontDownloaderLog(), PR_LOG_DEBUG,
|
||||
("fontdownloader (%p) download start - font uri: (%s) "
|
||||
"referrer uri: (%s)\n",
|
||||
fontLoader.get(), fontURI.get(), referrerURI.get()));
|
||||
LOG(("userfonts (%p) download start - font uri: (%s) "
|
||||
"referrer uri: (%s)\n",
|
||||
fontLoader.get(), fontURI.get(), referrerURI.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -612,10 +610,12 @@ FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
|
|||
mUserFontSet->mLocalRulesUsed = false;
|
||||
|
||||
#if PR_LOGGING
|
||||
LOG(("userfonts (%p) userfont rules update (%s) rule count: %d",
|
||||
mUserFontSet.get(),
|
||||
(modified ? "modified" : "not modified"),
|
||||
mRuleFaces.Length()));
|
||||
if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) {
|
||||
LOG(("userfonts (%p) userfont rules update (%s) rule count: %d",
|
||||
mUserFontSet.get(),
|
||||
(modified ? "modified" : "not modified"),
|
||||
mRuleFaces.Length()));
|
||||
}
|
||||
#endif
|
||||
|
||||
return modified;
|
||||
|
@ -1073,9 +1073,8 @@ FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
|
|||
message.Append(fontURI);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
if (PR_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG)) {
|
||||
PR_LOG(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG,
|
||||
("userfonts (%p) %s", mUserFontSet.get(), message.get()));
|
||||
if (LOG_ENABLED()) {
|
||||
LOG(("userfonts (%p) %s", mUserFontSet.get(), message.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -25,20 +25,9 @@
|
|||
|
||||
using namespace mozilla;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo*
|
||||
nsFontFaceLoader::GetFontDownloaderLog()
|
||||
{
|
||||
static PRLogModuleInfo* sLog;
|
||||
if (!sLog)
|
||||
sLog = PR_NewLogModule("fontdownloader");
|
||||
return sLog;
|
||||
}
|
||||
#endif /* PR_LOGGING */
|
||||
|
||||
#define LOG(args) PR_LOG(GetFontDownloaderLog(), PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(GetFontDownloaderLog(), PR_LOG_DEBUG)
|
||||
|
||||
#define LOG(args) PR_LOG(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), \
|
||||
PR_LOG_DEBUG)
|
||||
|
||||
nsFontFaceLoader::nsFontFaceLoader(gfxUserFontEntry* aUserFontEntry,
|
||||
nsIURI* aFontURI,
|
||||
|
@ -119,7 +108,7 @@ nsFontFaceLoader::LoadTimerCallback(nsITimer* aTimer, void* aClosure)
|
|||
delay >> 1,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
updateUserFontSet = false;
|
||||
LOG(("fontdownloader (%p) 75%% done, resetting timer\n", loader));
|
||||
LOG(("userfonts (%p) 75%% done, resetting timer\n", loader));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +122,7 @@ nsFontFaceLoader::LoadTimerCallback(nsITimer* aTimer, void* aClosure)
|
|||
if (ctx) {
|
||||
loader->mFontFaceSet->IncrementGeneration();
|
||||
ctx->UserFontSetUpdated();
|
||||
LOG(("fontdownloader (%p) timeout reflow\n", loader));
|
||||
LOG(("userfonts (%p) timeout reflow\n", loader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,10 +148,10 @@ nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader,
|
|||
nsAutoCString fontURI;
|
||||
mFontURI->GetSpec(fontURI);
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
LOG(("fontdownloader (%p) download completed - font uri: (%s)\n",
|
||||
LOG(("userfonts (%p) download completed - font uri: (%s)\n",
|
||||
this, fontURI.get()));
|
||||
} else {
|
||||
LOG(("fontdownloader (%p) download failed - font uri: (%s) error: %8.8x\n",
|
||||
LOG(("userfonts (%p) download failed - font uri: (%s) error: %8.8x\n",
|
||||
this, fontURI.get(), aStatus));
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +194,7 @@ nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader,
|
|||
// Update layout for the presence of the new font. Since this is
|
||||
// asynchronous, reflows will coalesce.
|
||||
ctx->UserFontSetUpdated();
|
||||
LOG(("fontdownloader (%p) reflow\n", this));
|
||||
LOG(("userfonts (%p) reflow\n", this));
|
||||
}
|
||||
|
||||
// done with font set
|
||||
|
|
|
@ -46,10 +46,6 @@ public:
|
|||
nsIURI* aTargetURI,
|
||||
nsISupports* aContext);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
static PRLogModuleInfo* GetFontDownloaderLog();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual ~nsFontFaceLoader();
|
||||
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.mozglue.DirectBufferAllocator;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/** A Cairo image that simply saves a buffer of pixel data. */
|
||||
public class BufferedCairoImage extends CairoImage {
|
||||
private ByteBuffer mBuffer;
|
||||
private IntSize mSize;
|
||||
private int mFormat;
|
||||
|
||||
private static final String LOGTAG = "GeckoBufferedCairoImage";
|
||||
|
||||
/** Creates a buffered Cairo image from a byte buffer. */
|
||||
public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
|
||||
setBuffer(inBuffer, inWidth, inHeight, inFormat);
|
||||
}
|
||||
|
||||
/** Creates a buffered Cairo image from an Android bitmap. */
|
||||
public BufferedCairoImage(Bitmap bitmap) {
|
||||
setBitmap(bitmap);
|
||||
}
|
||||
|
||||
private synchronized void freeBuffer() {
|
||||
mBuffer = DirectBufferAllocator.free(mBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
try {
|
||||
freeBuffer();
|
||||
} catch (Exception ex) {
|
||||
Log.e(LOGTAG, "error clearing buffer: ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getBuffer() { return mBuffer; }
|
||||
@Override
|
||||
public IntSize getSize() { return mSize; }
|
||||
@Override
|
||||
public int getFormat() { return mFormat; }
|
||||
|
||||
|
||||
public void setBuffer(ByteBuffer buffer, int width, int height, int format) {
|
||||
freeBuffer();
|
||||
mBuffer = buffer;
|
||||
mSize = new IntSize(width, height);
|
||||
mFormat = format;
|
||||
}
|
||||
|
||||
public void setBitmap(Bitmap bitmap) {
|
||||
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
|
||||
mSize = new IntSize(bitmap.getWidth(), bitmap.getHeight());
|
||||
|
||||
int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat);
|
||||
mBuffer = DirectBufferAllocator.allocate(mSize.getArea() * bpp);
|
||||
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.mozglue.DirectBufferAllocator;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/** A buffered image that simply saves a buffer of pixel data. */
|
||||
public class BufferedImage {
|
||||
private ByteBuffer mBuffer;
|
||||
private IntSize mSize;
|
||||
private int mFormat;
|
||||
|
||||
private static final String LOGTAG = "GeckoBufferedImage";
|
||||
|
||||
/** Creates an empty buffered image */
|
||||
public BufferedImage() {
|
||||
setBuffer(null, 0, 0, 0);
|
||||
}
|
||||
|
||||
/** Creates a buffered image from an Android bitmap. */
|
||||
public BufferedImage(Bitmap bitmap) {
|
||||
setBitmap(bitmap);
|
||||
}
|
||||
|
||||
private synchronized void freeBuffer() {
|
||||
mBuffer = DirectBufferAllocator.free(mBuffer);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
try {
|
||||
freeBuffer();
|
||||
} catch (Exception ex) {
|
||||
Log.e(LOGTAG, "error clearing buffer: ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ByteBuffer getBuffer() { return mBuffer; }
|
||||
public IntSize getSize() { return mSize; }
|
||||
public int getFormat() { return mFormat; }
|
||||
|
||||
public static final int FORMAT_INVALID = -1;
|
||||
public static final int FORMAT_ARGB32 = 0;
|
||||
public static final int FORMAT_RGB24 = 1;
|
||||
public static final int FORMAT_A8 = 2;
|
||||
public static final int FORMAT_A1 = 3;
|
||||
public static final int FORMAT_RGB16_565 = 4;
|
||||
|
||||
public void setBuffer(ByteBuffer buffer, int width, int height, int format) {
|
||||
freeBuffer();
|
||||
mBuffer = buffer;
|
||||
mSize = new IntSize(width, height);
|
||||
mFormat = format;
|
||||
}
|
||||
|
||||
public void setBitmap(Bitmap bitmap) {
|
||||
mFormat = bitmapConfigToFormat(bitmap.getConfig());
|
||||
mSize = new IntSize(bitmap.getWidth(), bitmap.getHeight());
|
||||
|
||||
int bpp = bitsPerPixelForFormat(mFormat);
|
||||
mBuffer = DirectBufferAllocator.allocate(mSize.getArea() * bpp);
|
||||
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
||||
}
|
||||
|
||||
private static int bitsPerPixelForFormat(int format) {
|
||||
switch (format) {
|
||||
case FORMAT_A1: return 1;
|
||||
case FORMAT_A8: return 8;
|
||||
case FORMAT_RGB16_565: return 16;
|
||||
case FORMAT_RGB24: return 24;
|
||||
case FORMAT_ARGB32: return 32;
|
||||
default:
|
||||
throw new RuntimeException("Unknown Cairo format");
|
||||
}
|
||||
}
|
||||
|
||||
private static int bitmapConfigToFormat(Bitmap.Config config) {
|
||||
if (config == null)
|
||||
return FORMAT_ARGB32; /* Droid Pro fix. */
|
||||
|
||||
switch (config) {
|
||||
case ALPHA_8: return FORMAT_A8;
|
||||
case ARGB_4444: throw new RuntimeException("ARGB_444 unsupported");
|
||||
case ARGB_8888: return FORMAT_ARGB32;
|
||||
case RGB_565: return FORMAT_RGB16_565;
|
||||
default: throw new RuntimeException("Unknown Skia bitmap config");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,28 +7,28 @@ package org.mozilla.gecko.gfx;
|
|||
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
/** Information needed to render Cairo bitmaps using OpenGL ES. */
|
||||
public class CairoGLInfo {
|
||||
/** Information needed to render buffered bitmaps using OpenGL ES. */
|
||||
public class BufferedImageGLInfo {
|
||||
public final int internalFormat;
|
||||
public final int format;
|
||||
public final int type;
|
||||
|
||||
public CairoGLInfo(int cairoFormat) {
|
||||
switch (cairoFormat) {
|
||||
case CairoImage.FORMAT_ARGB32:
|
||||
public BufferedImageGLInfo(int bufferedImageFormat) {
|
||||
switch (bufferedImageFormat) {
|
||||
case BufferedImage.FORMAT_ARGB32:
|
||||
internalFormat = format = GL10.GL_RGBA; type = GL10.GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case CairoImage.FORMAT_RGB24:
|
||||
case BufferedImage.FORMAT_RGB24:
|
||||
internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case CairoImage.FORMAT_RGB16_565:
|
||||
case BufferedImage.FORMAT_RGB16_565:
|
||||
internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_SHORT_5_6_5;
|
||||
break;
|
||||
case CairoImage.FORMAT_A8:
|
||||
case CairoImage.FORMAT_A1:
|
||||
throw new RuntimeException("Cairo FORMAT_A1 and FORMAT_A8 unsupported");
|
||||
case BufferedImage.FORMAT_A8:
|
||||
case BufferedImage.FORMAT_A1:
|
||||
throw new RuntimeException("BufferedImage FORMAT_A1 and FORMAT_A8 unsupported");
|
||||
default:
|
||||
throw new RuntimeException("Unknown Cairo format");
|
||||
throw new RuntimeException("Unknown BufferedImage format");
|
||||
}
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче