diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 3800c1296f..ab8a4d7ea6 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -402,6 +402,31 @@ KeyframeEffectReadOnly::HasAnimationOfProperties( return false; } +void +KeyframeEffectReadOnly::CopyPropertiesFrom(const KeyframeEffectReadOnly& aOther) +{ + nsCSSPropertySet winningInCascadeProperties; + nsCSSPropertySet runningOnCompositorProperties; + + for (const AnimationProperty& property : mProperties) { + if (property.mWinsInCascade) { + winningInCascadeProperties.AddProperty(property.mProperty); + } + if (property.mIsRunningOnCompositor) { + runningOnCompositorProperties.AddProperty(property.mProperty); + } + } + + mProperties = aOther.mProperties; + + for (AnimationProperty& property : mProperties) { + property.mWinsInCascade = + winningInCascadeProperties.HasProperty(property.mProperty); + property.mIsRunningOnCompositor = + runningOnCompositorProperties.HasProperty(property.mProperty); + } +} + void KeyframeEffectReadOnly::ComposeStyle(RefPtr& aStyleRule, nsCSSPropertySet& aSetProperties) @@ -1354,7 +1379,6 @@ BuildSegmentsFromValueEntries(nsTArray& aEntries, MOZ_ASSERT(aEntries[i].mOffset == 0.0f); animationProperty = aResult.AppendElement(); animationProperty->mProperty = aEntries[i].mProperty; - animationProperty->mWinsInCascade = true; lastProperty = aEntries[i].mProperty; } @@ -1556,7 +1580,6 @@ BuildAnimationPropertyListFromPropertyIndexedKeyframes( animationPropertyIndexes[i] = aResult.Length(); AnimationProperty* animationProperty = aResult.AppendElement(); animationProperty->mProperty = p; - animationProperty->mWinsInCascade = true; properties.AddProperty(p); } } diff --git a/dom/animation/KeyframeEffect.h b/dom/animation/KeyframeEffect.h index 194053723e..040fc8b25b 100644 --- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -131,9 +131,10 @@ struct AnimationProperty // For CSS Animations, which are overridden by !important rules in the // cascade, we actually determine this from the CSS cascade // computations, and then use it for OMTA. + // // **NOTE**: This member is not included when comparing AnimationProperty // objects for equality. - bool mWinsInCascade = true; + bool mWinsInCascade = false; // If true, the propery is currently being animated on the compositor. // @@ -141,6 +142,9 @@ struct AnimationProperty // between calling RequestRestyle on its AnimationCollection and when the // restyle is performed, this member may temporarily become false even if // the animation remains on the layer after the restyle. + // + // **NOTE**: This member is not included when comparing AnimationProperty + // objects for equality. bool mIsRunningOnCompositor = false; InfallibleTArray mSegments; @@ -279,6 +283,10 @@ public: InfallibleTArray& Properties() { return mProperties; } + // Copies the properties from another keyframe effect whilst preserving + // the mWinsInCascade and mIsRunningOnCompositor state of matching + // properties. + void CopyPropertiesFrom(const KeyframeEffectReadOnly& aOther); // Updates |aStyleRule| with the animation values produced by this // AnimationEffect for the current time except any properties already diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 30f54b02fd..b7d7540191 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -3480,7 +3480,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess } mTextRun = mFontgrp->MakeTextRun(text, length, - mThebes, + mThebes->GetDrawTarget(), mAppUnitsPerDevPixel, flags, mMissingFonts); @@ -3493,7 +3493,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess mDoMeasureBoundingBox ? gfxFont::TIGHT_INK_EXTENTS : gfxFont::LOOSE_INK_EXTENTS, - mThebes, + mThebes->GetDrawTarget(), nullptr); // this only measures the height; the total width is gotten from the @@ -3529,7 +3529,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess mDoMeasureBoundingBox ? gfxFont::TIGHT_INK_EXTENTS : gfxFont::LOOSE_INK_EXTENTS, - mThebes, + mThebes->GetDrawTarget(), nullptr); inlineCoord += textRunMetrics.mAdvanceWidth; // old code was: diff --git a/gfx/skia/generate_mozbuild.py b/gfx/skia/generate_mozbuild.py index 9135fe56d0..054ad1e711 100755 --- a/gfx/skia/generate_mozbuild.py +++ b/gfx/skia/generate_mozbuild.py @@ -109,34 +109,34 @@ elif CONFIG['CLANG_CL']: SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1'] SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx'] +if CONFIG['GNU_CXX'] and CONFIG['CPU_ARCH'] == 'arm': + SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer'] + DEFINES['SKIA_IMPLEMENTATION'] = 1 if not CONFIG['MOZ_ENABLE_SKIA_GPU']: DEFINES['SK_SUPPORT_GPU'] = 0 -if CONFIG['GNU_CXX']: +# Suppress warnings in third-party code. +if CONFIG['GNU_CXX'] or CONFIG['CLANG_CL']: CXXFLAGS += [ '-Wno-deprecated-declarations', '-Wno-overloaded-virtual', '-Wno-sign-compare', '-Wno-unused-function', ] - if CONFIG['CLANG_CXX']: - CXXFLAGS += [ - '-Wno-implicit-fallthrough', - '-Wno-inconsistent-missing-override', - '-Wno-macro-redefined', - '-Wno-unused-private-field', - ] - # work around inline function linking bug with template arguments - SOURCES['skia/src/gpu/GrResourceCache.cpp'].flags += ['-fkeep-inline-functions'] - else: - CXXFLAGS += [ - '-Wno-logical-op', - '-Wno-maybe-uninitialized', - ] - if CONFIG['CPU_ARCH'] == 'arm': - SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer'] +if CONFIG['GNU_CXX'] and not CONFIG['CLANG_CXX'] and not CONFIG['CLANG_CL']: + CXXFLAGS += [ + '-Wno-logical-op', + '-Wno-maybe-uninitialized', + ] +if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']: + CXXFLAGS += [ + '-Wno-implicit-fallthrough', + '-Wno-inconsistent-missing-override', + '-Wno-macro-redefined', + '-Wno-unused-private-field', + ] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'android', 'gonk', 'qt'): CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] diff --git a/gfx/skia/moz.build b/gfx/skia/moz.build index b9c727ff94..14d6d83ee1 100644 --- a/gfx/skia/moz.build +++ b/gfx/skia/moz.build @@ -733,34 +733,34 @@ elif CONFIG['CLANG_CL']: SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1'] SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx'] +if CONFIG['GNU_CXX'] and CONFIG['CPU_ARCH'] == 'arm': + SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer'] + DEFINES['SKIA_IMPLEMENTATION'] = 1 if not CONFIG['MOZ_ENABLE_SKIA_GPU']: DEFINES['SK_SUPPORT_GPU'] = 0 -if CONFIG['GNU_CXX']: +# Suppress warnings in third-party code. +if CONFIG['GNU_CXX'] or CONFIG['CLANG_CL']: CXXFLAGS += [ '-Wno-deprecated-declarations', '-Wno-overloaded-virtual', '-Wno-sign-compare', '-Wno-unused-function', ] - if CONFIG['CLANG_CXX']: - CXXFLAGS += [ - '-Wno-implicit-fallthrough', - '-Wno-inconsistent-missing-override', - '-Wno-macro-redefined', - '-Wno-unused-private-field', - ] - # work around inline function linking bug with template arguments - SOURCES['skia/src/gpu/GrResourceCache.cpp'].flags += ['-fkeep-inline-functions'] - else: - CXXFLAGS += [ - '-Wno-logical-op', - '-Wno-maybe-uninitialized', - ] - if CONFIG['CPU_ARCH'] == 'arm': - SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer'] +if CONFIG['GNU_CXX'] and not CONFIG['CLANG_CXX'] and not CONFIG['CLANG_CL']: + CXXFLAGS += [ + '-Wno-logical-op', + '-Wno-maybe-uninitialized', + ] +if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']: + CXXFLAGS += [ + '-Wno-implicit-fallthrough', + '-Wno-inconsistent-missing-override', + '-Wno-macro-redefined', + '-Wno-unused-private-field', + ] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'android', 'gonk', 'qt'): CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] diff --git a/gfx/src/moz.build b/gfx/src/moz.build index 19879c4b3e..9118c1ec04 100644 --- a/gfx/src/moz.build +++ b/gfx/src/moz.build @@ -59,7 +59,6 @@ UNIFIED_SOURCES += [ 'nsFontMetrics.cpp', 'nsRect.cpp', 'nsRegion.cpp', - 'nsRenderingContext.cpp', 'nsScriptableRegion.cpp', 'nsThebesFontEnumerator.cpp', 'nsThebesGfxFactory.cpp', diff --git a/gfx/src/nsFontMetrics.cpp b/gfx/src/nsFontMetrics.cpp index 8b88a88b20..311d698ab2 100644 --- a/gfx/src/nsFontMetrics.cpp +++ b/gfx/src/nsFontMetrics.cpp @@ -27,23 +27,25 @@ namespace { class AutoTextRun { public: - AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC, + typedef mozilla::gfx::DrawTarget DrawTarget; + + AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget, const char* aString, int32_t aLength) { mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun( reinterpret_cast(aString), aLength, - aRC->ThebesContext(), + aDrawTarget, aMetrics->AppUnitsPerDevPixel(), ComputeFlags(aMetrics), nullptr); } - AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC, + AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget, const char16_t* aString, int32_t aLength) { mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun( aString, aLength, - aRC->ThebesContext(), + aDrawTarget, aMetrics->AppUnitsPerDevPixel(), ComputeFlags(aMetrics), nullptr); @@ -91,7 +93,7 @@ public: NS_ERROR("This shouldn't be called because we never enable hyphens"); return 0; } - virtual already_AddRefed GetContext() { + virtual already_AddRefed GetDrawTarget() { NS_ERROR("This shouldn't be called because we never enable hyphens"); return nullptr; } @@ -313,7 +315,7 @@ nsFontMetrics::GetMaxStringLength() nscoord nsFontMetrics::GetWidth(const char* aString, uint32_t aLength, - nsRenderingContext *aContext) + DrawTarget* aDrawTarget) { if (aLength == 0) return 0; @@ -322,14 +324,14 @@ nsFontMetrics::GetWidth(const char* aString, uint32_t aLength, return SpaceWidth(); StubPropertyProvider provider; - AutoTextRun textRun(this, aContext, aString, aLength); + AutoTextRun textRun(this, aDrawTarget, aString, aLength); return textRun.get() ? NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0; } nscoord nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength, - nsRenderingContext *aContext) + DrawTarget* aDrawTarget) { if (aLength == 0) return 0; @@ -338,7 +340,7 @@ nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength, return SpaceWidth(); StubPropertyProvider provider; - AutoTextRun textRun(this, aContext, aString, aLength); + AutoTextRun textRun(this, aDrawTarget, aString, aLength); return textRun.get() ? NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0; } @@ -353,7 +355,7 @@ nsFontMetrics::DrawString(const char *aString, uint32_t aLength, return; StubPropertyProvider provider; - AutoTextRun textRun(this, aContext, aString, aLength); + AutoTextRun textRun(this, aContext->GetDrawTarget(), aString, aLength); if (!textRun.get()) { return; } @@ -373,13 +375,13 @@ void nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength, nscoord aX, nscoord aY, nsRenderingContext *aContext, - nsRenderingContext *aTextRunConstructionContext) + DrawTarget* aTextRunConstructionDrawTarget) { if (aLength == 0) return; StubPropertyProvider provider; - AutoTextRun textRun(this, aTextRunConstructionContext, aString, aLength); + AutoTextRun textRun(this, aTextRunConstructionDrawTarget, aString, aLength); if (!textRun.get()) { return; } @@ -396,20 +398,19 @@ nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength, } static nsBoundingMetrics -GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t *aString, uint32_t aLength, - nsRenderingContext *aContext, gfxFont::BoundingBoxType aType) +GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t* aString, + uint32_t aLength, mozilla::gfx::DrawTarget* aDrawTarget, + gfxFont::BoundingBoxType aType) { if (aLength == 0) return nsBoundingMetrics(); StubPropertyProvider provider; - AutoTextRun textRun(aMetrics, aContext, aString, aLength); + AutoTextRun textRun(aMetrics, aDrawTarget, aString, aLength); nsBoundingMetrics m; if (textRun.get()) { gfxTextRun::Metrics theMetrics = - textRun->MeasureText(0, aLength, - aType, - aContext->ThebesContext(), &provider); + textRun->MeasureText(0, aLength, aType, aDrawTarget, &provider); m.leftBearing = NSToCoordFloor( theMetrics.mBoundingBox.X()); m.rightBearing = NSToCoordCeil( theMetrics.mBoundingBox.XMost()); @@ -422,16 +423,17 @@ GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t *aString, uint32_ nsBoundingMetrics nsFontMetrics::GetBoundingMetrics(const char16_t *aString, uint32_t aLength, - nsRenderingContext *aContext) + DrawTarget* aDrawTarget) { - return GetTextBoundingMetrics(this, aString, aLength, aContext, gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS); - + return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget, + gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS); } nsBoundingMetrics nsFontMetrics::GetInkBoundsForVisualOverflow(const char16_t *aString, uint32_t aLength, - nsRenderingContext *aContext) + DrawTarget* aDrawTarget) { - return GetTextBoundingMetrics(this, aString, aLength, aContext, gfxFont::LOOSE_INK_EXTENTS); + return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget, + gfxFont::LOOSE_INK_EXTENTS); } diff --git a/gfx/src/nsFontMetrics.h b/gfx/src/nsFontMetrics.h index da80023683..cecdf103f7 100644 --- a/gfx/src/nsFontMetrics.h +++ b/gfx/src/nsFontMetrics.h @@ -46,6 +46,8 @@ struct nsBoundingMetrics; class nsFontMetrics final { public: + typedef mozilla::gfx::DrawTarget DrawTarget; + nsFontMetrics(); NS_INLINE_DECL_REFCOUNTING(nsFontMetrics) @@ -187,9 +189,9 @@ public: // width in points, not twips. Callers must convert it if they // want it in another format. nscoord GetWidth(const char* aString, uint32_t aLength, - nsRenderingContext *aContext); + DrawTarget* aDrawTarget); nscoord GetWidth(const char16_t* aString, uint32_t aLength, - nsRenderingContext *aContext); + DrawTarget* aDrawTarget); // Draw a string using this font handle on the surface passed in. void DrawString(const char *aString, uint32_t aLength, @@ -198,17 +200,17 @@ public: void DrawString(const char16_t* aString, uint32_t aLength, nscoord aX, nscoord aY, nsRenderingContext *aContext, - nsRenderingContext *aTextRunConstructionContext); + DrawTarget* aTextRunConstructionDrawTarget); nsBoundingMetrics GetBoundingMetrics(const char16_t *aString, uint32_t aLength, - nsRenderingContext *aContext); + DrawTarget* aDrawTarget); // Returns the LOOSE_INK_EXTENTS bounds of the text for determing the // overflow area of the string. nsBoundingMetrics GetInkBoundsForVisualOverflow(const char16_t *aString, uint32_t aLength, - nsRenderingContext *aContext); + DrawTarget* aDrawTarget); void SetTextRunRTL(bool aIsRTL) { mTextRunRTL = aIsRTL; } bool GetTextRunRTL() const { return mTextRunRTL; } diff --git a/gfx/src/nsRenderingContext.cpp b/gfx/src/nsRenderingContext.cpp deleted file mode 100644 index 4eb9190c17..0000000000 --- a/gfx/src/nsRenderingContext.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsRenderingContext.h" - -void -nsRenderingContext::Init(gfxContext *aThebesContext) -{ - mThebes = aThebesContext; - mThebes->SetLineWidth(1.0); -} - -void -nsRenderingContext::Init(DrawTarget *aDrawTarget) -{ - Init(new gfxContext(aDrawTarget)); -} diff --git a/gfx/src/nsRenderingContext.h b/gfx/src/nsRenderingContext.h index 9ec42967ae..02bb397d65 100644 --- a/gfx/src/nsRenderingContext.h +++ b/gfx/src/nsRenderingContext.h @@ -22,8 +22,6 @@ class MOZ_STACK_CLASS nsRenderingContext final typedef mozilla::gfx::DrawTarget DrawTarget; public: - nsRenderingContext() {} - explicit nsRenderingContext(gfxContext* aThebesContext) : mThebes(aThebesContext) {} @@ -32,13 +30,6 @@ public: : mThebes(aThebesContext) {} - explicit nsRenderingContext(DrawTarget* aDrawTarget) { - Init(aDrawTarget); - } - - void Init(gfxContext* aThebesContext); - void Init(DrawTarget* aDrawTarget); - // These accessors will never return null. gfxContext *ThebesContext() { return mThebes; } DrawTarget *GetDrawTarget() { return mThebes->GetDrawTarget(); } diff --git a/gfx/tests/gtest/gfxWordCacheTest.cpp b/gfx/tests/gtest/gfxWordCacheTest.cpp index 53d2e1d957..68e5c4c6a9 100644 --- a/gfx/tests/gtest/gfxWordCacheTest.cpp +++ b/gfx/tests/gtest/gfxWordCacheTest.cpp @@ -81,23 +81,21 @@ MakeTextRun(const char16_t *aText, uint32_t aLength, gfxFontGroup *aFontGroup, return textRun.forget(); } -static already_AddRefed -MakeContext () +static already_AddRefed +MakeDrawTarget() { const int size = 200; RefPtr drawTarget = gfxPlatform::GetPlatform()-> CreateOffscreenContentDrawTarget(IntSize(size, size), SurfaceFormat::B8G8R8X8); - RefPtr ctx = new gfxContext(drawTarget); - - return ctx.forget(); + return drawTarget.forget(); } TEST(Gfx, WordCache) { gTextRuns = new FrameTextRunCache(); - RefPtr ctx = MakeContext(); + RefPtr dt = MakeDrawTarget(); { gfxFontStyle style(mozilla::gfx::FontStyle::NORMAL, 139, @@ -114,7 +112,7 @@ TEST(Gfx, WordCache) { nullptr, nullptr, 1.0); gfxTextRunFactory::Parameters params = { - ctx, nullptr, nullptr, nullptr, 0, 60 + dt, nullptr, nullptr, nullptr, 0, 60 }; uint32_t flags = gfxTextRunFactory::TEXT_IS_PERSISTENT; diff --git a/gfx/thebes/gfxCoreTextShaper.cpp b/gfx/thebes/gfxCoreTextShaper.cpp index 331c38dd8f..60500d2518 100644 --- a/gfx/thebes/gfxCoreTextShaper.cpp +++ b/gfx/thebes/gfxCoreTextShaper.cpp @@ -48,7 +48,7 @@ gfxCoreTextShaper::~gfxCoreTextShaper() } bool -gfxCoreTextShaper::ShapeText(gfxContext *aContext, +gfxCoreTextShaper::ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, diff --git a/gfx/thebes/gfxCoreTextShaper.h b/gfx/thebes/gfxCoreTextShaper.h index 21f6d4770b..9026128e17 100644 --- a/gfx/thebes/gfxCoreTextShaper.h +++ b/gfx/thebes/gfxCoreTextShaper.h @@ -18,7 +18,7 @@ public: virtual ~gfxCoreTextShaper(); - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, diff --git a/gfx/thebes/gfxDWriteFonts.cpp b/gfx/thebes/gfxDWriteFonts.cpp index 99c5779a16..22045569ac 100644 --- a/gfx/thebes/gfxDWriteFonts.cpp +++ b/gfx/thebes/gfxDWriteFonts.cpp @@ -561,17 +561,16 @@ gfxDWriteFont::GetCairoScaledFont() } gfxFont::RunMetrics -gfxDWriteFont::Measure(gfxTextRun *aTextRun, - uint32_t aStart, uint32_t aEnd, - BoundingBoxType aBoundingBoxType, - gfxContext *aRefContext, - Spacing *aSpacing, - uint16_t aOrientation) +gfxDWriteFont::Measure(gfxTextRun* aTextRun, + uint32_t aStart, uint32_t aEnd, + BoundingBoxType aBoundingBoxType, + DrawTarget* aRefDrawTarget, + Spacing* aSpacing, + uint16_t aOrientation) { gfxFont::RunMetrics metrics = - gfxFont::Measure(aTextRun, aStart, aEnd, - aBoundingBoxType, aRefContext, aSpacing, - aOrientation); + gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType, + aRefDrawTarget, aSpacing, aOrientation); // if aBoundingBoxType is LOOSE_INK_EXTENTS // and the underlying cairo font may be antialiased, diff --git a/gfx/thebes/gfxDWriteFonts.h b/gfx/thebes/gfxDWriteFonts.h index e59949ff87..beb2d99e1f 100644 --- a/gfx/thebes/gfxDWriteFonts.h +++ b/gfx/thebes/gfxDWriteFonts.h @@ -50,7 +50,7 @@ public: virtual RunMetrics Measure(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, BoundingBoxType aBoundingBoxType, - gfxContext *aContextForTightBoundingBox, + DrawTarget *aDrawTargetForTightBoundingBox, Spacing *aSpacing, uint16_t aOrientation) override; diff --git a/gfx/thebes/gfxFT2Fonts.cpp b/gfx/thebes/gfxFT2Fonts.cpp index c840e5343a..dd115548d2 100644 --- a/gfx/thebes/gfxFT2Fonts.cpp +++ b/gfx/thebes/gfxFT2Fonts.cpp @@ -43,7 +43,7 @@ */ bool -gfxFT2Font::ShapeText(gfxContext *aContext, +gfxFT2Font::ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -51,11 +51,11 @@ gfxFT2Font::ShapeText(gfxContext *aContext, bool aVertical, gfxShapedText *aShapedText) { - if (!gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript, + if (!gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript, aVertical, aShapedText)) { // harfbuzz must have failed(?!), just render raw glyphs AddRange(aText, aOffset, aLength, aShapedText); - PostShapingFixup(aContext->GetDrawTarget(), aText, aOffset, aLength, + PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical, aShapedText); } diff --git a/gfx/thebes/gfxFT2Fonts.h b/gfx/thebes/gfxFT2Fonts.h index ca00bf44e6..f92befb44f 100644 --- a/gfx/thebes/gfxFT2Fonts.h +++ b/gfx/thebes/gfxFT2Fonts.h @@ -65,7 +65,7 @@ public: // new functions #endif protected: - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index ea3fe364bf..c6b01372bd 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -48,6 +48,7 @@ #include "graphite2/Font.h" #include +#include #include using namespace mozilla; @@ -862,13 +863,13 @@ gfxFont::~gfxFont() } gfxFloat -gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID) +gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID) { - if (!SetupCairoFont(aCtx->GetDrawTarget())) { + if (!SetupCairoFont(aDrawTarget)) { return 0; } if (ProvidesGlyphWidths()) { - return GetGlyphWidth(*aCtx->GetDrawTarget(), aGID) / 65536.0; + return GetGlyphWidth(*aDrawTarget, aGID) / 65536.0; } if (mFUnitsConvFactor < 0.0f) { GetMetrics(eHorizontal); @@ -1944,13 +1945,14 @@ gfxFont::DrawEmphasisMarks(gfxTextRun* aShapedText, gfxPoint* aPt, gfxFloat& inlineCoord = aParams.isVertical ? aPt->y : aPt->x; uint32_t markLength = aParams.mark->GetLength(); - gfxFloat clusterStart = NAN; + gfxFloat clusterStart = -std::numeric_limits::infinity(); bool shouldDrawEmphasisMark = false; for (uint32_t i = 0, idx = aOffset; i < aCount; ++i, ++idx) { if (aParams.spacing) { inlineCoord += aParams.direction * aParams.spacing[i].mBefore; } - if (aShapedText->IsClusterStart(idx)) { + if (aShapedText->IsClusterStart(idx) || + clusterStart == -std::numeric_limits::infinity()) { clusterStart = inlineCoord; } if (aShapedText->CharMayHaveEmphasisMark(idx)) { @@ -1959,7 +1961,6 @@ gfxFont::DrawEmphasisMarks(gfxTextRun* aShapedText, gfxPoint* aPt, inlineCoord += aParams.direction * aShapedText->GetAdvanceForGlyph(idx); if (shouldDrawEmphasisMark && (i + 1 == aCount || aShapedText->IsClusterStart(idx + 1))) { - MOZ_ASSERT(!std::isnan(clusterStart), "Should have cluster start"); gfxFloat clusterAdvance = inlineCoord - clusterStart; // Move the coord backward to get the needed start point. gfxFloat delta = (clusterAdvance + aParams.advance) / 2; @@ -2231,7 +2232,7 @@ NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun) } bool -gfxFont::IsSpaceGlyphInvisible(gfxContext *aRefContext, gfxTextRun *aTextRun) +gfxFont::IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget, gfxTextRun* aTextRun) { if (!mFontEntry->mSpaceGlyphIsInvisibleInitialized && GetAdjustedSize() >= 1.0) { @@ -2239,7 +2240,7 @@ gfxFont::IsSpaceGlyphInvisible(gfxContext *aRefContext, gfxTextRun *aTextRun) GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit()); gfxRect glyphExtents; mFontEntry->mSpaceGlyphIsInvisible = - extents->GetTightGlyphExtentsAppUnits(this, aRefContext, + extents->GetTightGlyphExtentsAppUnits(this, aRefDrawTarget, GetSpaceGlyph(), &glyphExtents) && glyphExtents.IsEmpty(); mFontEntry->mSpaceGlyphIsInvisibleInitialized = true; @@ -2251,7 +2252,7 @@ gfxFont::RunMetrics gfxFont::Measure(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, BoundingBoxType aBoundingBoxType, - gfxContext *aRefContext, + DrawTarget* aRefDrawTarget, Spacing *aSpacing, uint16_t aOrientation) { @@ -2269,7 +2270,7 @@ gfxFont::Measure(gfxTextRun *aTextRun, if (mNonAAFont) { return mNonAAFont->Measure(aTextRun, aStart, aEnd, TIGHT_HINTED_OUTLINE_EXTENTS, - aRefContext, aSpacing, aOrientation); + aRefDrawTarget, aSpacing, aOrientation); } } @@ -2332,7 +2333,7 @@ gfxFont::Measure(gfxTextRun *aTextRun, double advance = glyphData->GetSimpleAdvance(); uint32_t glyphIndex = glyphData->GetSimpleGlyph(); if (glyphIndex != spaceGlyph || - !IsSpaceGlyphInvisible(aRefContext, aTextRun)) { + !IsSpaceGlyphInvisible(aRefDrawTarget, aTextRun)) { allGlyphsInvisible = false; } // Only get the real glyph horizontal extent if we were asked @@ -2347,7 +2348,7 @@ gfxFont::Measure(gfxTextRun *aTextRun, } else { gfxRect glyphRect; if (!extents->GetTightGlyphExtentsAppUnits(this, - aRefContext, glyphIndex, &glyphRect)) { + aRefDrawTarget, glyphIndex, &glyphRect)) { glyphRect = gfxRect(0, metrics.mBoundingBox.Y(), advance, metrics.mBoundingBox.Height()); } @@ -2379,7 +2380,7 @@ gfxFont::Measure(gfxTextRun *aTextRun, gfxRect glyphRect; if (glyphData->IsMissing() || !extents || !extents->GetTightGlyphExtentsAppUnits(this, - aRefContext, glyphIndex, &glyphRect)) { + aRefDrawTarget, glyphIndex, &glyphRect)) { // We might have failed to get glyph extents due to // OOM or something glyphRect = gfxRect(0, -metrics.mAscent, @@ -2499,7 +2500,7 @@ IsBoundarySpace(char16_t aChar, char16_t aNextChar) template gfxShapedWord* -gfxFont::GetShapedWord(gfxContext *aContext, +gfxFont::GetShapedWord(DrawTarget *aDrawTarget, const T *aText, uint32_t aLength, uint32_t aHash, @@ -2564,7 +2565,7 @@ gfxFont::GetShapedWord(gfxContext *aContext, } DebugOnly ok = - ShapeText(aContext, aText, 0, aLength, aRunScript, aVertical, sw); + ShapeText(aDrawTarget, aText, 0, aLength, aRunScript, aVertical, sw); NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text"); @@ -2572,7 +2573,7 @@ gfxFont::GetShapedWord(gfxContext *aContext, } template gfxShapedWord* -gfxFont::GetShapedWord(gfxContext *aContext, +gfxFont::GetShapedWord(DrawTarget *aDrawTarget, const uint8_t *aText, uint32_t aLength, uint32_t aHash, @@ -2620,7 +2621,7 @@ gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const } bool -gfxFont::ShapeText(gfxContext *aContext, +gfxFont::ShapeText(DrawTarget *aDrawTarget, const uint8_t *aText, uint32_t aOffset, uint32_t aLength, @@ -2634,12 +2635,12 @@ gfxFont::ShapeText(gfxContext *aContext, if (utf16.Length() != aLength) { return false; } - return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength, + return ShapeText(aDrawTarget, utf16.BeginReading(), aOffset, aLength, aScript, aVertical, aShapedText); } bool -gfxFont::ShapeText(gfxContext *aContext, +gfxFont::ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -2656,7 +2657,7 @@ gfxFont::ShapeText(gfxContext *aContext, if (!mGraphiteShaper) { mGraphiteShaper = new gfxGraphiteShaper(this); } - ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength, + ok = mGraphiteShaper->ShapeText(aDrawTarget, aText, aOffset, aLength, aScript, aVertical, aShapedText); } } @@ -2665,13 +2666,13 @@ gfxFont::ShapeText(gfxContext *aContext, if (!mHarfBuzzShaper) { mHarfBuzzShaper = new gfxHarfBuzzShaper(this); } - ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength, + ok = mHarfBuzzShaper->ShapeText(aDrawTarget, aText, aOffset, aLength, aScript, aVertical, aShapedText); } NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text"); - PostShapingFixup(aContext->GetDrawTarget(), aText, aOffset, aLength, + PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical, aShapedText); return ok; @@ -2704,7 +2705,7 @@ gfxFont::PostShapingFixup(DrawTarget* aDrawTarget, template bool -gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext, +gfxFont::ShapeFragmentWithoutWordCache(DrawTarget *aDrawTarget, const T *aText, uint32_t aOffset, uint32_t aLength, @@ -2745,7 +2746,7 @@ gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext, } } - ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aVertical, + ok = ShapeText(aDrawTarget, aText, aOffset, fragLen, aScript, aVertical, aTextRun); aText += fragLen; @@ -2769,7 +2770,7 @@ IsInvalidControlChar(uint32_t aCh) template bool -gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext, +gfxFont::ShapeTextWithoutWordCache(DrawTarget *aDrawTarget, const T *aText, uint32_t aOffset, uint32_t aLength, @@ -2791,7 +2792,7 @@ gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext, } if (length > 0) { - ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart, + ok = ShapeFragmentWithoutWordCache(aDrawTarget, aText + fragStart, aOffset + fragStart, length, aScript, aVertical, aTextRun); } @@ -2810,7 +2811,7 @@ gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext, } else if (IsInvalidControlChar(ch) && !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) { if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) { - ShapeFragmentWithoutWordCache(aContext, aText + i, + ShapeFragmentWithoutWordCache(aDrawTarget, aText + i, aOffset + i, 1, aScript, aVertical, aTextRun); } else { @@ -2850,7 +2851,7 @@ inline static bool HasSpaces(const char16_t *aString, uint32_t aLen) template bool -gfxFont::SplitAndInitTextRun(gfxContext *aContext, +gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget, gfxTextRun *aTextRun, const T *aString, // text for this font run uint32_t aRunStart, // position in the textrun @@ -2890,7 +2891,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, if (aRunLength > wordCacheCharLimit || HasSpaces(aString, aRunLength)) { TEXT_PERF_INCR(tp, wordCacheSpaceRules); - return ShapeTextWithoutWordCache(aContext, aString, + return ShapeTextWithoutWordCache(aDrawTarget, aString, aRunStart, aRunLength, aRunScript, aVertical, aTextRun); @@ -2940,7 +2941,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, // font's word cache but just shape directly into the textrun. if (length > wordCacheCharLimit) { TEXT_PERF_INCR(tp, wordCacheLong); - bool ok = ShapeFragmentWithoutWordCache(aContext, + bool ok = ShapeFragmentWithoutWordCache(aDrawTarget, aString + wordStart, aRunStart + wordStart, length, @@ -2960,7 +2961,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT; } } - gfxShapedWord *sw = GetShapedWord(aContext, + gfxShapedWord* sw = GetShapedWord(aDrawTarget, aString + wordStart, length, hash, aRunScript, aVertical, appUnitsPerDevUnit, @@ -2991,7 +2992,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, DebugOnly boundary16 = boundary; NS_ASSERTION(boundary16 < 256, "unexpected boundary!"); gfxShapedWord *sw = - GetShapedWord(aContext, &boundary, 1, + GetShapedWord(aDrawTarget, &boundary, 1, gfxShapedWord::HashMix(0, boundary), aRunScript, aVertical, appUnitsPerDevUnit, flags | gfxTextRunFactory::TEXT_IS_8BIT, tp); @@ -3024,7 +3025,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, } else if (IsInvalidControlChar(ch) && !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) { if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) { - ShapeFragmentWithoutWordCache(aContext, aString + i, + ShapeFragmentWithoutWordCache(aDrawTarget, aString + i, aRunStart + i, 1, aRunScript, aVertical, aTextRun); } else { @@ -3042,7 +3043,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, // Explicit instantiations of SplitAndInitTextRun, to avoid libxul link failure template bool -gfxFont::SplitAndInitTextRun(gfxContext *aContext, +gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget, gfxTextRun *aTextRun, const uint8_t *aString, uint32_t aRunStart, @@ -3050,7 +3051,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, int32_t aRunScript, bool aVertical); template bool -gfxFont::SplitAndInitTextRun(gfxContext *aContext, +gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget, gfxTextRun *aTextRun, const char16_t *aString, uint32_t aRunStart, @@ -3060,7 +3061,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, template<> bool -gfxFont::InitFakeSmallCapsRun(gfxContext *aContext, +gfxFont::InitFakeSmallCapsRun(DrawTarget *aDrawTarget, gfxTextRun *aTextRun, const char16_t *aText, uint32_t aOffset, @@ -3141,7 +3142,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext, // just use the current font and the existing string aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true, aOrientation); - if (!f->SplitAndInitTextRun(aContext, aTextRun, + if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun, aText + runStart, aOffset + runStart, runLength, aScript, vertical)) { @@ -3176,7 +3177,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext, // into the destination textrun but have to handle the // mismatch of character positions. gfxTextRunFactory::Parameters params = { - aContext, nullptr, nullptr, nullptr, 0, + aDrawTarget, nullptr, nullptr, nullptr, 0, aTextRun->GetAppUnitsPerDevUnit() }; nsAutoPtr tempRun; @@ -3184,7 +3185,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext, gfxTextRun::Create(¶ms, convertedString.Length(), aTextRun->GetFontGroup(), 0); tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation); - if (!f->SplitAndInitTextRun(aContext, tempRun, + if (!f->SplitAndInitTextRun(aDrawTarget, tempRun, convertedString.BeginReading(), 0, convertedString.Length(), aScript, vertical)) { @@ -3203,7 +3204,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext, } else { aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true, aOrientation); - if (!f->SplitAndInitTextRun(aContext, aTextRun, + if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun, convertedString.BeginReading(), aOffset + runStart, runLength, aScript, vertical)) { @@ -3227,7 +3228,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext, template<> bool -gfxFont::InitFakeSmallCapsRun(gfxContext *aContext, +gfxFont::InitFakeSmallCapsRun(DrawTarget *aDrawTarget, gfxTextRun *aTextRun, const uint8_t *aText, uint32_t aOffset, @@ -3240,7 +3241,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext, { NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast(aText), aLength); - return InitFakeSmallCapsRun(aContext, aTextRun, static_cast(unicodeString.get()), + return InitFakeSmallCapsRun(aDrawTarget, aTextRun, static_cast(unicodeString.get()), aOffset, aLength, aMatchType, aOrientation, aScript, aSyntheticLower, aSyntheticUpper); } diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 26f956d55d..d48b24fff3 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -455,6 +455,8 @@ class gfxTextRunFactory { NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory) public: + typedef mozilla::gfx::DrawTarget DrawTarget; + // Flags in the mask 0xFFFF0000 are reserved for textrun clients // Flags in the mask 0x0000F000 are reserved for per-platform fonts // Flags in the mask 0x00000FFF are set by the textrun creator. @@ -573,8 +575,8 @@ public: * This record contains all the parameters needed to initialize a textrun. */ struct Parameters { - // A reference context suggesting where the textrun will be rendered - gfxContext *mContext; + // Shape text params suggesting where the textrun will be rendered + DrawTarget *mDrawTarget; // Pointer to arbitrary user data (which should outlive the textrun) void *mUserData; // A description of which characters have been stripped from the original @@ -627,7 +629,7 @@ public: // Shape a piece of text and store the resulting glyph data into // aShapedText. Parameters aOffset/aLength indicate the range of // aShapedText to be updated; aLength is also the length of aText. - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -1487,7 +1489,7 @@ public: return 0; } // Return the horizontal advance of a glyph. - gfxFloat GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID); + gfxFloat GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID); // Return Azure GlyphRenderingOptions for drawing this font. virtual already_AddRefed @@ -1627,7 +1629,7 @@ public: * the advance width for the character run,y=-(font ascent), and height= * font ascent + font descent). Otherwise, we must return as tight as possible * an approximation to the area actually painted by glyphs. - * @param aContextForTightBoundingBox when aTight is true, this must + * @param aDrawTargetForTightBoundingBox when aTight is true, this must * be non-null. * @param aSpacing spacing to insert before and after glyphs. The bounding box * need not include the spacing itself, but the spacing affects the glyph @@ -1644,7 +1646,7 @@ public: virtual RunMetrics Measure(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, BoundingBoxType aBoundingBoxType, - gfxContext *aContextForTightBoundingBox, + DrawTarget* aDrawTargetForTightBoundingBox, Spacing *aSpacing, uint16_t aOrientation); /** * Line breaks have been changed at the beginning and/or end of a substring @@ -1710,7 +1712,7 @@ public: } template - bool InitFakeSmallCapsRun(gfxContext *aContext, + bool InitFakeSmallCapsRun(DrawTarget *aDrawTarget, gfxTextRun *aTextRun, const T *aText, uint32_t aOffset, @@ -1725,7 +1727,7 @@ public: // limiting the length of text passed by processing the run in multiple // segments if necessary template - bool SplitAndInitTextRun(gfxContext *aContext, + bool SplitAndInitTextRun(DrawTarget *aDrawTarget, gfxTextRun *aTextRun, const T *aString, uint32_t aRunStart, @@ -1736,7 +1738,7 @@ public: // Get a ShapedWord representing the given text (either 8- or 16-bit) // for use in setting up a gfxTextRun. template - gfxShapedWord* GetShapedWord(gfxContext *aContext, + gfxShapedWord* GetShapedWord(DrawTarget *aDrawTarget, const T *aText, uint32_t aLength, uint32_t aHash, @@ -1906,7 +1908,8 @@ protected: return -1; } - bool IsSpaceGlyphInvisible(gfxContext *aRefContext, gfxTextRun *aTextRun); + bool IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget, + gfxTextRun* aTextRun); void AddGlyphChangeObserver(GlyphChangeObserver *aObserver); void RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver); @@ -1918,7 +1921,7 @@ protected: bool SpaceMayParticipateInShaping(int32_t aRunScript); // For 8-bit text, expand to 16-bit and then call the following method. - bool ShapeText(gfxContext *aContext, + bool ShapeText(DrawTarget *aContext, const uint8_t *aText, uint32_t aOffset, // dest offset in gfxShapedText uint32_t aLength, @@ -1928,7 +1931,7 @@ protected: // Call the appropriate shaper to generate glyphs for aText and store // them into aShapedText. - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(DrawTarget *aContext, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -1954,7 +1957,7 @@ protected: // not handled via normal shaping, but does not otherwise divide up the // text. template - bool ShapeTextWithoutWordCache(gfxContext *aContext, + bool ShapeTextWithoutWordCache(DrawTarget *aDrawTarget, const T *aText, uint32_t aOffset, uint32_t aLength, @@ -1968,7 +1971,7 @@ protected: // that will ensure we don't pass excessively long runs to the various // platform shapers. template - bool ShapeFragmentWithoutWordCache(gfxContext *aContext, + bool ShapeFragmentWithoutWordCache(DrawTarget *aDrawTarget, const T *aText, uint32_t aOffset, uint32_t aLength, diff --git a/gfx/thebes/gfxGDIFont.cpp b/gfx/thebes/gfxGDIFont.cpp index 474ca8e4d5..82072b3065 100644 --- a/gfx/thebes/gfxGDIFont.cpp +++ b/gfx/thebes/gfxGDIFont.cpp @@ -78,7 +78,7 @@ gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption) } bool -gfxGDIFont::ShapeText(gfxContext *aContext, +gfxGDIFont::ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -98,11 +98,11 @@ gfxGDIFont::ShapeText(gfxContext *aContext, // creating a "toy" font internally (see bug 544617). // We must check that this succeeded, otherwise we risk cairo creating the // wrong kind of font internally as a fallback (bug 744480). - if (!SetupCairoFont(aContext->GetDrawTarget())) { + if (!SetupCairoFont(aDrawTarget)) { return false; } - return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript, + return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript, aVertical, aShapedText); } @@ -144,14 +144,13 @@ gfxFont::RunMetrics gfxGDIFont::Measure(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, BoundingBoxType aBoundingBoxType, - gfxContext *aRefContext, + DrawTarget *aRefDrawTarget, Spacing *aSpacing, uint16_t aOrientation) { gfxFont::RunMetrics metrics = - gfxFont::Measure(aTextRun, aStart, aEnd, - aBoundingBoxType, aRefContext, aSpacing, - aOrientation); + gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType, + aRefDrawTarget, aSpacing, aOrientation); // if aBoundingBoxType is LOOSE_INK_EXTENTS // and the underlying cairo font may be antialiased, diff --git a/gfx/thebes/gfxGDIFont.h b/gfx/thebes/gfxGDIFont.h index 3aacce25f2..50190d6887 100644 --- a/gfx/thebes/gfxGDIFont.h +++ b/gfx/thebes/gfxGDIFont.h @@ -48,7 +48,7 @@ public: virtual RunMetrics Measure(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, BoundingBoxType aBoundingBoxType, - gfxContext *aContextForTightBoundingBox, + DrawTarget *aDrawTargetForTightBoundingBox, Spacing *aSpacing, uint16_t aOrientation) override; @@ -82,7 +82,7 @@ protected: virtual const Metrics& GetHorizontalMetrics() override; /* override to ensure the cairo font is set up properly */ - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, diff --git a/gfx/thebes/gfxGlyphExtents.cpp b/gfx/thebes/gfxGlyphExtents.cpp index 3ea3f1715e..cb8f5838b6 100644 --- a/gfx/thebes/gfxGlyphExtents.cpp +++ b/gfx/thebes/gfxGlyphExtents.cpp @@ -35,21 +35,24 @@ gfxGlyphExtents::~gfxGlyphExtents() } bool -gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont, - gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents) +gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont* aFont, + DrawTarget* aDrawTarget, uint32_t aGlyphID, gfxRect* aExtents) { HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID); if (!entry) { - if (!aContext) { - NS_WARNING("Could not get glyph extents (no aContext)"); + // Some functions higher up in the call chain deliberately pass in a + // nullptr DrawTarget, e.g. GetBaselinePosition() passes nullptr to + // gfxTextRun::MeasureText() and that nullptr reaches here. + if (!aDrawTarget) { + NS_WARNING("Could not get glyph extents (no aDrawTarget)"); return false; } - if (aFont->SetupCairoFont(aContext->GetDrawTarget())) { + if (aFont->SetupCairoFont(aDrawTarget)) { #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS ++gGlyphExtentsSetupLazyTight; #endif - aFont->SetupGlyphExtents(aContext->GetDrawTarget(), aGlyphID, true, this); + aFont->SetupGlyphExtents(aDrawTarget, aGlyphID, true, this); entry = mTightGlyphExtents.GetEntry(aGlyphID); } if (!entry) { diff --git a/gfx/thebes/gfxGlyphExtents.h b/gfx/thebes/gfxGlyphExtents.h index 14cd57fa59..c6fac9f533 100644 --- a/gfx/thebes/gfxGlyphExtents.h +++ b/gfx/thebes/gfxGlyphExtents.h @@ -62,8 +62,8 @@ public: // Get glyph extents; a rectangle relative to the left baseline origin // Returns true on success. Can fail on OOM or when aContext is null // and extents were not (successfully) prefetched. - bool GetTightGlyphExtentsAppUnits(gfxFont *aFont, - gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents); + bool GetTightGlyphExtentsAppUnits(gfxFont* aFont, + DrawTarget* aDrawTarget, uint32_t aGlyphID, gfxRect* aExtents); void SetContainedGlyphWidthAppUnits(uint32_t aGlyphID, uint16_t aWidth) { mContainedGlyphWidths.Set(aGlyphID, aWidth); diff --git a/gfx/thebes/gfxGraphiteShaper.cpp b/gfx/thebes/gfxGraphiteShaper.cpp index 553143b609..97ac0f4d3e 100644 --- a/gfx/thebes/gfxGraphiteShaper.cpp +++ b/gfx/thebes/gfxGraphiteShaper.cpp @@ -82,7 +82,7 @@ AddFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg) } bool -gfxGraphiteShaper::ShapeText(gfxContext *aContext, +gfxGraphiteShaper::ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -91,11 +91,11 @@ gfxGraphiteShaper::ShapeText(gfxContext *aContext, gfxShapedText *aShapedText) { // some font back-ends require this in order to get proper hinted metrics - if (!mFont->SetupCairoFont(aContext->GetDrawTarget())) { + if (!mFont->SetupCairoFont(aDrawTarget)) { return false; } - mCallbackData.mDrawTarget = aContext->GetDrawTarget(); + mCallbackData.mDrawTarget = aDrawTarget; const gfxFontStyle *style = mFont->GetStyle(); @@ -173,7 +173,7 @@ gfxGraphiteShaper::ShapeText(gfxContext *aContext, return false; } - nsresult rv = SetGlyphsFromSegment(aContext, aShapedText, aOffset, aLength, + nsresult rv = SetGlyphsFromSegment(aDrawTarget, aShapedText, aOffset, aLength, aText, seg); gr_seg_destroy(seg); @@ -193,7 +193,7 @@ struct Cluster { }; nsresult -gfxGraphiteShaper::SetGlyphsFromSegment(gfxContext *aContext, +gfxGraphiteShaper::SetGlyphsFromSegment(DrawTarget *aDrawTarget, gfxShapedText *aShapedText, uint32_t aOffset, uint32_t aLength, @@ -269,7 +269,7 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxContext *aContext, } bool roundX, roundY; - GetRoundOffsetsToPixels(aContext->GetDrawTarget(), &roundX, &roundY); + GetRoundOffsetsToPixels(aDrawTarget, &roundX, &roundY); gfxShapedText::CompressedGlyph *charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset; diff --git a/gfx/thebes/gfxGraphiteShaper.h b/gfx/thebes/gfxGraphiteShaper.h index 1075e9d1b8..b07fcfc84a 100644 --- a/gfx/thebes/gfxGraphiteShaper.h +++ b/gfx/thebes/gfxGraphiteShaper.h @@ -19,7 +19,7 @@ public: explicit gfxGraphiteShaper(gfxFont *aFont); virtual ~gfxGraphiteShaper(); - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -30,7 +30,7 @@ public: static void Shutdown(); protected: - nsresult SetGlyphsFromSegment(gfxContext *aContext, + nsresult SetGlyphsFromSegment(DrawTarget *aDrawTarget, gfxShapedText *aShapedText, uint32_t aOffset, uint32_t aLength, diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index 16bbf5485f..dd33adfd05 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -1462,7 +1462,7 @@ gfxHarfBuzzShaper::InitializeVertical() } bool -gfxHarfBuzzShaper::ShapeText(gfxContext *aContext, +gfxHarfBuzzShaper::ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -1471,11 +1471,11 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext, gfxShapedText *aShapedText) { // some font back-ends require this in order to get proper hinted metrics - if (!mFont->SetupCairoFont(aContext->GetDrawTarget())) { + if (!mFont->SetupCairoFont(aDrawTarget)) { return false; } - mCallbackData.mDrawTarget = aContext->GetDrawTarget(); + mCallbackData.mDrawTarget = aDrawTarget; mUseVerticalPresentationForms = false; if (!Initialize()) { @@ -1565,7 +1565,7 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext, hb_buffer_reverse(buffer); } - nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength, + nsresult rv = SetGlyphsFromRun(aDrawTarget, aShapedText, aOffset, aLength, aText, buffer, aVertical); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord"); @@ -1579,7 +1579,7 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext, // for charToGlyphArray nsresult -gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext, +gfxHarfBuzzShaper::SetGlyphsFromRun(DrawTarget *aDrawTarget, gfxShapedText *aShapedText, uint32_t aOffset, uint32_t aLength, @@ -1618,11 +1618,10 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext, int32_t charStart = 0; // and this char index within the range of the run bool roundI, roundB; - DrawTarget* drawTarget = aContext->GetDrawTarget(); if (aVertical) { - GetRoundOffsetsToPixels(drawTarget, &roundB, &roundI); + GetRoundOffsetsToPixels(aDrawTarget, &roundB, &roundI); } else { - GetRoundOffsetsToPixels(drawTarget, &roundI, &roundB); + GetRoundOffsetsToPixels(aDrawTarget, &roundI, &roundB); } int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit(); diff --git a/gfx/thebes/gfxHarfBuzzShaper.h b/gfx/thebes/gfxHarfBuzzShaper.h index 1e8417ba4b..122dec4a34 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.h +++ b/gfx/thebes/gfxHarfBuzzShaper.h @@ -27,7 +27,7 @@ public: }; bool Initialize(); - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -98,7 +98,7 @@ public: } protected: - nsresult SetGlyphsFromRun(gfxContext *aContext, + nsresult SetGlyphsFromRun(DrawTarget *aDrawTarget, gfxShapedText *aShapedText, uint32_t aOffset, uint32_t aLength, diff --git a/gfx/thebes/gfxMacFont.cpp b/gfx/thebes/gfxMacFont.cpp index a10671c0f7..29b4dd092b 100644 --- a/gfx/thebes/gfxMacFont.cpp +++ b/gfx/thebes/gfxMacFont.cpp @@ -120,7 +120,7 @@ gfxMacFont::~gfxMacFont() } bool -gfxMacFont::ShapeText(gfxContext *aContext, +gfxMacFont::ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, @@ -140,15 +140,15 @@ gfxMacFont::ShapeText(gfxContext *aContext, if (!mCoreTextShaper) { mCoreTextShaper = new gfxCoreTextShaper(this); } - if (mCoreTextShaper->ShapeText(aContext, aText, aOffset, aLength, + if (mCoreTextShaper->ShapeText(aDrawTarget, aText, aOffset, aLength, aScript, aVertical, aShapedText)) { - PostShapingFixup(aContext->GetDrawTarget(), aText, aOffset, + PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical, aShapedText); return true; } } - return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript, + return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript, aVertical, aShapedText); } @@ -168,13 +168,13 @@ gfxFont::RunMetrics gfxMacFont::Measure(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, BoundingBoxType aBoundingBoxType, - gfxContext *aRefContext, + DrawTarget *aRefDrawTarget, Spacing *aSpacing, uint16_t aOrientation) { gfxFont::RunMetrics metrics = gfxFont::Measure(aTextRun, aStart, aEnd, - aBoundingBoxType, aRefContext, aSpacing, + aBoundingBoxType, aRefDrawTarget, aSpacing, aOrientation); // if aBoundingBoxType is not TIGHT_HINTED_OUTLINE_EXTENTS then we need to add diff --git a/gfx/thebes/gfxMacFont.h b/gfx/thebes/gfxMacFont.h index 6cdaa18e61..142403780e 100644 --- a/gfx/thebes/gfxMacFont.h +++ b/gfx/thebes/gfxMacFont.h @@ -34,7 +34,7 @@ public: virtual RunMetrics Measure(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, BoundingBoxType aBoundingBoxType, - gfxContext *aContextForTightBoundingBox, + DrawTarget *aDrawTargetForTightBoundingBox, Spacing *aSpacing, uint16_t aOrientation) override; @@ -67,7 +67,7 @@ protected: } // override to prefer CoreText shaping with fonts that depend on AAT - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(DrawTarget *aDrawTarget, const char16_t *aText, uint32_t aOffset, uint32_t aLength, diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index eb745f9463..12e603e117 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -575,7 +575,8 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode, if (aAdvanceWidth) { gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS, - aContext, aProvider); + aContext->GetDrawTarget(), + aProvider); *aAdvanceWidth = metrics.mAdvanceWidth * direction; } @@ -596,7 +597,8 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode, // measure text, use the bounding box gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS, - aContext, aProvider); + aContext->GetDrawTarget(), + aProvider); metrics.mBoundingBox.MoveBy(aPt); syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, GetAppUnitsPerDevUnit()); @@ -713,7 +715,7 @@ void gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, gfxFont::BoundingBoxType aBoundingBoxType, - gfxContext *aRefContext, + DrawTarget* aRefDrawTarget, PropertyProvider *aProvider, uint32_t aSpacingStart, uint32_t aSpacingEnd, uint16_t aOrientation, @@ -722,7 +724,8 @@ gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont, nsAutoTArray spacingBuffer; bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider, aSpacingStart, aSpacingEnd, &spacingBuffer); - Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext, + Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, + aRefDrawTarget, haveSpacing ? spacingBuffer.Elements() : nullptr, aOrientation); aMetrics->CombineWith(metrics, IsRightToLeft()); @@ -731,7 +734,7 @@ gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont, void gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, - gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext, + gfxFont::BoundingBoxType aBoundingBoxType, DrawTarget* aRefDrawTarget, PropertyProvider *aProvider, uint16_t aOrientation, Metrics *aMetrics) { if (aStart >= aEnd) @@ -744,7 +747,7 @@ gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont, // First measure the complete ligature Metrics metrics; AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd, - aBoundingBoxType, aRefContext, + aBoundingBoxType, aRefDrawTarget, aProvider, aStart, aEnd, aOrientation, &metrics); // Clip the bounding box to the ligature part @@ -769,7 +772,7 @@ gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont, gfxTextRun::Metrics gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength, gfxFont::BoundingBoxType aBoundingBoxType, - gfxContext *aRefContext, + DrawTarget* aRefDrawTarget, PropertyProvider *aProvider) { NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range"); @@ -785,7 +788,7 @@ gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength, ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd); AccumulatePartialLigatureMetrics(font, start, ligatureRunStart, - aBoundingBoxType, aRefContext, aProvider, + aBoundingBoxType, aRefDrawTarget, aProvider, iter.GetGlyphRun()->mOrientation, &accumulatedMetrics); // XXX This sucks. We have to get glyph extents just so we can detect @@ -795,11 +798,11 @@ gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength, // advance widths. AccumulateMetricsForRun(font, ligatureRunStart, ligatureRunEnd, aBoundingBoxType, - aRefContext, aProvider, ligatureRunStart, ligatureRunEnd, + aRefDrawTarget, aProvider, ligatureRunStart, ligatureRunEnd, iter.GetGlyphRun()->mOrientation, &accumulatedMetrics); AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end, - aBoundingBoxType, aRefContext, aProvider, + aBoundingBoxType, aRefDrawTarget, aProvider, iter.GetGlyphRun()->mOrientation, &accumulatedMetrics); } @@ -816,7 +819,7 @@ gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength, gfxFloat *aTrimWhitespace, Metrics *aMetrics, gfxFont::BoundingBoxType aBoundingBoxType, - gfxContext *aRefContext, + DrawTarget* aRefDrawTarget, bool *aUsedHyphenation, uint32_t *aLastBreak, bool aCanWordWrap, @@ -962,13 +965,13 @@ gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength, } if (aMetrics) { - *aMetrics = MeasureText(aStart, charsFit, - aBoundingBoxType, aRefContext, aProvider); + *aMetrics = MeasureText(aStart, charsFit, aBoundingBoxType, + aRefDrawTarget, aProvider); if (trimmableChars) { Metrics trimMetrics = MeasureText(aStart + charsFit - trimmableChars, trimmableChars, aBoundingBoxType, - aRefContext, aProvider); + aRefDrawTarget, aProvider); aMetrics->mAdvanceWidth -= trimMetrics.mAdvanceWidth; } } @@ -1032,8 +1035,7 @@ gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength, bool gfxTextRun::SetLineBreaks(uint32_t aStart, uint32_t aLength, bool aLineBreakBefore, bool aLineBreakAfter, - gfxFloat *aAdvanceWidthDelta, - gfxContext *aRefContext) + gfxFloat *aAdvanceWidthDelta) { // Do nothing because our shaping does not currently take linebreaks into // account. There is no change in advance width. @@ -1317,7 +1319,7 @@ gfxTextRun::ClearGlyphsAndCharacters() } void -gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, +gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget, uint32_t aCharIndex, uint16_t aOrientation) { if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) { @@ -1332,7 +1334,7 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, aOrientation; bool vertical = (GetFlags() & gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) != 0; - gfxShapedWord *sw = aFont->GetShapedWord(aContext, + gfxShapedWord* sw = aFont->GetShapedWord(aDrawTarget, &space, 1, gfxShapedWord::HashMix(0, ' '), MOZ_SCRIPT_LATIN, @@ -1378,7 +1380,7 @@ gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex, } void -gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) +gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget) { bool needsGlyphExtents = NeedsGlyphExtents(this); if (!needsGlyphExtents && !mDetailedGlyphs) @@ -1410,7 +1412,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) uint32_t glyphIndex = glyphData->GetSimpleGlyph(); if (!extents->IsGlyphKnown(glyphIndex)) { if (!fontIsSetup) { - if (!font->SetupCairoFont(aRefContext->GetDrawTarget())) { + if (!font->SetupCairoFont(aRefDrawTarget)) { NS_WARNING("failed to set up font for glyph extents"); break; } @@ -1419,7 +1421,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS ++gGlyphExtentsSetupEagerSimple; #endif - font->SetupGlyphExtents(aRefContext->GetDrawTarget(), + font->SetupGlyphExtents(aRefDrawTarget, glyphIndex, false, extents); } } @@ -1436,7 +1438,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) uint32_t glyphIndex = details->mGlyphID; if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) { if (!fontIsSetup) { - if (!font->SetupCairoFont(aRefContext->GetDrawTarget())) { + if (!font->SetupCairoFont(aRefDrawTarget)) { NS_WARNING("failed to set up font for glyph extents"); break; } @@ -1445,7 +1447,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS ++gGlyphExtentsSetupEagerTight; #endif - font->SetupGlyphExtents(aRefContext->GetDrawTarget(), + font->SetupGlyphExtents(aRefDrawTarget, glyphIndex, true, extents); } } @@ -1986,7 +1988,7 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags) if (font->GetSpaceGlyph()) { // Normally, the font has a cached space glyph, so we can avoid // the cost of calling FindFontForChar. - textRun->SetSpaceGlyph(font, aParams->mContext, 0, orientation); + textRun->SetSpaceGlyph(font, aParams->mDrawTarget, 0, orientation); } else { // In case the primary font doesn't have (bug 970891), // find one that does. @@ -1995,7 +1997,7 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags) FindFontForChar(' ', 0, 0, MOZ_SCRIPT_LATIN, nullptr, &matchType); if (spaceFont) { - textRun->SetSpaceGlyph(spaceFont, aParams->mContext, 0, + textRun->SetSpaceGlyph(spaceFont, aParams->mDrawTarget, 0, orientation); } } @@ -2027,7 +2029,8 @@ gfxFontGroup::MakeBlankTextRun(uint32_t aLength, } gfxTextRun * -gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit) +gfxFontGroup::MakeHyphenTextRun(DrawTarget* aDrawTarget, + uint32_t aAppUnitsPerDevUnit) { // only use U+2010 if it is supported by the first font in the group; // it's better to use ASCII '-' from the primary font than to fall back to @@ -2035,12 +2038,12 @@ gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit) static const char16_t hyphen = 0x2010; gfxFont *font = GetFirstValidFont(uint32_t(hyphen)); if (font->HasCharacter(hyphen)) { - return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit, + return MakeTextRun(&hyphen, 1, aDrawTarget, aAppUnitsPerDevUnit, gfxFontGroup::TEXT_IS_PERSISTENT, nullptr); } static const uint8_t dash = '-'; - return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit, + return MakeTextRun(&dash, 1, aDrawTarget, aAppUnitsPerDevUnit, gfxFontGroup::TEXT_IS_PERSISTENT, nullptr); } @@ -2048,10 +2051,10 @@ gfxFloat gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider) { if (mHyphenWidth < 0) { - RefPtr ctx(aProvider->GetContext()); - if (ctx) { + RefPtr dt(aProvider->GetDrawTarget()); + if (dt) { nsAutoPtr - hyphRun(MakeHyphenTextRun(ctx, + hyphRun(MakeHyphenTextRun(dt, aProvider->GetAppUnitsPerDevUnit())); mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth(0, hyphRun->GetLength(), nullptr) : 0; @@ -2088,9 +2091,9 @@ gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength, return nullptr; } - InitTextRun(aParams->mContext, textRun, aString, aLength, aMFR); + InitTextRun(aParams->mDrawTarget, textRun, aString, aLength, aMFR); - textRun->FetchGlyphExtents(aParams->mContext); + textRun->FetchGlyphExtents(aParams->mDrawTarget); return textRun; } @@ -2117,16 +2120,16 @@ gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength, return nullptr; } - InitTextRun(aParams->mContext, textRun, aString, aLength, aMFR); + InitTextRun(aParams->mDrawTarget, textRun, aString, aLength, aMFR); - textRun->FetchGlyphExtents(aParams->mContext); + textRun->FetchGlyphExtents(aParams->mDrawTarget); return textRun; } template void -gfxFontGroup::InitTextRun(gfxContext *aContext, +gfxFontGroup::InitTextRun(DrawTarget* aDrawTarget, gfxTextRun *aTextRun, const T *aString, uint32_t aLength, @@ -2205,7 +2208,7 @@ gfxFontGroup::InitTextRun(gfxContext *aContext, // the text is still purely 8-bit; bypass the script-run itemizer // and treat it as a single Latin run - InitScriptRun(aContext, aTextRun, aString, + InitScriptRun(aDrawTarget, aTextRun, aString, 0, aLength, MOZ_SCRIPT_LATIN, aMFR); } else { const char16_t *textPtr; @@ -2251,7 +2254,7 @@ gfxFontGroup::InitTextRun(gfxContext *aContext, NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get())); } - InitScriptRun(aContext, aTextRun, textPtr + runStart, + InitScriptRun(aDrawTarget, aTextRun, textPtr + runStart, runStart, runLimit - runStart, runScript, aMFR); } } @@ -2298,7 +2301,7 @@ IsPUA(uint32_t aUSV) template void -gfxFontGroup::InitScriptRun(gfxContext *aContext, +gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget, gfxTextRun *aTextRun, const T *aString, // text for this script run, // not the entire textrun @@ -2340,7 +2343,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext, aTextRun->AddGlyphRun(matchedFont, range.matchType, aOffset + runStart, (matchedLength > 0), range.orientation); - if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun, + if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun, aString + runStart, aOffset + runStart, matchedLength, @@ -2380,7 +2383,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext, aTextRun->AddGlyphRun(subSuperFont, range.matchType, aOffset + runStart, (matchedLength > 0), range.orientation); - if (!subSuperFont->SplitAndInitTextRun(aContext, aTextRun, + if (!subSuperFont->SplitAndInitTextRun(aDrawTarget, aTextRun, aString + runStart, aOffset + runStart, matchedLength, @@ -2397,7 +2400,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext, syntheticUpper)) { // fallback for small-caps variant glyphs - if (!matchedFont->InitFakeSmallCapsRun(aContext, aTextRun, + if (!matchedFont->InitFakeSmallCapsRun(aDrawTarget, aTextRun, aString + runStart, aOffset + runStart, matchedLength, @@ -2425,7 +2428,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext, aTextRun->AddGlyphRun(matchedFont, range.matchType, aOffset + runStart, (matchedLength > 0), range.orientation); - if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun, + if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun, aString + runStart, aOffset + runStart, matchedLength, @@ -2531,7 +2534,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext, gfxTextRun * gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags, - LazyReferenceContextGetter& aRefContextGetter) + LazyReferenceDrawTargetGetter& aRefDrawTargetGetter) { MOZ_ASSERT(!(aFlags & ~TEXT_ORIENT_MASK), "flags here should only be used to specify orientation"); @@ -2550,9 +2553,9 @@ gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags, : nsDependentString(kASCIIPeriodsChar, ArrayLength(kASCIIPeriodsChar) - 1); - RefPtr refCtx = aRefContextGetter.GetRefContext(); + RefPtr refDT = aRefDrawTargetGetter.GetRefDrawTarget(); Parameters params = { - refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel + refDT, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel }; gfxTextRun* textRun = MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms, diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h index 4a76fea2cc..f39f1790ec 100644 --- a/gfx/thebes/gfxTextRun.h +++ b/gfx/thebes/gfxTextRun.h @@ -82,7 +82,6 @@ struct gfxTextRunDrawCallbacks { */ class gfxTextRun : public gfxShapedText { public: - // Override operator delete to properly free the object that was // allocated via moz_malloc. void operator delete(void* p) { @@ -92,6 +91,7 @@ public: virtual ~gfxTextRun(); typedef gfxFont::RunMetrics Metrics; + typedef mozilla::gfx::DrawTarget DrawTarget; // Public textrun API for general use @@ -194,7 +194,7 @@ public: // Returns a gfxContext that can be used to measure the hyphen glyph. // Only called if the hyphen width is requested. - virtual already_AddRefed GetContext() = 0; + virtual already_AddRefed GetDrawTarget() = 0; // Return the appUnitsPerDevUnit value to be used when measuring. // Only called if the hyphen width is requested. @@ -267,8 +267,8 @@ public: */ Metrics MeasureText(uint32_t aStart, uint32_t aLength, gfxFont::BoundingBoxType aBoundingBoxType, - gfxContext *aRefContextForTightBoundingBox, - PropertyProvider *aProvider); + DrawTarget* aDrawTargetForTightBoundingBox, + PropertyProvider* aProvider); /** * Computes just the advance width for a substring. @@ -310,8 +310,7 @@ public: */ virtual bool SetLineBreaks(uint32_t aStart, uint32_t aLength, bool aLineBreakBefore, bool aLineBreakAfter, - gfxFloat *aAdvanceWidthDelta, - gfxContext *aRefContext); + gfxFloat* aAdvanceWidthDelta); enum SuppressBreak { eNoSuppressBreak, @@ -355,7 +354,7 @@ public: * @param aMetrics if non-null, we fill this in for the returned substring. * If a hyphenation break was used, the hyphen is NOT included in the returned metrics. * @param aBoundingBoxType whether to make the bounding box in aMetrics tight - * @param aRefContextForTightBoundingBox a reference context to get the + * @param aDrawTargetForTightBoundingbox a reference DrawTarget to get the * tight bounding box, if requested * @param aUsedHyphenation if non-null, records if we selected a hyphenation break * @param aLastBreak if non-null and result is aMaxLength, we set this to @@ -383,7 +382,7 @@ public: gfxFloat *aTrimWhitespace, Metrics *aMetrics, gfxFont::BoundingBoxType aBoundingBoxType, - gfxContext *aRefContextForTightBoundingBox, + DrawTarget* aDrawTargetForTightBoundingBox, bool *aUsedHyphenation, uint32_t *aLastBreak, bool aCanWordWrap, @@ -490,8 +489,8 @@ public: // clean out results from shaping in progress, used for fallback scenarios void ClearGlyphsAndCharacters(); - void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, uint32_t aCharIndex, - uint16_t aOrientation); + void SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget, + uint32_t aCharIndex, uint16_t aOrientation); // Set the glyph data for the given character index to the font's // space glyph, IF this can be done as a "simple" glyph record @@ -531,7 +530,7 @@ public: * that some glyph extents might not be fetched due to OOM or other * errors. */ - void FetchGlyphExtents(gfxContext *aRefContext); + void FetchGlyphExtents(DrawTarget* aRefDrawTarget); uint32_t CountMissingGlyphs(); const GlyphRun *GetGlyphRuns(uint32_t *aNumGlyphRuns) { @@ -696,7 +695,7 @@ private: void AccumulatePartialLigatureMetrics(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, gfxFont::BoundingBoxType aBoundingBoxType, - gfxContext *aRefContext, + DrawTarget* aRefDrawTarget, PropertyProvider *aProvider, uint16_t aOrientation, Metrics *aMetrics); @@ -704,7 +703,7 @@ private: // **** measurement helper **** void AccumulateMetricsForRun(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, gfxFont::BoundingBoxType aBoundingBoxType, - gfxContext *aRefContext, + DrawTarget* aRefDrawTarget, PropertyProvider *aProvider, uint32_t aSpacingStart, uint32_t aSpacingEnd, uint16_t aOrientation, @@ -793,14 +792,14 @@ public: * a full Parameters record. */ template - gfxTextRun *MakeTextRun(const T *aString, uint32_t aLength, - gfxContext *aRefContext, + gfxTextRun* MakeTextRun(const T* aString, uint32_t aLength, + DrawTarget* aRefDrawTarget, int32_t aAppUnitsPerDevUnit, uint32_t aFlags, gfxMissingFontRecorder *aMFR) { gfxTextRunFactory::Parameters params = { - aRefContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit + aRefDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit }; return MakeTextRun(aString, aLength, ¶ms, aFlags, aMFR); } @@ -820,7 +819,7 @@ public: * The caller is responsible for deleting the returned text run * when no longer required. */ - gfxTextRun *MakeHyphenTextRun(gfxContext *aCtx, + gfxTextRun* MakeHyphenTextRun(DrawTarget* aDrawTarget, uint32_t aAppUnitsPerDevUnit); /** @@ -871,9 +870,9 @@ public: return mSkipDrawing; } - class LazyReferenceContextGetter { + class LazyReferenceDrawTargetGetter { public: - virtual already_AddRefed GetRefContext() = 0; + virtual already_AddRefed GetRefDrawTarget() = 0; }; // The gfxFontGroup keeps ownership of this textrun. // It is only guaranteed to exist until the next call to GetEllipsisTextRun @@ -881,7 +880,7 @@ public: // group, or until UpdateUserFonts is called, or the fontgroup is destroyed. // Get it/use it/forget it :) - don't keep a reference that might go stale. gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags, - LazyReferenceContextGetter& aRefContextGetter); + LazyReferenceDrawTargetGetter& aRefDrawTargetGetter); protected: // search through pref fonts for a character, return nullptr if no matching pref font @@ -1102,7 +1101,7 @@ protected: // Set up the textrun glyphs for an entire text run: // find script runs, and then call InitScriptRun for each template - void InitTextRun(gfxContext *aContext, + void InitTextRun(DrawTarget* aDrawTarget, gfxTextRun *aTextRun, const T *aString, uint32_t aLength, @@ -1111,7 +1110,7 @@ protected: // InitTextRun helper to handle a single script run, by finding font ranges // and calling each font's InitTextRun() as appropriate template - void InitScriptRun(gfxContext *aContext, + void InitScriptRun(DrawTarget* aDrawTarget, gfxTextRun *aTextRun, const T *aString, uint32_t aScriptRunStart, diff --git a/js/public/Conversions.h b/js/public/Conversions.h index c08da01346..ac09cc2261 100644 --- a/js/public/Conversions.h +++ b/js/public/Conversions.h @@ -36,6 +36,10 @@ ToNumberSlow(JSContext* cx, JS::Value v, double* dp); extern JS_PUBLIC_API(bool) ToInt8Slow(JSContext *cx, JS::HandleValue v, int8_t *out); +/* DO NOT CALL THIS. Use JS::ToUint8. */ +extern JS_PUBLIC_API(bool) +ToUint8Slow(JSContext *cx, JS::HandleValue v, uint8_t *out); + /* DO NOT CALL THIS. Use JS::ToInt16. */ extern JS_PUBLIC_API(bool) ToInt16Slow(JSContext *cx, JS::HandleValue v, int16_t *out); @@ -215,6 +219,19 @@ ToInt8(JSContext *cx, JS::HandleValue v, int8_t *out) return js::ToInt8Slow(cx, v, out); } +/* ES6 ECMA-262, 7.1.10 */ +MOZ_ALWAYS_INLINE bool +ToUint8(JSContext *cx, JS::HandleValue v, uint8_t *out) +{ + detail::AssertArgumentsAreSane(cx, v); + + if (v.isInt32()) { + *out = uint8_t(v.toInt32()); + return true; + } + return js::ToUint8Slow(cx, v, out); +} + /* * Non-standard, with behavior similar to that of ToInt32, except in its * producing an int64_t. @@ -525,6 +542,13 @@ ToInt8(double d) return detail::ToIntWidth(d); } +/* ECMA-262 7.1.10 ToUInt8() specialized for doubles. */ +inline int8_t +ToUint8(double d) +{ + return detail::ToUintWidth(d); +} + /* WEBIDL 4.2.6 */ inline int16_t ToInt16(double d) diff --git a/js/public/ProfilingFrameIterator.h b/js/public/ProfilingFrameIterator.h index 1a79687eac..4c22e568c3 100644 --- a/js/public/ProfilingFrameIterator.h +++ b/js/public/ProfilingFrameIterator.h @@ -19,12 +19,14 @@ class JSScript; namespace js { class Activation; - class AsmJSProfilingFrameIterator; namespace jit { class JitActivation; class JitProfilingFrameIterator; class JitcodeGlobalEntry; } // namespace jit + namespace wasm { + class ProfilingFrameIterator; + } // namespace wasm } // namespace js namespace JS { @@ -49,15 +51,15 @@ class JS_PUBLIC_API(ProfilingFrameIterator) static const unsigned StorageSpace = 8 * sizeof(void*); mozilla::AlignedStorage storage_; - js::AsmJSProfilingFrameIterator& asmJSIter() { + js::wasm::ProfilingFrameIterator& asmJSIter() { MOZ_ASSERT(!done()); MOZ_ASSERT(isAsmJS()); - return *reinterpret_cast(storage_.addr()); + return *reinterpret_cast(storage_.addr()); } - const js::AsmJSProfilingFrameIterator& asmJSIter() const { + const js::wasm::ProfilingFrameIterator& asmJSIter() const { MOZ_ASSERT(!done()); MOZ_ASSERT(isAsmJS()); - return *reinterpret_cast(storage_.addr()); + return *reinterpret_cast(storage_.addr()); } js::jit::JitProfilingFrameIterator& jitIter() { diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJS.cpp similarity index 71% rename from js/src/asmjs/AsmJSValidate.cpp rename to js/src/asmjs/AsmJS.cpp index 63bc52d7e0..1a7413febf 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -16,43 +16,745 @@ * limitations under the License. */ -#include "asmjs/AsmJSValidate.h" +#include "asmjs/AsmJS.h" -#include "mozilla/Move.h" -#include "mozilla/UniquePtr.h" +#include "mozilla/Compression.h" +#include "mozilla/MathAlgorithms.h" #include "jsmath.h" #include "jsprf.h" #include "jsutil.h" -#include "asmjs/AsmJSLink.h" -#include "asmjs/AsmJSModule.h" +#include "jswrapper.h" + #include "asmjs/WasmGenerator.h" +#include "asmjs/WasmSerialize.h" #include "builtin/SIMD.h" #include "frontend/Parser.h" #include "jit/AtomicOperations.h" #include "jit/MIR.h" +#include "js/Class.h" +#include "js/MemoryMetrics.h" +#include "vm/StringBuffer.h" #include "vm/Time.h" +#include "vm/TypedArrayObject.h" #include "jsobjinlines.h" #include "frontend/ParseNode-inl.h" #include "frontend/Parser-inl.h" +#include "vm/ArrayBufferObject-inl.h" using namespace js; using namespace js::frontend; using namespace js::jit; using namespace js::wasm; +using mozilla::Compression::LZ4; using mozilla::HashGeneric; using mozilla::IsNaN; using mozilla::IsNegativeZero; +using mozilla::MallocSizeOf; using mozilla::Move; +using mozilla::PodCopy; +using mozilla::PodEqual; +using mozilla::PodZero; using mozilla::PositiveInfinity; -using mozilla::UniquePtr; using JS::AsmJSOption; using JS::GenericNaN; +/*****************************************************************************/ +// asm.js module object + +// The asm.js spec recognizes this set of builtin Math functions. +enum AsmJSMathBuiltinFunction +{ + AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan, + AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan, + AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp, + AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt, + AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul, + AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max, + AsmJSMathBuiltin_clz32 +}; + +// The asm.js spec will recognize this set of builtin Atomics functions. +enum AsmJSAtomicsBuiltinFunction +{ + AsmJSAtomicsBuiltin_compareExchange, + AsmJSAtomicsBuiltin_exchange, + AsmJSAtomicsBuiltin_load, + AsmJSAtomicsBuiltin_store, + AsmJSAtomicsBuiltin_fence, + AsmJSAtomicsBuiltin_add, + AsmJSAtomicsBuiltin_sub, + AsmJSAtomicsBuiltin_and, + AsmJSAtomicsBuiltin_or, + AsmJSAtomicsBuiltin_xor, + AsmJSAtomicsBuiltin_isLockFree +}; + +// Set of known global object SIMD's attributes, i.e. types +enum AsmJSSimdType +{ + AsmJSSimdType_int32x4, + AsmJSSimdType_float32x4, + AsmJSSimdType_bool32x4 +}; + +static inline bool +IsSignedIntSimdType(AsmJSSimdType type) +{ + switch (type) { + case AsmJSSimdType_int32x4: + return true; + case AsmJSSimdType_float32x4: + case AsmJSSimdType_bool32x4: + return false; + } + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown SIMD type"); +} + +// Set of known operations, for a given SIMD type (int32x4, float32x4,...) +enum AsmJSSimdOperation +{ +#define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op, + FORALL_SIMD_ASMJS_OP(ASMJSSIMDOPERATION) +#undef ASMJSSIMDOPERATION +}; + +// An AsmJSModule extends (via containment) a wasm::Module with the extra +// persistent state necessary to represent a compiled asm.js module. +class js::AsmJSModule +{ + public: + class Global + { + public: + enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction, + AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength }; + enum VarInitKind { InitConstant, InitImport }; + enum ConstantKind { GlobalConstant, MathConstant }; + + private: + struct CacheablePod { + Which which_; + union { + struct { + uint32_t globalDataOffset_; + VarInitKind initKind_; + union { + wasm::ValType importType_; + wasm::Val val_; + } u; + } var; + uint32_t ffiIndex_; + Scalar::Type viewType_; + AsmJSMathBuiltinFunction mathBuiltinFunc_; + AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_; + AsmJSSimdType simdCtorType_; + struct { + AsmJSSimdType type_; + AsmJSSimdOperation which_; + } simdOp; + struct { + ConstantKind kind_; + double value_; + } constant; + } u; + } pod; + PropertyName* name_; + + friend class AsmJSModule; + + Global(Which which, PropertyName* name) { + mozilla::PodZero(&pod); // zero padding for Valgrind + pod.which_ = which; + name_ = name; + MOZ_ASSERT_IF(name_, name_->isTenured()); + } + + void trace(JSTracer* trc) { + if (name_) + TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name"); + } + + public: + Global() {} + Which which() const { + return pod.which_; + } + uint32_t varGlobalDataOffset() const { + MOZ_ASSERT(pod.which_ == Variable); + return pod.u.var.globalDataOffset_; + } + VarInitKind varInitKind() const { + MOZ_ASSERT(pod.which_ == Variable); + return pod.u.var.initKind_; + } + wasm::Val varInitVal() const { + MOZ_ASSERT(pod.which_ == Variable); + MOZ_ASSERT(pod.u.var.initKind_ == InitConstant); + return pod.u.var.u.val_; + } + wasm::ValType varInitImportType() const { + MOZ_ASSERT(pod.which_ == Variable); + MOZ_ASSERT(pod.u.var.initKind_ == InitImport); + return pod.u.var.u.importType_; + } + PropertyName* varImportField() const { + MOZ_ASSERT(pod.which_ == Variable); + MOZ_ASSERT(pod.u.var.initKind_ == InitImport); + return name_; + } + PropertyName* ffiField() const { + MOZ_ASSERT(pod.which_ == FFI); + return name_; + } + uint32_t ffiIndex() const { + MOZ_ASSERT(pod.which_ == FFI); + return pod.u.ffiIndex_; + } + // When a view is created from an imported constructor: + // var I32 = stdlib.Int32Array; + // var i32 = new I32(buffer); + // the second import has nothing to validate and thus has a null field. + PropertyName* maybeViewName() const { + MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); + return name_; + } + Scalar::Type viewType() const { + MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); + return pod.u.viewType_; + } + PropertyName* mathName() const { + MOZ_ASSERT(pod.which_ == MathBuiltinFunction); + return name_; + } + PropertyName* atomicsName() const { + MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction); + return name_; + } + AsmJSMathBuiltinFunction mathBuiltinFunction() const { + MOZ_ASSERT(pod.which_ == MathBuiltinFunction); + return pod.u.mathBuiltinFunc_; + } + AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const { + MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction); + return pod.u.atomicsBuiltinFunc_; + } + AsmJSSimdType simdCtorType() const { + MOZ_ASSERT(pod.which_ == SimdCtor); + return pod.u.simdCtorType_; + } + PropertyName* simdCtorName() const { + MOZ_ASSERT(pod.which_ == SimdCtor); + return name_; + } + PropertyName* simdOperationName() const { + MOZ_ASSERT(pod.which_ == SimdOperation); + return name_; + } + AsmJSSimdOperation simdOperation() const { + MOZ_ASSERT(pod.which_ == SimdOperation); + return pod.u.simdOp.which_; + } + AsmJSSimdType simdOperationType() const { + MOZ_ASSERT(pod.which_ == SimdOperation); + return pod.u.simdOp.type_; + } + PropertyName* constantName() const { + MOZ_ASSERT(pod.which_ == Constant); + return name_; + } + ConstantKind constantKind() const { + MOZ_ASSERT(pod.which_ == Constant); + return pod.u.constant.kind_; + } + double constantValue() const { + MOZ_ASSERT(pod.which_ == Constant); + return pod.u.constant.value_; + } + + WASM_DECLARE_SERIALIZABLE(Global); + }; + + typedef Vector GlobalVector; + + // An import is slightly different than an asm.js FFI function: a single + // asm.js FFI function can be called with many different signatures. When + // compiled to wasm, each unique FFI function paired with signature + // generates a wasm import. + class Import + { + uint32_t ffiIndex_; + public: + Import() = default; + explicit Import(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {} + uint32_t ffiIndex() const { return ffiIndex_; } + }; + + typedef Vector ImportVector; + + class Export + { + PropertyName* name_; + PropertyName* maybeFieldName_; + struct CacheablePod { + uint32_t wasmIndex_; + uint32_t startOffsetInModule_; // Store module-start-relative offsets + uint32_t endOffsetInModule_; // so preserved by serialization. + } pod; + + public: + Export() {} + Export(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex, + uint32_t startOffsetInModule, uint32_t endOffsetInModule) + : name_(name), + maybeFieldName_(maybeFieldName) + { + MOZ_ASSERT(name_->isTenured()); + MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured()); + pod.wasmIndex_ = wasmIndex; + pod.startOffsetInModule_ = startOffsetInModule; + pod.endOffsetInModule_ = endOffsetInModule; + } + + void trace(JSTracer* trc) { + TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name"); + if (maybeFieldName_) + TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field"); + } + + PropertyName* name() const { + return name_; + } + PropertyName* maybeFieldName() const { + return maybeFieldName_; + } + uint32_t startOffsetInModule() const { + return pod.startOffsetInModule_; + } + uint32_t endOffsetInModule() const { + return pod.endOffsetInModule_; + } + static const uint32_t ChangeHeap = UINT32_MAX; + bool isChangeHeap() const { + return pod.wasmIndex_ == ChangeHeap; + } + uint32_t wasmIndex() const { + MOZ_ASSERT(!isChangeHeap()); + return pod.wasmIndex_; + } + + WASM_DECLARE_SERIALIZABLE(Export) + }; + + typedef Vector ExportVector; + + typedef JS::UniquePtr> UniqueWasmModule; + + private: + UniqueWasmModule wasmModule_; + wasm::UniqueStaticLinkData linkData_; + struct CacheablePod { + uint32_t minHeapLength_; + uint32_t maxHeapLength_; + uint32_t heapLengthMask_; + uint32_t numFFIs_; + uint32_t srcLength_; + uint32_t srcLengthWithRightBrace_; + bool strict_; + bool hasArrayView_; + bool isSharedView_; + bool hasFixedMinHeapLength_; + } pod; + const ScriptSourceHolder scriptSource_; + const uint32_t srcStart_; + const uint32_t srcBodyStart_; + GlobalVector globals_; + ImportVector imports_; + ExportVector exports_; + PropertyName* globalArgumentName_; + PropertyName* importArgumentName_; + PropertyName* bufferArgumentName_; + + public: + explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart, + bool strict) + : scriptSource_(scriptSource), + srcStart_(srcStart), + srcBodyStart_(srcBodyStart), + globalArgumentName_(nullptr), + importArgumentName_(nullptr), + bufferArgumentName_(nullptr) + { + mozilla::PodZero(&pod); + pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0); + pod.maxHeapLength_ = 0x80000000; + pod.strict_ = strict; + + MOZ_ASSERT(srcStart_ <= srcBodyStart_); + + // AsmJSCheckedImmediateRange should be defined to be at most the minimum + // heap length so that offsets can be folded into bounds checks. + MOZ_ASSERT(pod.minHeapLength_ - jit::AsmJSCheckedImmediateRange <= pod.minHeapLength_); + } + + void trace(JSTracer* trc) { + if (wasmModule_) + wasmModule_->trace(trc); + for (Global& global : globals_) + global.trace(trc); + for (Export& exp : exports_) + exp.trace(trc); + if (globalArgumentName_) + TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name"); + if (importArgumentName_) + TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name"); + if (bufferArgumentName_) + TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name"); + } + + /*************************************************************************/ + // These functions may be used as soon as the module is constructed: + + ScriptSource* scriptSource() const { + return scriptSource_.get(); + } + bool strict() const { + return pod.strict_; + } + + // srcStart() refers to the offset in the ScriptSource to the beginning of + // the asm.js module function. If the function has been created with the + // Function constructor, this will be the first character in the function + // source. Otherwise, it will be the opening parenthesis of the arguments + // list. + uint32_t srcStart() const { + return srcStart_; + } + + // srcBodyStart() refers to the offset in the ScriptSource to the end + // of the 'use asm' string-literal token. + uint32_t srcBodyStart() const { + return srcBodyStart_; + } + + // While these functions may be accessed at any time, their values will + // change as the module is compiled. + uint32_t minHeapLength() const { + return pod.minHeapLength_; + } + uint32_t maxHeapLength() const { + return pod.maxHeapLength_; + } + uint32_t heapLengthMask() const { + MOZ_ASSERT(pod.hasFixedMinHeapLength_); + return pod.heapLengthMask_; + } + + void initGlobalArgumentName(PropertyName* n) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT_IF(n, n->isTenured()); + globalArgumentName_ = n; + } + void initImportArgumentName(PropertyName* n) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT_IF(n, n->isTenured()); + importArgumentName_ = n; + } + void initBufferArgumentName(PropertyName* n) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT_IF(n, n->isTenured()); + bufferArgumentName_ = n; + } + PropertyName* globalArgumentName() const { + return globalArgumentName_; + } + PropertyName* importArgumentName() const { + return importArgumentName_; + } + PropertyName* bufferArgumentName() const { + return bufferArgumentName_; + } + + bool addGlobalVarInit(const wasm::Val& v, uint32_t globalDataOffset) { + MOZ_ASSERT(!isFinished()); + Global g(Global::Variable, nullptr); + g.pod.u.var.initKind_ = Global::InitConstant; + g.pod.u.var.u.val_ = v; + g.pod.u.var.globalDataOffset_ = globalDataOffset; + return globals_.append(g); + } + bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t globalDataOffset) { + MOZ_ASSERT(!isFinished()); + Global g(Global::Variable, name); + g.pod.u.var.initKind_ = Global::InitImport; + g.pod.u.var.u.importType_ = importType; + g.pod.u.var.globalDataOffset_ = globalDataOffset; + return globals_.append(g); + } + // See Import comment above for FFI vs. Import. + bool addFFI(PropertyName* field, uint32_t* ffiIndex) { + MOZ_ASSERT(!isFinished()); + if (pod.numFFIs_ == UINT32_MAX) + return false; + Global g(Global::FFI, field); + g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++; + return globals_.append(g); + } + bool addArrayView(Scalar::Type vt, PropertyName* maybeField) { + MOZ_ASSERT(!isFinished()); + pod.hasArrayView_ = true; + pod.isSharedView_ = false; + Global g(Global::ArrayView, maybeField); + g.pod.u.viewType_ = vt; + return globals_.append(g); + } + bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT(field); + pod.isSharedView_ = false; + Global g(Global::ArrayViewCtor, field); + g.pod.u.viewType_ = vt; + return globals_.append(g); + } + bool addByteLength() { + MOZ_ASSERT(!isFinished()); + Global g(Global::ByteLength, nullptr); + return globals_.append(g); + } + bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::MathBuiltinFunction, field); + g.pod.u.mathBuiltinFunc_ = func; + return globals_.append(g); + } + bool addMathBuiltinConstant(double value, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::Constant, field); + g.pod.u.constant.value_ = value; + g.pod.u.constant.kind_ = Global::MathConstant; + return globals_.append(g); + } + bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::AtomicsBuiltinFunction, field); + g.pod.u.atomicsBuiltinFunc_ = func; + return globals_.append(g); + } + bool addSimdCtor(AsmJSSimdType type, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::SimdCtor, field); + g.pod.u.simdCtorType_ = type; + return globals_.append(g); + } + bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::SimdOperation, field); + g.pod.u.simdOp.type_ = type; + g.pod.u.simdOp.which_ = op; + return globals_.append(g); + } + bool addGlobalConstant(double value, PropertyName* name) { + MOZ_ASSERT(!isFinished()); + Global g(Global::Constant, name); + g.pod.u.constant.value_ = value; + g.pod.u.constant.kind_ = Global::GlobalConstant; + return globals_.append(g); + } + // See Import comment above for FFI vs. Import. + bool addImport(uint32_t ffiIndex, uint32_t importIndex) { + MOZ_ASSERT(imports_.length() == importIndex); + return imports_.emplaceBack(ffiIndex); + } + bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex, + uint32_t funcSrcBegin, uint32_t funcSrcEnd) + { + // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource + // (the entire file) and ExportedFunctions store offsets relative to + // the beginning of the module (so that they are caching-invariant). + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT(srcStart_ < funcSrcBegin); + MOZ_ASSERT(funcSrcBegin < funcSrcEnd); + return exports_.emplaceBack(name, maybeFieldName, wasmIndex, + funcSrcBegin - srcStart_, funcSrcEnd - srcStart_); + } + bool addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT(!pod.hasFixedMinHeapLength_); + MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1)); + MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0)); + MOZ_ASSERT(max <= pod.maxHeapLength_); + MOZ_ASSERT(min <= max); + pod.heapLengthMask_ = mask; + pod.minHeapLength_ = min; + pod.maxHeapLength_ = max; + pod.hasFixedMinHeapLength_ = true; + return true; + } + + const GlobalVector& globals() const { + return globals_; + } + const ImportVector& imports() const { + return imports_; + } + const ExportVector& exports() const { + return exports_; + } + + void setViewsAreShared() { + if (pod.hasArrayView_) + pod.isSharedView_ = true; + } + bool hasArrayView() const { + return pod.hasArrayView_; + } + bool isSharedView() const { + return pod.isSharedView_; + } + bool tryRequireHeapLengthToBeAtLeast(uint32_t len) { + MOZ_ASSERT(!isFinished()); + if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_) + return false; + if (len > pod.maxHeapLength_) + return false; + len = RoundUpToNextValidAsmJSHeapLength(len); + if (len > pod.minHeapLength_) + pod.minHeapLength_ = len; + return true; + } + + /*************************************************************************/ + // A module isFinished() when compilation completes. After being finished, + // a module must be statically and dynamically linked before execution. + + bool isFinished() const { + return !!wasmModule_; + } + void finish(wasm::Module* wasmModule, wasm::UniqueStaticLinkData linkData, + uint32_t endBeforeCurly, uint32_t endAfterCurly) + { + MOZ_ASSERT(!isFinished()); + + wasmModule_.reset(wasmModule); + linkData_ = Move(linkData); + + MOZ_ASSERT(endBeforeCurly >= srcBodyStart_); + MOZ_ASSERT(endAfterCurly >= srcBodyStart_); + pod.srcLength_ = endBeforeCurly - srcStart_; + pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_; + + MOZ_ASSERT(isFinished()); + } + + /*************************************************************************/ + // These accessor functions can only be used after finish(): + + wasm::Module& wasmModule() const { + MOZ_ASSERT(isFinished()); + return *wasmModule_; + } + uint32_t numFFIs() const { + MOZ_ASSERT(isFinished()); + return pod.numFFIs_; + } + uint32_t srcEndBeforeCurly() const { + MOZ_ASSERT(isFinished()); + return srcStart_ + pod.srcLength_; + } + uint32_t srcEndAfterCurly() const { + MOZ_ASSERT(isFinished()); + return srcStart_ + pod.srcLengthWithRightBrace_; + } + bool staticallyLink(ExclusiveContext* cx) { + return wasmModule_->staticallyLink(cx, *linkData_); + } + + // See WASM_DECLARE_SERIALIZABLE. + size_t serializedSize() const; + uint8_t* serialize(uint8_t* cursor) const; + const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor); + bool clone(JSContext* cx, HandleAsmJSModule moduleObj) const; + void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data); +}; + +static void +AsmJSModuleObject_finalize(FreeOp* fop, JSObject* obj) +{ + AsmJSModuleObject& moduleObj = obj->as(); + if (moduleObj.hasModule()) + fop->delete_(&moduleObj.module()); +} + +static void +AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj) +{ + AsmJSModuleObject& moduleObj = obj->as(); + if (moduleObj.hasModule()) + moduleObj.module().trace(trc); +} + +const Class AsmJSModuleObject::class_ = { + "AsmJSModuleObject", + JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK | + JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS), + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + nullptr, /* resolve */ + nullptr, /* mayResolve */ + AsmJSModuleObject_finalize, + nullptr, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + AsmJSModuleObject_trace +}; + +static AsmJSModuleObject* +NewAsmJSModuleObject(ExclusiveContext* cx) +{ + AutoSetNewObjectMetadata metadata(cx); + JSObject* obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr); + if (!obj) + return nullptr; + + return &obj->as(); +} + +bool +AsmJSModuleObject::hasModule() const +{ + MOZ_ASSERT(is()); + return !getReservedSlot(MODULE_SLOT).isUndefined(); +} + +void +AsmJSModuleObject::setModule(AsmJSModule* newModule) +{ + MOZ_ASSERT(is()); + if (hasModule()) + js_delete(&module()); + setReservedSlot(MODULE_SLOT, PrivateValue(newModule)); +} + +AsmJSModule& +AsmJSModuleObject::module() const +{ + MOZ_ASSERT(is()); + return *(AsmJSModule*)getReservedSlot(MODULE_SLOT).toPrivate(); +} + +void +AsmJSModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) +{ + module().addSizeOfMisc(mallocSizeOf, code, data); +} + /*****************************************************************************/ // ParseNode utilities @@ -540,7 +1242,7 @@ class NumLit Which which_; union { Value scalar_; - jit::SimdConstant simd_; + SimdConstant simd_; } u; public: @@ -551,7 +1253,7 @@ class NumLit MOZ_ASSERT(!isSimd()); } - NumLit(Which w, jit::SimdConstant c) : which_(w) { + NumLit(Which w, SimdConstant c) : which_(w) { u.simd_ = c; MOZ_ASSERT(isSimd()); } @@ -843,30 +1545,30 @@ class Type return ValType::F32x4; } - jit::MIRType toMIRType() const { + MIRType toMIRType() const { switch (which_) { case Double: case DoubleLit: case MaybeDouble: - return jit::MIRType_Double; + return MIRType_Double; case Float: case Floatish: case MaybeFloat: - return jit::MIRType_Float32; + return MIRType_Float32; case Fixnum: case Int: case Signed: case Unsigned: case Intish: - return jit::MIRType_Int32; + return MIRType_Int32; case Int32x4: - return jit::MIRType_Int32x4; + return MIRType_Int32x4; case Float32x4: - return jit::MIRType_Float32x4; + return MIRType_Float32x4; case Bool32x4: - return jit::MIRType_Bool32x4; + return MIRType_Bool32x4; case Void: - return jit::MIRType_None; + return MIRType_None; } MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type"); } @@ -1164,13 +1866,13 @@ class MOZ_STACK_CLASS ModuleValidator Scalar::Type type; }; - class ExitDescriptor + class ImportDescriptor { PropertyName* name_; const LifoSig* sig_; public: - ExitDescriptor(PropertyName* name, const LifoSig& sig) + ImportDescriptor(PropertyName* name, const LifoSig& sig) : name_(name), sig_(&sig) {} @@ -1189,7 +1891,7 @@ class MOZ_STACK_CLASS ModuleValidator static HashNumber hash(const Lookup& l) { return HashGeneric(l.name_, l.sig_.hash()); } - static bool match(const ExitDescriptor& lhs, const Lookup& rhs) { + static bool match(const ImportDescriptor& lhs, const Lookup& rhs) { return lhs.name_ == rhs.name_ && *lhs.sig_ == rhs.sig_; } }; @@ -1202,36 +1904,37 @@ class MOZ_STACK_CLASS ModuleValidator typedef Vector ArrayViewVector; public: - typedef HashMap ExitMap; + typedef HashMap ImportMap; private: - ExclusiveContext* cx_; - AsmJSParser& parser_; + ExclusiveContext* cx_; + AsmJSParser& parser_; - ModuleGenerator mg_; + ModuleGenerator mg_; + AsmJSModule* module_; - LifoAlloc validationLifo_; - FuncVector functions_; - FuncPtrTableVector funcPtrTables_; - GlobalMap globals_; - ArrayViewVector arrayViews_; - ExitMap exits_; + LifoAlloc validationLifo_; + FuncVector functions_; + FuncPtrTableVector funcPtrTables_; + GlobalMap globals_; + ArrayViewVector arrayViews_; + ImportMap imports_; - MathNameMap standardLibraryMathNames_; - AtomicsNameMap standardLibraryAtomicsNames_; - SimdOperationNameMap standardLibrarySimdOpNames_; + MathNameMap standardLibraryMathNames_; + AtomicsNameMap standardLibraryAtomicsNames_; + SimdOperationNameMap standardLibrarySimdOpNames_; - ParseNode* moduleFunctionNode_; - PropertyName* moduleFunctionName_; + ParseNode* moduleFunctionNode_; + PropertyName* moduleFunctionName_; - UniquePtr errorString_; - uint32_t errorOffset_; - bool errorOverRecursed_; + UniqueChars errorString_; + uint32_t errorOffset_; + bool errorOverRecursed_; - bool canValidateChangeHeap_; - bool hasChangeHeap_; - bool supportsSimd_; - bool atomicsPresent_; + bool canValidateChangeHeap_; + bool hasChangeHeap_; + bool supportsSimd_; + bool atomicsPresent_; public: ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser) @@ -1243,7 +1946,7 @@ class MOZ_STACK_CLASS ModuleValidator funcPtrTables_(cx), globals_(cx), arrayViews_(cx), - exits_(cx), + imports_(cx), standardLibraryMathNames_(cx), standardLibraryAtomicsNames_(cx), standardLibrarySimdOpNames_(cx), @@ -1303,8 +2006,8 @@ class MOZ_STACK_CLASS ModuleValidator public: - bool init() { - if (!globals_.init() || !exits_.init()) + bool init(HandleAsmJSModule moduleObj) { + if (!globals_.init() || !imports_.init()) return false; if (!standardLibraryMathNames_.init() || @@ -1371,11 +2074,47 @@ class MOZ_STACK_CLASS ModuleValidator // js::FunctionToString. bool strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict(); - return mg_.init(parser_.ss, srcStart, srcBodyStart, strict); + module_ = cx_->new_(parser_.ss, srcStart, srcBodyStart, strict); + if (!module_) + return false; + + moduleObj->setModule(module_); + + return mg_.init(); } - bool finish(ScopedJSDeletePtr* module, SlowFunctionVector* slowFuncs) { - return mg_.finish(parser_.tokenStream, module, slowFuncs); + bool finish(SlowFunctionVector* slowFuncs) { + uint32_t endBeforeCurly = tokenStream().currentToken().pos.end; + TokenPos pos; + JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand)); + uint32_t endAfterCurly = pos.end; + + auto usesHeap = Module::HeapBool(module_->hasArrayView()); + auto sharedHeap = Module::SharedBool(module_->isSharedView()); + auto mutedErrors = Module::MutedBool(parser_.ss->mutedErrors()); + + CacheableChars filename = make_string_copy(parser_.ss->filename()); + if (!filename) + return false; + + CacheableTwoByteChars displayURL; + if (parser_.ss->hasDisplayURL()) { + uint32_t length = js_strlen(parser_.ss->displayURL()); + displayURL.reset(js_pod_calloc(length + 1)); + if (!displayURL) + return false; + PodCopy(displayURL.get(), parser_.ss->displayURL(), length); + } + + UniqueStaticLinkData linkData; + Module* wasm = mg_.finish(usesHeap, sharedHeap, mutedErrors, + Move(filename), Move(displayURL), + &linkData, slowFuncs); + if (!wasm) + return false; + + module_->finish(wasm, Move(linkData), endBeforeCurly, endAfterCurly); + return true; } // Mutable interface. @@ -1384,140 +2123,128 @@ class MOZ_STACK_CLASS ModuleValidator void initImportArgumentName(PropertyName* n) { module().initImportArgumentName(n); } void initBufferArgumentName(PropertyName* n) { module().initBufferArgumentName(n); } - bool addGlobalVarInit(PropertyName* varName, const NumLit& lit, bool isConst) { - // The type of a const is the exact type of the literal (since its value - // cannot change) which is more precise than the corresponding vartype. - Type type = isConst ? Type::lit(lit) : Type::var(lit.type()); + bool addGlobalVarInit(PropertyName* var, const NumLit& lit, bool isConst) { uint32_t globalDataOffset; - if (!module().addGlobalVarInit(lit.value(), &globalDataOffset)) + if (!mg_.allocateGlobalVar(lit.type(), &globalDataOffset)) return false; Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable; Global* global = validationLifo_.new_(which); if (!global) return false; global->u.varOrConst.globalDataOffset_ = globalDataOffset; - global->u.varOrConst.type_ = type.which(); + global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : Type::var(lit.type())).which(); if (isConst) global->u.varOrConst.literalValue_ = lit; - return globals_.putNew(varName, global); + return globals_.putNew(var, global) && + module().addGlobalVarInit(lit.value(), globalDataOffset); } - bool addGlobalVarImport(PropertyName* varName, PropertyName* fieldName, ValType importType, - bool isConst) - { + bool addGlobalVarImport(PropertyName* var, PropertyName* field, ValType type, bool isConst) { uint32_t globalDataOffset; - if (!module().addGlobalVarImport(fieldName, importType, &globalDataOffset)) + if (!mg_.allocateGlobalVar(type, &globalDataOffset)) return false; Global::Which which = isConst ? Global::ConstantImport : Global::Variable; Global* global = validationLifo_.new_(which); if (!global) return false; global->u.varOrConst.globalDataOffset_ = globalDataOffset; - global->u.varOrConst.type_ = Type::var(importType).which(); - return globals_.putNew(varName, global); + global->u.varOrConst.type_ = Type::var(type).which(); + return globals_.putNew(var, global) && + module().addGlobalVarImport(field, type, globalDataOffset); } - bool addArrayView(PropertyName* varName, Scalar::Type vt, PropertyName* maybeField) - { - if (!arrayViews_.append(ArrayView(varName, vt))) + bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) { + if (!arrayViews_.append(ArrayView(var, vt))) return false; Global* global = validationLifo_.new_(Global::ArrayView); if (!global) return false; - if (!module().addArrayView(vt, maybeField)) - return false; global->u.viewInfo.viewType_ = vt; - return globals_.putNew(varName, global); + return globals_.putNew(var, global) && + module().addArrayView(vt, maybeField); } - bool addMathBuiltinFunction(PropertyName* varName, AsmJSMathBuiltinFunction func, - PropertyName* fieldName) + bool addMathBuiltinFunction(PropertyName* var, AsmJSMathBuiltinFunction func, + PropertyName* field) { - if (!module().addMathBuiltinFunction(func, fieldName)) - return false; Global* global = validationLifo_.new_(Global::MathBuiltinFunction); if (!global) return false; global->u.mathBuiltinFunc_ = func; - return globals_.putNew(varName, global); + return globals_.putNew(var, global) && + module().addMathBuiltinFunction(func, field); } private: - bool addGlobalDoubleConstant(PropertyName* varName, double constant) { + bool addGlobalDoubleConstant(PropertyName* var, double constant) { Global* global = validationLifo_.new_(Global::ConstantLiteral); if (!global) return false; global->u.varOrConst.type_ = Type::Double; global->u.varOrConst.literalValue_ = NumLit(NumLit::Double, DoubleValue(constant)); - return globals_.putNew(varName, global); + return globals_.putNew(var, global); } public: - bool addMathBuiltinConstant(PropertyName* varName, double constant, PropertyName* fieldName) { - if (!module().addMathBuiltinConstant(constant, fieldName)) - return false; - return addGlobalDoubleConstant(varName, constant); + bool addMathBuiltinConstant(PropertyName* var, double constant, PropertyName* field) { + return addGlobalDoubleConstant(var, constant) && + module().addMathBuiltinConstant(constant, field); } - bool addGlobalConstant(PropertyName* varName, double constant, PropertyName* fieldName) { - if (!module().addGlobalConstant(constant, fieldName)) - return false; - return addGlobalDoubleConstant(varName, constant); + bool addGlobalConstant(PropertyName* var, double constant, PropertyName* field) { + return addGlobalDoubleConstant(var, constant) && + module().addGlobalConstant(constant, field); } - bool addAtomicsBuiltinFunction(PropertyName* varName, AsmJSAtomicsBuiltinFunction func, - PropertyName* fieldName) + bool addAtomicsBuiltinFunction(PropertyName* var, AsmJSAtomicsBuiltinFunction func, + PropertyName* field) { - if (!module().addAtomicsBuiltinFunction(func, fieldName)) - return false; Global* global = validationLifo_.new_(Global::AtomicsBuiltinFunction); if (!global) return false; atomicsPresent_ = true; global->u.atomicsBuiltinFunc_ = func; - return globals_.putNew(varName, global); + return globals_.putNew(var, global) && + module().addAtomicsBuiltinFunction(func, field); } - bool addSimdCtor(PropertyName* varName, AsmJSSimdType type, PropertyName* fieldName) { - if (!module().addSimdCtor(type, fieldName)) - return false; + bool addSimdCtor(PropertyName* var, AsmJSSimdType type, PropertyName* field) { Global* global = validationLifo_.new_(Global::SimdCtor); if (!global) return false; global->u.simdCtorType_ = type; - return globals_.putNew(varName, global); + return globals_.putNew(var, global) && + module().addSimdCtor(type, field); } - bool addSimdOperation(PropertyName* varName, AsmJSSimdType type, AsmJSSimdOperation op, - PropertyName* typeVarName, PropertyName* opName) + bool addSimdOperation(PropertyName* var, AsmJSSimdType type, AsmJSSimdOperation op, + PropertyName* opName) { - if (!module().addSimdOperation(type, op, opName)) - return false; Global* global = validationLifo_.new_(Global::SimdOperation); if (!global) return false; global->u.simdOp.type_ = type; global->u.simdOp.which_ = op; - return globals_.putNew(varName, global); + return globals_.putNew(var, global) && + module().addSimdOperation(type, op, opName); } bool addByteLength(PropertyName* name) { canValidateChangeHeap_ = true; - if (!module().addByteLength()) - return false; Global* global = validationLifo_.new_(Global::ByteLength); - return global && globals_.putNew(name, global); + return global && + globals_.putNew(name, global) && + module().addByteLength(); } bool addChangeHeap(PropertyName* name, ParseNode* fn, uint32_t mask, uint32_t min, uint32_t max) { hasChangeHeap_ = true; - module().addChangeHeap(mask, min, max); Global* global = validationLifo_.new_(Global::ChangeHeap); if (!global) return false; global->u.changeHeap.srcBegin_ = fn->pn_pos.begin; global->u.changeHeap.srcEnd_ = fn->pn_pos.end; - return globals_.putNew(name, global); + return globals_.putNew(name, global) && + module().addChangeHeap(mask, min, max); } - bool addArrayViewCtor(PropertyName* varName, Scalar::Type vt, PropertyName* fieldName) { + bool addArrayViewCtor(PropertyName* var, Scalar::Type vt, PropertyName* field) { Global* global = validationLifo_.new_(Global::ArrayViewCtor); if (!global) return false; - if (!module().addArrayViewCtor(vt, fieldName)) - return false; global->u.viewInfo.viewType_ = vt; - return globals_.putNew(varName, global); + return globals_.putNew(var, global) && + module().addArrayViewCtor(vt, field); } - bool addFFI(PropertyName* varName, PropertyName* field) { + bool addFFI(PropertyName* var, PropertyName* field) { Global* global = validationLifo_.new_(Global::FFI); if (!global) return false; @@ -1525,19 +2252,24 @@ class MOZ_STACK_CLASS ModuleValidator if (!module().addFFI(field, &index)) return false; global->u.ffiIndex_ = index; - return globals_.putNew(varName, global); + return globals_.putNew(var, global); } - bool addExportedFunction(const Func& func, PropertyName* maybeFieldName) { + bool addExport(ParseNode* pn, const Func& func, PropertyName* maybeFieldName) { MallocSig::ArgVector args; if (!args.appendAll(func.sig().args())) return false; MallocSig sig(Move(args), func.sig().ret()); - return module().addExportedFunction(func.name(), func.index(), func.srcBegin(), - func.srcEnd(), maybeFieldName, Move(sig)); + uint32_t wasmIndex; + if (!mg_.declareExport(Move(sig), func.index(), &wasmIndex)) + return false; + if (wasmIndex == AsmJSModule::Export::ChangeHeap) + return fail(pn, "too many exports"); + return module().addExport(func.name(), maybeFieldName, wasmIndex, + func.srcBegin(), func.srcEnd()); } - bool addExportedChangeHeap(PropertyName* name, const Global& g, PropertyName* maybeFieldName) { - return module().addExportedChangeHeap(name, g.changeHeapSrcBegin(), g.changeHeapSrcEnd(), - maybeFieldName); + bool addChangeHeapExport(PropertyName* name, const Global& g, PropertyName* maybeFieldName) { + return module().addExport(name, maybeFieldName, AsmJSModule::Export::ChangeHeap, + g.changeHeapSrcBegin(), g.changeHeapSrcEnd()); } private: const LifoSig* getLifoSig(const LifoSig& sig) { @@ -1559,9 +2291,7 @@ class MOZ_STACK_CLASS ModuleValidator if (!lifoSig) return false; *func = validationLifo_.new_(name, firstUse, *lifoSig, funcIndex); - if (!*func) - return false; - return functions_.append(*func); + return *func && functions_.append(*func); } template bool declareFuncPtrTable(PropertyName* name, uint32_t firstUse, SigT& sig, uint32_t mask, @@ -1582,29 +2312,30 @@ class MOZ_STACK_CLASS ModuleValidator FuncPtrTable* t = validationLifo_.new_(cx_, name, firstUse, *lifoSig, mask); return t && funcPtrTables_.append(t); } - bool defineFuncPtrTable(uint32_t funcPtrTableIndex, ModuleGenerator::FuncIndexVector&& elems) { + bool defineFuncPtrTable(uint32_t funcPtrTableIndex, const Vector& elems) { FuncPtrTable& table = *funcPtrTables_[funcPtrTableIndex]; if (table.defined()) return false; table.define(); - return mg_.defineFuncPtrTable(funcPtrTableIndex, Move(elems)); + mg_.defineFuncPtrTable(funcPtrTableIndex, elems); + return true; } - bool addExit(PropertyName* name, MallocSig&& sig, unsigned ffiIndex, unsigned* exitIndex, + bool addImport(PropertyName* name, MallocSig&& sig, unsigned ffiIndex, unsigned* importIndex, const LifoSig** lifoSig) { - ExitDescriptor::Lookup lookup(name, sig); - ExitMap::AddPtr p = exits_.lookupForAdd(lookup); + ImportDescriptor::Lookup lookup(name, sig); + ImportMap::AddPtr p = imports_.lookupForAdd(lookup); if (p) { *lifoSig = &p->key().sig(); - *exitIndex = p->value(); + *importIndex = p->value(); return true; } *lifoSig = getLifoSig(sig); if (!*lifoSig) return false; - if (!module().addExit(Move(sig), ffiIndex, exitIndex)) - return false; - return exits_.add(p, ExitDescriptor(name, **lifoSig), *exitIndex); + return mg_.declareImport(Move(sig), importIndex) && + imports_.add(p, ImportDescriptor(name, **lifoSig), *importIndex) && + module().addImport(ffiIndex, *importIndex); } bool tryOnceToValidateChangeHeap() { @@ -1636,7 +2367,7 @@ class MOZ_STACK_CLASS ModuleValidator MOZ_ASSERT(errorOffset_ == UINT32_MAX); MOZ_ASSERT(str); errorOffset_ = offset; - errorString_ = DuplicateString(cx_, str); + errorString_ = make_string_copy(str); return false; } @@ -1692,7 +2423,7 @@ class MOZ_STACK_CLASS ModuleValidator ParseNode* moduleFunctionNode() const { return moduleFunctionNode_; } PropertyName* moduleFunctionName() const { return moduleFunctionName_; } ModuleGenerator& mg() { return mg_; } - AsmJSModule& module() const { return mg_.module(); } + AsmJSModule& module() const { return *module_; } AsmJSParser& parser() const { return parser_; } TokenStream& tokenStream() const { return parser_.tokenStream; } bool supportsSimd() const { return supportsSimd_; } @@ -1753,9 +2484,19 @@ class MOZ_STACK_CLASS ModuleValidator return false; } - void startFunctionBodies() { - if (atomicsPresent_) + bool startFunctionBodies() { + if (atomicsPresent_) { +#if defined(ENABLE_SHARED_ARRAY_BUFFER) module().setViewsAreShared(); +#else + return failOffset(parser_.tokenStream.currentToken().pos.begin, + "shared memory and atomics not supported by this build"); +#endif + } + return true; + } + bool finishFunctionBodies() { + return mg_.finishFuncs(); } }; @@ -2680,8 +3421,7 @@ CheckGlobalSimdImport(ModuleValidator& m, ParseNode* initNode, PropertyName* var static bool CheckGlobalSimdOperationImport(ModuleValidator& m, const ModuleValidator::Global* global, - ParseNode* initNode, PropertyName* varName, PropertyName* ctorVarName, - PropertyName* opName) + ParseNode* initNode, PropertyName* varName, PropertyName* opName) { AsmJSSimdType simdType = global->simdCtorType(); AsmJSSimdOperation simdOp; @@ -2689,7 +3429,7 @@ CheckGlobalSimdOperationImport(ModuleValidator& m, const ModuleValidator::Global return m.failName(initNode, "'%s' is not a standard SIMD operation", opName); if (!IsSimdValidOperationType(simdType, simdOp)) return m.failName(initNode, "'%s' is not an operation supported by the SIMD type", opName); - return m.addSimdOperation(varName, simdType, simdOp, ctorVarName, opName); + return m.addSimdOperation(varName, simdType, simdOp, opName); } static bool @@ -2751,7 +3491,7 @@ CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initN if (!global->isSimdCtor()) return m.failName(base, "expecting SIMD constructor name, got %s", field); - return CheckGlobalSimdOperationImport(m, global, initNode, varName, base->name(), field); + return CheckGlobalSimdOperationImport(m, global, initNode, varName, field); } static bool @@ -3672,7 +4412,7 @@ CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type) } static bool -CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, js::jit::AtomicOp op) +CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op) { if (CallArgListLength(call) != 3) return f.fail(call, "Atomics binary operator must be passed 3 arguments"); @@ -4054,8 +4794,7 @@ CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, ExprType ret, Type* if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, sig, mask, &funcPtrTableIndex)) return false; - uint32_t globalDataOffset = f.m().module().funcPtrTable(funcPtrTableIndex).globalDataOffset(); - f.patch32(globalDataOffsetAt, globalDataOffset); + f.patch32(globalDataOffsetAt, f.m().mg().funcPtrTableGlobalDataOffset(funcPtrTableIndex)); f.patchSig(sigAt, &f.m().funcPtrTable(funcPtrTableIndex).sig()); *type = Type::ret(ret); @@ -4099,7 +4838,7 @@ CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprT // Global data offset size_t offsetAt = f.temp32(); - // Pointer to the exit's signature in the module's lifo + // Pointer to the import's signature in the module's lifo size_t sigAt = f.tempPtr(); // Call node position (asm.js specific) WriteCallLineCol(f, callNode); @@ -4110,13 +4849,12 @@ CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprT MallocSig sig(Move(args), ret); - unsigned exitIndex = 0; + unsigned importIndex = 0; const LifoSig* lifoSig = nullptr; - if (!f.m().addExit(calleeName, Move(sig), ffiIndex, &exitIndex, &lifoSig)) + if (!f.m().addImport(calleeName, Move(sig), ffiIndex, &importIndex, &lifoSig)) return false; - JS_STATIC_ASSERT(offsetof(AsmJSModule::ExitDatum, exit) == 0); - f.patch32(offsetAt, f.module().exit(exitIndex).globalDataOffset()); + f.patch32(offsetAt, f.m().mg().importExitGlobalDataOffset(importIndex)); f.patchSig(sigAt, lifoSig); *type = Type::ret(ret); return true; @@ -4970,6 +5708,10 @@ CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValida case AsmJSSimdOperation_shiftLeftByScalar: return CheckSimdBinary(f, call, opType, MSimdShift::lsh, type); + case AsmJSSimdOperation_shiftRightByScalar: + return CheckSimdBinary(f, call, opType, + IsSignedIntSimdType(opType) ? MSimdShift::rsh : MSimdShift::ursh, + type); case AsmJSSimdOperation_shiftRightArithmeticByScalar: return CheckSimdBinary(f, call, opType, MSimdShift::rsh, type); case AsmJSSimdOperation_shiftRightLogicalByScalar: @@ -5852,7 +6594,7 @@ enum class InterruptCheckPosition { static void MaybeAddInterruptCheck(FunctionValidator& f, InterruptCheckPosition pos, ParseNode* pn) { - if (f.m().module().usesSignalHandlersForInterrupt()) + if (f.m().mg().args().useSignalHandlersForInterrupt) return; switch (pos) { @@ -6681,7 +7423,7 @@ CheckFuncPtrTable(ModuleValidator& m, ParseNode* var) unsigned mask = length - 1; - ModuleGenerator::FuncIndexVector elems; + Vector elemFuncIndices(m.cx()); const LifoSig* sig = nullptr; for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) { if (!elem->isKind(PNK_NAME)) @@ -6699,7 +7441,7 @@ CheckFuncPtrTable(ModuleValidator& m, ParseNode* var) sig = &func->sig(); } - if (!elems.append(func->index())) + if (!elemFuncIndices.append(func->index())) return false; } @@ -6707,7 +7449,7 @@ CheckFuncPtrTable(ModuleValidator& m, ParseNode* var) if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), *sig, mask, &funcPtrTableIndex)) return false; - if (!m.defineFuncPtrTable(funcPtrTableIndex, Move(elems))) + if (!m.defineFuncPtrTable(funcPtrTableIndex, elemFuncIndices)) return m.fail(var, "duplicate function-pointer definition"); return true; @@ -6752,10 +7494,10 @@ CheckModuleExportFunction(ModuleValidator& m, ParseNode* pn, PropertyName* maybe return m.failName(pn, "exported function name '%s' not found", funcName); if (global->which() == ModuleValidator::Global::Function) - return m.addExportedFunction(m.function(global->funcIndex()), maybeFieldName); + return m.addExport(pn, m.function(global->funcIndex()), maybeFieldName); if (global->which() == ModuleValidator::Global::ChangeHeap) - return m.addExportedChangeHeap(funcName, *global, maybeFieldName); + return m.addChangeHeapExport(funcName, *global, maybeFieldName); return m.failName(pn, "'%s' is not a function", funcName); } @@ -6838,14 +7580,13 @@ CheckModuleEnd(ModuleValidator &m) } static bool -CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, - ScopedJSDeletePtr* module, unsigned* time, - SlowFunctionVector* slowFuncs) +CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, HandleAsmJSModule obj, + unsigned* time, SlowFunctionVector* slowFuncs) { int64_t before = PRMJ_Now(); ModuleValidator m(cx, parser); - if (!m.init()) + if (!m.init(obj)) return false; if (PropertyName* moduleFunctionName = FunctionName(m.moduleFunctionNode())) { @@ -6869,17 +7610,15 @@ CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, if (!CheckModuleGlobals(m)) return false; - m.startFunctionBodies(); - -#if !defined(ENABLE_SHARED_ARRAY_BUFFER) - if (m.usesSharedMemory()) - return m.failOffset(m.parser().tokenStream.currentToken().pos.begin, - "shared memory and atomics not supported by this build"); -#endif + if (!m.startFunctionBodies()) + return false; if (!CheckFunctions(m)) return false; + if (!m.finishFunctionBodies()) + return false; + if (!CheckFuncPtrTables(m)) return false; @@ -6889,81 +7628,1280 @@ CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, if (!CheckModuleEnd(m)) return false; - if (!m.finish(module, slowFuncs)) + if (!m.finish(slowFuncs)) return false; *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC; return true; } -static bool -BuildConsoleMessage(ExclusiveContext* cx, AsmJSModule& module, - unsigned time, const SlowFunctionVector& slowFuncs, - JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr* out) +/*****************************************************************************/ +// Runtime calls to asm.js module exports + +static AsmJSModuleObject& +FunctionToModuleObject(JSFunction* fun) { -#ifndef JS_MORE_DETERMINISTIC - ScopedJSFreePtr slowText; - if (!slowFuncs.empty()) { - slowText.reset(JS_smprintf("; %d functions compiled slowly: ", slowFuncs.length())); - if (!slowText) - return true; + MOZ_ASSERT(IsAsmJSFunction(fun) || IsAsmJSModule(fun)); + const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_MODULE_SLOT); + return v.toObject().as(); +} - for (unsigned i = 0; i < slowFuncs.length(); i++) { - const SlowFunction& func = slowFuncs[i]; - JSAutoByteString name; - if (!AtomToPrintableString(cx, func.name, &name)) +static unsigned +FunctionToExportIndex(JSFunction* fun) +{ + MOZ_ASSERT(IsAsmJSFunction(fun)); + const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT); + return v.toInt32(); +} + +static bool +ChangeHeap(JSContext* cx, AsmJSModule& module, const CallArgs& args) +{ + HandleValue bufferArg = args.get(0); + if (!IsArrayBuffer(bufferArg)) { + ReportIncompatible(cx, args); + return false; + } + + Rooted newBuffer(cx, &bufferArg.toObject().as()); + uint32_t heapLength = newBuffer->byteLength(); + if (heapLength & module.heapLengthMask() || + heapLength < module.minHeapLength() || + heapLength > module.maxHeapLength()) + { + args.rval().set(BooleanValue(false)); + return true; + } + + if (!module.hasArrayView()) { + args.rval().set(BooleanValue(true)); + return true; + } + + MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength)); + + bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB; + if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, useSignalHandlers)) + return false; + + args.rval().set(BooleanValue(module.wasmModule().changeHeap(newBuffer, cx))); + return true; +} + +static bool +CallAsmJS(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedFunction callee(cx, &args.callee().as()); + + AsmJSModule& module = FunctionToModuleObject(callee).module(); + const AsmJSModule::Export& exp = module.exports()[FunctionToExportIndex(callee)]; + + // The heap-changing function is a special-case and is implemented by C++. + if (exp.isChangeHeap()) + return ChangeHeap(cx, module, args); + + return module.wasmModule().callExport(cx, exp.wasmIndex(), args); +} + +/*****************************************************************************/ +// Link-time validation + +static bool +LinkFail(JSContext* cx, const char* str) +{ + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage, + nullptr, JSMSG_USE_ASM_LINK_FAIL, str); + return false; +} + +static bool +GetDataProperty(JSContext* cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v) +{ + if (!objVal.isObject()) + return LinkFail(cx, "accessing property of non-object"); + + RootedObject obj(cx, &objVal.toObject()); + if (IsScriptedProxy(obj)) + return LinkFail(cx, "accessing property of a Proxy"); + + Rooted desc(cx); + RootedId id(cx, NameToId(field)); + if (!GetPropertyDescriptor(cx, obj, id, &desc)) + return false; + + if (!desc.object()) + return LinkFail(cx, "property not present on object"); + + if (!desc.isDataDescriptor()) + return LinkFail(cx, "property is not a data property"); + + v.set(desc.value()); + return true; +} + +static bool +HasPureCoercion(JSContext* cx, HandleValue v) +{ + if (IsVectorObject(v) || IsVectorObject(v) || IsVectorObject(v)) + return true; + + // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a + // bug that generates code that passes functions for some imports. To avoid + // breaking all the code that contains this bug, we make an exception for + // functions that don't have user-defined valueOf or toString, for their + // coercions are not observable and coercion via ToNumber/ToInt32 + // definitely produces NaN/0. We should remove this special case later once + // most apps have been built with newer Emscripten. + jsid toString = NameToId(cx->names().toString); + if (v.toObject().is() && + HasObjectValueOf(&v.toObject(), cx) && + ClassMethodIsNative(cx, &v.toObject().as(), &JSFunction::class_, toString, fun_toString)) + { + return true; + } + + return false; +} + +static bool +ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t* globalData, + HandleValue importVal) +{ + void* datum = globalData + global.varGlobalDataOffset(); + + switch (global.varInitKind()) { + case AsmJSModule::Global::InitConstant: { + Val v = global.varInitVal(); + switch (v.type()) { + case ValType::I32: + *(int32_t*)datum = v.i32(); + break; + case ValType::I64: + MOZ_CRASH("int64"); + case ValType::F32: + *(float*)datum = v.f32(); + break; + case ValType::F64: + *(double*)datum = v.f64(); + break; + case ValType::I32x4: + case ValType::B32x4: + // Bool32x4 uses the same data layout as Int32x4. + memcpy(datum, v.i32x4(), Simd128DataSize); + break; + case ValType::F32x4: + memcpy(datum, v.f32x4(), Simd128DataSize); + break; + } + break; + } + + case AsmJSModule::Global::InitImport: { + RootedPropertyName field(cx, global.varImportField()); + RootedValue v(cx); + if (!GetDataProperty(cx, importVal, field, &v)) + return false; + + if (!v.isPrimitive() && !HasPureCoercion(cx, v)) + return LinkFail(cx, "Imported values must be primitives"); + + switch (global.varInitImportType()) { + case ValType::I32: + if (!ToInt32(cx, v, (int32_t*)datum)) return false; + break; + case ValType::I64: + MOZ_CRASH("int64"); + case ValType::F32: + if (!RoundFloat32(cx, v, (float*)datum)) + return false; + break; + case ValType::F64: + if (!ToNumber(cx, v, (double*)datum)) + return false; + break; + case ValType::I32x4: { + SimdConstant simdConstant; + if (!ToSimdConstant(cx, v, &simdConstant)) + return false; + memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize); + break; + } + case ValType::F32x4: { + SimdConstant simdConstant; + if (!ToSimdConstant(cx, v, &simdConstant)) + return false; + memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize); + break; + } + case ValType::B32x4: { + SimdConstant simdConstant; + if (!ToSimdConstant(cx, v, &simdConstant)) + return false; + // Bool32x4 uses the same data layout as Int32x4. + memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize); + break; + } + } + break; + } + } - slowText.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowText.get(), - name.ptr(), func.line, func.column, func.ms, - i+1 < slowFuncs.length() ? ", " : "")); - if (!slowText) - return true; + return true; +} + +static bool +ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue importVal, + AutoVectorRooter* ffis) +{ + RootedPropertyName field(cx, global.ffiField()); + RootedValue v(cx); + if (!GetDataProperty(cx, importVal, field, &v)) + return false; + + if (!v.isObject() || !v.toObject().is()) + return LinkFail(cx, "FFI imports must be functions"); + + (*ffis)[global.ffiIndex()].set(&v.toObject().as()); + return true; +} + +static bool +ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + RootedPropertyName field(cx, global.maybeViewName()); + if (!field) + return true; + + RootedValue v(cx); + if (!GetDataProperty(cx, globalVal, field, &v)) + return false; + + bool tac = IsTypedArrayConstructor(v, global.viewType()); + if (!tac) + return LinkFail(cx, "bad typed array constructor"); + + return true; +} + +static bool +ValidateByteLength(JSContext* cx, HandleValue globalVal) +{ + RootedPropertyName field(cx, cx->names().byteLength); + RootedValue v(cx); + if (!GetDataProperty(cx, globalVal, field, &v)) + return false; + + if (!v.isObject() || !v.toObject().isBoundFunction()) + return LinkFail(cx, "byteLength must be a bound function object"); + + RootedFunction fun(cx, &v.toObject().as()); + + RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget())); + if (!IsNativeFunction(boundTarget, fun_call)) + return LinkFail(cx, "bound target of byteLength must be Function.prototype.call"); + + RootedValue boundThis(cx, fun->getBoundFunctionThis()); + if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter)) + return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor"); + + return true; +} + +static bool +ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + RootedValue v(cx); + if (!GetDataProperty(cx, globalVal, cx->names().Math, &v)) + return false; + + RootedPropertyName field(cx, global.mathName()); + if (!GetDataProperty(cx, v, field, &v)) + return false; + + Native native = nullptr; + switch (global.mathBuiltinFunction()) { + case AsmJSMathBuiltin_sin: native = math_sin; break; + case AsmJSMathBuiltin_cos: native = math_cos; break; + case AsmJSMathBuiltin_tan: native = math_tan; break; + case AsmJSMathBuiltin_asin: native = math_asin; break; + case AsmJSMathBuiltin_acos: native = math_acos; break; + case AsmJSMathBuiltin_atan: native = math_atan; break; + case AsmJSMathBuiltin_ceil: native = math_ceil; break; + case AsmJSMathBuiltin_floor: native = math_floor; break; + case AsmJSMathBuiltin_exp: native = math_exp; break; + case AsmJSMathBuiltin_log: native = math_log; break; + case AsmJSMathBuiltin_pow: native = math_pow; break; + case AsmJSMathBuiltin_sqrt: native = math_sqrt; break; + case AsmJSMathBuiltin_min: native = math_min; break; + case AsmJSMathBuiltin_max: native = math_max; break; + case AsmJSMathBuiltin_abs: native = math_abs; break; + case AsmJSMathBuiltin_atan2: native = math_atan2; break; + case AsmJSMathBuiltin_imul: native = math_imul; break; + case AsmJSMathBuiltin_clz32: native = math_clz32; break; + case AsmJSMathBuiltin_fround: native = math_fround; break; + } + + if (!IsNativeFunction(v, native)) + return LinkFail(cx, "bad Math.* builtin function"); + + return true; +} + +static PropertyName* +SimdTypeToName(JSContext* cx, AsmJSSimdType type) +{ + switch (type) { + case AsmJSSimdType_int32x4: return cx->names().int32x4; + case AsmJSSimdType_float32x4: return cx->names().float32x4; + case AsmJSSimdType_bool32x4: return cx->names().bool32x4; + } + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type"); +} + +static SimdTypeDescr::Type +AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type) +{ + switch (type) { + case AsmJSSimdType_int32x4: return Int32x4::type; + case AsmJSSimdType_float32x4: return Float32x4::type; + case AsmJSSimdType_bool32x4: return Bool32x4::type; + } + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType"); +} + +static bool +ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal, + MutableHandleValue out) +{ + RootedValue v(cx); + if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v)) + return false; + + AsmJSSimdType type; + if (global.which() == AsmJSModule::Global::SimdCtor) + type = global.simdCtorType(); + else + type = global.simdOperationType(); + + RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type)); + if (!GetDataProperty(cx, v, simdTypeName, &v)) + return false; + + if (!v.isObject()) + return LinkFail(cx, "bad SIMD type"); + + RootedObject simdDesc(cx, &v.toObject()); + if (!simdDesc->is()) + return LinkFail(cx, "bad SIMD type"); + + if (AsmJSSimdTypeToTypeDescrType(type) != simdDesc->as().type()) + return LinkFail(cx, "bad SIMD type"); + + out.set(v); + return true; +} + +static bool +ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + RootedValue _(cx); + return ValidateSimdType(cx, global, globalVal, &_); +} + +static bool +ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + // SIMD operations are loaded from the SIMD type, so the type must have been + // validated before the operation. + RootedValue v(cx); + JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v)); + + RootedPropertyName opName(cx, global.simdOperationName()); + if (!GetDataProperty(cx, v, opName, &v)) + return false; + + Native native = nullptr; + switch (global.simdOperationType()) { +#define SET_NATIVE_INT32X4(op) case AsmJSSimdOperation_##op: native = simd_int32x4_##op; break; +#define SET_NATIVE_FLOAT32X4(op) case AsmJSSimdOperation_##op: native = simd_float32x4_##op; break; +#define SET_NATIVE_BOOL32X4(op) case AsmJSSimdOperation_##op: native = simd_bool32x4_##op; break; +#define FALLTHROUGH(op) case AsmJSSimdOperation_##op: + case AsmJSSimdType_int32x4: + switch (global.simdOperation()) { + FORALL_INT32X4_ASMJS_OP(SET_NATIVE_INT32X4) + default: + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first " + "place"); + } + break; + case AsmJSSimdType_float32x4: + switch (global.simdOperation()) { + FORALL_FLOAT32X4_ASMJS_OP(SET_NATIVE_FLOAT32X4) + default: + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first " + "place"); + } + break; + case AsmJSSimdType_bool32x4: + switch (global.simdOperation()) { + FORALL_BOOL_SIMD_OP(SET_NATIVE_BOOL32X4) + default: + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first " + "place"); + } + break; +#undef FALLTHROUGH +#undef SET_NATIVE_FLOAT32X4 +#undef SET_NATIVE_INT32X4 +#undef SET_NATIVE_BOOL32X4 +#undef SET_NATIVE + } + if (!native || !IsNativeFunction(v, native)) + return LinkFail(cx, "bad SIMD.type.* operation"); + return true; +} + +static bool +ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + RootedValue v(cx); + if (!GetDataProperty(cx, globalVal, cx->names().Atomics, &v)) + return false; + RootedPropertyName field(cx, global.atomicsName()); + if (!GetDataProperty(cx, v, field, &v)) + return false; + + Native native = nullptr; + switch (global.atomicsBuiltinFunction()) { + case AsmJSAtomicsBuiltin_compareExchange: native = atomics_compareExchange; break; + case AsmJSAtomicsBuiltin_exchange: native = atomics_exchange; break; + case AsmJSAtomicsBuiltin_load: native = atomics_load; break; + case AsmJSAtomicsBuiltin_store: native = atomics_store; break; + case AsmJSAtomicsBuiltin_fence: native = atomics_fence; break; + case AsmJSAtomicsBuiltin_add: native = atomics_add; break; + case AsmJSAtomicsBuiltin_sub: native = atomics_sub; break; + case AsmJSAtomicsBuiltin_and: native = atomics_and; break; + case AsmJSAtomicsBuiltin_or: native = atomics_or; break; + case AsmJSAtomicsBuiltin_xor: native = atomics_xor; break; + case AsmJSAtomicsBuiltin_isLockFree: native = atomics_isLockFree; break; + } + + if (!IsNativeFunction(v, native)) + return LinkFail(cx, "bad Atomics.* builtin function"); + + return true; +} + +static bool +ValidateConstant(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + RootedPropertyName field(cx, global.constantName()); + RootedValue v(cx, globalVal); + + if (global.constantKind() == AsmJSModule::Global::MathConstant) { + if (!GetDataProperty(cx, v, cx->names().Math, &v)) + return false; + } + + if (!GetDataProperty(cx, v, field, &v)) + return false; + + if (!v.isNumber()) + return LinkFail(cx, "math / global constant value needs to be a number"); + + // NaN != NaN + if (IsNaN(global.constantValue())) { + if (!IsNaN(v.toNumber())) + return LinkFail(cx, "global constant value needs to be NaN"); + } else { + if (v.toNumber() != global.constantValue()) + return LinkFail(cx, "global constant value mismatch"); + } + + return true; +} + +static bool +CheckBuffer(JSContext* cx, AsmJSModule& module, HandleValue bufferVal, + MutableHandle buffer) +{ + if (module.isSharedView() && !IsSharedArrayBuffer(bufferVal)) + return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer"); + + if (!module.isSharedView() && !IsArrayBuffer(bufferVal)) + return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer"); + + buffer.set(&AsAnyArrayBuffer(bufferVal)); + uint32_t heapLength = buffer->byteLength(); + + if (!IsValidAsmJSHeapLength(heapLength)) { + UniqueChars msg( + JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next " + "valid length is 0x%x", + heapLength, + RoundUpToNextValidAsmJSHeapLength(heapLength))); + return LinkFail(cx, msg.get()); + } + + // This check is sufficient without considering the size of the loaded datum because heap + // loads and stores start on an aligned boundary and the heap byteLength has larger alignment. + MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX); + if (heapLength < module.minHeapLength()) { + UniqueChars msg( + JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied " + "by const heap accesses and/or change-heap minimum-length requirements).", + heapLength, + module.minHeapLength())); + return LinkFail(cx, msg.get()); + } + + if (heapLength > module.maxHeapLength()) { + UniqueChars msg( + JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x", + heapLength, + module.maxHeapLength())); + return LinkFail(cx, msg.get()); + } + + // Shell builtins may have disabled signal handlers since the module we're + // cloning was compiled. LookupAsmJSModuleInCache checks for signal handlers + // as well for the caching case. + if (module.wasmModule().compileArgs() != CompileArgs(cx)) + return LinkFail(cx, "Signals have been toggled since compilation"); + + if (buffer->is()) { + Rooted abheap(cx, &buffer->as()); + bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB; + if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, useSignalHandlers)) + return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use"); + } + + return true; +} + +static bool +DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module) +{ + HandleValue globalVal = args.get(0); + HandleValue importVal = args.get(1); + HandleValue bufferVal = args.get(2); + + Rooted buffer(cx); + if (module.hasArrayView() && !CheckBuffer(cx, module, bufferVal, &buffer)) + return false; + + AutoVectorRooter ffis(cx); + if (!ffis.resize(module.numFFIs())) + return false; + + for (const AsmJSModule::Global& global : module.globals()) { + switch (global.which()) { + case AsmJSModule::Global::Variable: + if (!ValidateGlobalVariable(cx, global, module.wasmModule().globalData(), importVal)) + return false; + break; + case AsmJSModule::Global::FFI: + if (!ValidateFFI(cx, global, importVal, &ffis)) + return false; + break; + case AsmJSModule::Global::ArrayView: + case AsmJSModule::Global::ArrayViewCtor: + if (!ValidateArrayView(cx, global, globalVal)) + return false; + break; + case AsmJSModule::Global::ByteLength: + if (!ValidateByteLength(cx, globalVal)) + return false; + break; + case AsmJSModule::Global::MathBuiltinFunction: + if (!ValidateMathBuiltinFunction(cx, global, globalVal)) + return false; + break; + case AsmJSModule::Global::AtomicsBuiltinFunction: + if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal)) + return false; + break; + case AsmJSModule::Global::Constant: + if (!ValidateConstant(cx, global, globalVal)) + return false; + break; + case AsmJSModule::Global::SimdCtor: + if (!ValidateSimdType(cx, global, globalVal)) + return false; + break; + case AsmJSModule::Global::SimdOperation: + if (!ValidateSimdOperation(cx, global, globalVal)) + return false; + break; } } - const char* cacheString = ""; - switch (cacheResult) { - case JS::AsmJSCache_Success: - cacheString = "stored in cache"; - break; - case JS::AsmJSCache_ModuleTooSmall: - cacheString = "not stored in cache (too small to benefit)"; - break; - case JS::AsmJSCache_SynchronousScript: - cacheString = "unable to cache asm.js in synchronous scripts; try loading " - "asm.js via