diff --git a/dom/svg/SVGAnimatedLength.h b/dom/svg/SVGAnimatedLength.h index 01ee773cef28..580675c2f8cc 100644 --- a/dom/svg/SVGAnimatedLength.h +++ b/dom/svg/SVGAnimatedLength.h @@ -33,7 +33,7 @@ class SVGViewportElement; class UserSpaceMetrics { public: - virtual ~UserSpaceMetrics() {} + virtual ~UserSpaceMetrics() = default; virtual float GetEmLength() const = 0; virtual float GetExLength() const = 0; diff --git a/dom/svg/SVGAnimatedLengthList.h b/dom/svg/SVGAnimatedLengthList.h index 1454155ebd76..4cd5f4693cf5 100644 --- a/dom/svg/SVGAnimatedLengthList.h +++ b/dom/svg/SVGAnimatedLengthList.h @@ -42,7 +42,7 @@ class SVGAnimatedLengthList { friend class dom::DOMSVGLengthList; public: - SVGAnimatedLengthList() {} + SVGAnimatedLengthList() = default; /** * Because it's so important that mBaseVal and its DOMSVGLengthList wrapper diff --git a/dom/svg/SVGAnimatedPathSegList.h b/dom/svg/SVGAnimatedPathSegList.h index 3bd4e1d04fc9..0b369d5ceec1 100644 --- a/dom/svg/SVGAnimatedPathSegList.h +++ b/dom/svg/SVGAnimatedPathSegList.h @@ -44,7 +44,7 @@ class SVGAnimatedPathSegList final { friend class DOMSVGPathSegList; public: - SVGAnimatedPathSegList() {} + SVGAnimatedPathSegList() = default; /** * Because it's so important that mBaseVal and its DOMSVGPathSegList wrapper diff --git a/dom/svg/SVGAnimatedPointList.h b/dom/svg/SVGAnimatedPointList.h index cf7d67c339ab..157ecf3709be 100644 --- a/dom/svg/SVGAnimatedPointList.h +++ b/dom/svg/SVGAnimatedPointList.h @@ -43,7 +43,7 @@ class SVGAnimatedPointList { friend class DOMSVGPointList; public: - SVGAnimatedPointList() {} + SVGAnimatedPointList() = default; /** * Because it's so important that mBaseVal and its DOMSVGPointList wrapper diff --git a/dom/svg/SVGComponentTransferFunctionElement.h b/dom/svg/SVGComponentTransferFunctionElement.h index ae7fff6a2d25..cf3ed14cc444 100644 --- a/dom/svg/SVGComponentTransferFunctionElement.h +++ b/dom/svg/SVGComponentTransferFunctionElement.h @@ -34,7 +34,7 @@ class SVGComponentTransferFunctionElement already_AddRefed&& aNodeInfo) : SVGComponentTransferFunctionElementBase(std::move(aNodeInfo)) {} - virtual ~SVGComponentTransferFunctionElement() {} + virtual ~SVGComponentTransferFunctionElement() = default; public: typedef gfx::ComponentTransferAttributes ComponentTransferAttributes; diff --git a/dom/svg/SVGFilters.h b/dom/svg/SVGFilters.h index 7960a9cbc2dd..f594e7b0bc2f 100644 --- a/dom/svg/SVGFilters.h +++ b/dom/svg/SVGFilters.h @@ -55,7 +55,7 @@ class SVGFE : public SVGFEBase { explicit SVGFE(already_AddRefed&& aNodeInfo) : SVGFEBase(std::move(aNodeInfo)) {} - virtual ~SVGFE() {} + virtual ~SVGFE() = default; public: typedef mozilla::gfx::PrimitiveAttributes PrimitiveAttributes; @@ -183,7 +183,7 @@ class SVGFELightingElement : public SVGFELightingElementBase { already_AddRefed&& aNodeInfo) : SVGFELightingElementBase(std::move(aNodeInfo)) {} - virtual ~SVGFELightingElement() {} + virtual ~SVGFELightingElement() = default; public: // interfaces: diff --git a/dom/svg/SVGIRect.h b/dom/svg/SVGIRect.h index 01147f2e5b04..62055d5309e8 100644 --- a/dom/svg/SVGIRect.h +++ b/dom/svg/SVGIRect.h @@ -20,7 +20,7 @@ namespace dom { class SVGIRect : public nsISupports, public nsWrapperCache { public: - virtual ~SVGIRect() {} + virtual ~SVGIRect() = default; JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override { diff --git a/dom/svg/SVGIntegerPairSMILType.h b/dom/svg/SVGIntegerPairSMILType.h index 4b15efa083a5..2a6708abcc26 100644 --- a/dom/svg/SVGIntegerPairSMILType.h +++ b/dom/svg/SVGIntegerPairSMILType.h @@ -41,7 +41,7 @@ class SVGIntegerPairSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGIntegerPairSMILType() {} + constexpr SVGIntegerPairSMILType() = default; }; } // namespace mozilla diff --git a/dom/svg/SVGLengthList.h b/dom/svg/SVGLengthList.h index dcd1d12f8f95..24c255ee06cc 100644 --- a/dom/svg/SVGLengthList.h +++ b/dom/svg/SVGLengthList.h @@ -39,8 +39,8 @@ class SVGLengthList { friend class SVGAnimatedLengthList; public: - SVGLengthList() {} - ~SVGLengthList() {} + SVGLengthList() = default; + ~SVGLengthList() = default; // Only methods that don't make/permit modification to this list are public. // Only our friend classes can access methods that may change us. diff --git a/dom/svg/SVGLengthListSMILType.h b/dom/svg/SVGLengthListSMILType.h index f4be976a9110..d1f1d8e19ca9 100644 --- a/dom/svg/SVGLengthListSMILType.h +++ b/dom/svg/SVGLengthListSMILType.h @@ -91,7 +91,7 @@ class SVGLengthListSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGLengthListSMILType() {} + constexpr SVGLengthListSMILType() = default; }; } // namespace mozilla diff --git a/dom/svg/SVGMatrix.h b/dom/svg/SVGMatrix.h index 85b8ac6f473a..23d8f794068f 100644 --- a/dom/svg/SVGMatrix.h +++ b/dom/svg/SVGMatrix.h @@ -63,7 +63,7 @@ class SVGMatrix final : public nsWrapperCache { * Ctors for SVGMatrix objects created independently of a DOMSVGTransform. */ // Default ctor for gfxMatrix will produce identity mx - SVGMatrix() {} + SVGMatrix() = default; explicit SVGMatrix(const gfxMatrix& aMatrix) : mMatrix(aMatrix) {} @@ -103,7 +103,7 @@ class SVGMatrix final : public nsWrapperCache { already_AddRefed SkewY(float angle, ErrorResult& rv); private: - ~SVGMatrix() {} + ~SVGMatrix() = default; void SetMatrix(const gfxMatrix& aMatrix) { if (mTransform) { diff --git a/dom/svg/SVGMotionSMILType.h b/dom/svg/SVGMotionSMILType.h index 963b155e06f1..8cf0508128fb 100644 --- a/dom/svg/SVGMotionSMILType.h +++ b/dom/svg/SVGMotionSMILType.h @@ -71,7 +71,7 @@ class SVGMotionSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGMotionSMILType() {} + constexpr SVGMotionSMILType() = default; }; } // namespace mozilla diff --git a/dom/svg/SVGNumberList.h b/dom/svg/SVGNumberList.h index bc1642eb5166..9a37e7e8f919 100644 --- a/dom/svg/SVGNumberList.h +++ b/dom/svg/SVGNumberList.h @@ -37,8 +37,8 @@ class SVGNumberList { friend class SVGAnimatedNumberList; public: - SVGNumberList() {} - ~SVGNumberList() {} + SVGNumberList() = default; + ~SVGNumberList() = default; // Only methods that don't make/permit modification to this list are public. // Only our friend classes can access methods that may change us. diff --git a/dom/svg/SVGNumberListSMILType.h b/dom/svg/SVGNumberListSMILType.h index bdb88ecdf3d3..4353932390ff 100644 --- a/dom/svg/SVGNumberListSMILType.h +++ b/dom/svg/SVGNumberListSMILType.h @@ -45,7 +45,7 @@ class SVGNumberListSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGNumberListSMILType() {} + constexpr SVGNumberListSMILType() = default; }; } // namespace mozilla diff --git a/dom/svg/SVGNumberPairSMILType.h b/dom/svg/SVGNumberPairSMILType.h index 63140330f5a6..e5857de186b1 100644 --- a/dom/svg/SVGNumberPairSMILType.h +++ b/dom/svg/SVGNumberPairSMILType.h @@ -38,7 +38,7 @@ class SVGNumberPairSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGNumberPairSMILType() {} + constexpr SVGNumberPairSMILType() = default; }; } // namespace mozilla diff --git a/dom/svg/SVGOrientSMILType.h b/dom/svg/SVGOrientSMILType.h index 16bb7e15fa40..5e959f9392b3 100644 --- a/dom/svg/SVGOrientSMILType.h +++ b/dom/svg/SVGOrientSMILType.h @@ -58,7 +58,7 @@ class SVGOrientSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGOrientSMILType() {} + constexpr SVGOrientSMILType() = default; }; } // namespace mozilla diff --git a/dom/svg/SVGPathData.h b/dom/svg/SVGPathData.h index 593015870ba5..f59833343591 100644 --- a/dom/svg/SVGPathData.h +++ b/dom/svg/SVGPathData.h @@ -91,8 +91,8 @@ class SVGPathData { public: typedef const float* const_iterator; - SVGPathData() {} - ~SVGPathData() {} + SVGPathData() = default; + ~SVGPathData() = default; // Only methods that don't make/permit modification to this list are public. // Only our friend classes can access methods that may change us. diff --git a/dom/svg/SVGPathSegListSMILType.h b/dom/svg/SVGPathSegListSMILType.h index b5a08fe9dc4d..b712cd373f21 100644 --- a/dom/svg/SVGPathSegListSMILType.h +++ b/dom/svg/SVGPathSegListSMILType.h @@ -48,7 +48,7 @@ class SVGPathSegListSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGPathSegListSMILType() {} + constexpr SVGPathSegListSMILType() = default; }; } // namespace mozilla diff --git a/dom/svg/SVGPathSegUtils.h b/dom/svg/SVGPathSegUtils.h index 26654a8a34c9..53d05b04441f 100644 --- a/dom/svg/SVGPathSegUtils.h +++ b/dom/svg/SVGPathSegUtils.h @@ -73,7 +73,7 @@ struct SVGPathTraversalState { */ class SVGPathSegUtils { private: - SVGPathSegUtils() {} // private to prevent instances + SVGPathSegUtils() = default; // private to prevent instances public: static void GetValueAsString(const float* aSeg, nsAString& aValue); diff --git a/dom/svg/SVGPointList.h b/dom/svg/SVGPointList.h index 78f7320642ed..7d12d64069de 100644 --- a/dom/svg/SVGPointList.h +++ b/dom/svg/SVGPointList.h @@ -37,8 +37,8 @@ class SVGPointList { friend class DOMSVGPoint; public: - SVGPointList() {} - ~SVGPointList() {} + SVGPointList() = default; + ~SVGPointList() = default; // Only methods that don't make/permit modification to this list are public. // Only our friend classes can access methods that may change us. diff --git a/dom/svg/SVGPointListSMILType.h b/dom/svg/SVGPointListSMILType.h index 8400ef8fd58d..88f79038cb72 100644 --- a/dom/svg/SVGPointListSMILType.h +++ b/dom/svg/SVGPointListSMILType.h @@ -45,7 +45,7 @@ class SVGPointListSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGPointListSMILType() {} + constexpr SVGPointListSMILType() = default; }; } // namespace mozilla diff --git a/dom/svg/SVGRect.h b/dom/svg/SVGRect.h index 934795e08a29..6474aefa3783 100644 --- a/dom/svg/SVGRect.h +++ b/dom/svg/SVGRect.h @@ -46,7 +46,7 @@ class SVGRect final : public SVGIRect { virtual nsIContent* GetParentObject() const override { return mParent; } protected: - ~SVGRect() {} + ~SVGRect() = default; nsCOMPtr mParent; float mX, mY, mWidth, mHeight; diff --git a/dom/svg/SVGSVGElement.h b/dom/svg/SVGSVGElement.h index e4c471dc264d..550807b8e28d 100644 --- a/dom/svg/SVGSVGElement.h +++ b/dom/svg/SVGSVGElement.h @@ -65,7 +65,7 @@ class DOMSVGTranslatePoint final : public nsISVGPoint { RefPtr mElement; private: - ~DOMSVGTranslatePoint() {} + ~DOMSVGTranslatePoint() = default; }; typedef SVGViewportElement SVGSVGElementBase; diff --git a/dom/svg/SVGStringList.h b/dom/svg/SVGStringList.h index e53c81a6c90d..2b9c604b9a70 100644 --- a/dom/svg/SVGStringList.h +++ b/dom/svg/SVGStringList.h @@ -22,7 +22,7 @@ class SVGStringList { public: SVGStringList() : mIsSet(false), mIsCommaSeparated(false) {} - ~SVGStringList() {} + ~SVGStringList() = default; void SetIsCommaSeparated(bool aIsCommaSeparated) { mIsCommaSeparated = aIsCommaSeparated; diff --git a/dom/svg/SVGTests.h b/dom/svg/SVGTests.h index 743e91622622..500af1c58fc0 100644 --- a/dom/svg/SVGTests.h +++ b/dom/svg/SVGTests.h @@ -105,7 +105,7 @@ class SVGTests : public nsISupports { } protected: - virtual ~SVGTests() {} + virtual ~SVGTests() = default; private: enum { FEATURES, EXTENSIONS, LANGUAGE }; diff --git a/dom/svg/SVGTransformList.h b/dom/svg/SVGTransformList.h index 536056153b3a..adbb1cc8fc39 100644 --- a/dom/svg/SVGTransformList.h +++ b/dom/svg/SVGTransformList.h @@ -32,8 +32,8 @@ class SVGTransformList { friend class dom::DOMSVGTransform; public: - SVGTransformList() {} - ~SVGTransformList() {} + SVGTransformList() = default; + ~SVGTransformList() = default; // Only methods that don't make/permit modification to this list are public. // Only our friend classes can access methods that may change us. diff --git a/dom/svg/SVGTransformListSMILType.h b/dom/svg/SVGTransformListSMILType.h index 0b15b006966e..bc7833e04bf0 100644 --- a/dom/svg/SVGTransformListSMILType.h +++ b/dom/svg/SVGTransformListSMILType.h @@ -116,7 +116,7 @@ class SVGTransformListSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGTransformListSMILType() {} + constexpr SVGTransformListSMILType() = default; }; } // end namespace mozilla diff --git a/dom/svg/SVGTransformableElement.h b/dom/svg/SVGTransformableElement.h index 204e807ab8bf..2a3adce9a44f 100644 --- a/dom/svg/SVGTransformableElement.h +++ b/dom/svg/SVGTransformableElement.h @@ -27,7 +27,7 @@ class SVGTransformableElement : public SVGElement { public: explicit SVGTransformableElement(already_AddRefed&& aNodeInfo) : SVGElement(std::move(aNodeInfo)) {} - virtual ~SVGTransformableElement() {} + virtual ~SVGTransformableElement() = default; virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override = 0; diff --git a/dom/svg/SVGViewBoxSMILType.h b/dom/svg/SVGViewBoxSMILType.h index 996697aac62f..943831247fba 100644 --- a/dom/svg/SVGViewBoxSMILType.h +++ b/dom/svg/SVGViewBoxSMILType.h @@ -38,7 +38,7 @@ class SVGViewBoxSMILType : public SMILType { private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGViewBoxSMILType() {} + constexpr SVGViewBoxSMILType() = default; }; } // namespace mozilla diff --git a/dom/svg/SVGViewportElement.h b/dom/svg/SVGViewportElement.h index c8b31b9c3c5a..b6c0fc8f5852 100644 --- a/dom/svg/SVGViewportElement.h +++ b/dom/svg/SVGViewportElement.h @@ -25,9 +25,9 @@ class nsSVGViewportFrame; namespace mozilla { class AutoPreserveAspectRatioOverride; -class DOMSVGAnimatedPreserveAspectRatio; namespace dom { +class DOMSVGAnimatedPreserveAspectRatio; class SVGAnimatedRect; class SVGViewElement; class SVGViewportElement; diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 0a930430e820..0e4f91b1a2d8 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -923,6 +923,7 @@ void gfxPlatform::Init() { #else # error "No gfxPlatform implementation available" #endif + gPlatform->PopulateScreenInfo(); gPlatform->InitAcceleration(); gPlatform->InitWebRenderConfig(); // When using WebRender, we defer initialization of the D3D11 devices until @@ -960,7 +961,6 @@ void gfxPlatform::Init() { InitLayersIPC(); - gPlatform->PopulateScreenInfo(); gPlatform->ComputeTileSize(); #ifdef MOZ_ENABLE_FREETYPE @@ -2511,7 +2511,7 @@ static bool CalculateWrQualifiedPrefValue() { } static FeatureState& WebRenderHardwareQualificationStatus( - bool aHasBattery, nsCString& aOutFailureId) { + const IntSize& aScreenSize, bool aHasBattery, nsCString& aOutFailureId) { FeatureState& featureWebRenderQualified = gfxConfig::GetFeature(Feature::WEBRENDER_QUALIFIED); featureWebRenderQualified.EnableByDefault(); @@ -2576,7 +2576,8 @@ static FeatureState& WebRenderHardwareQualificationStatus( FeatureStatus::Blocked, "Device too old", NS_LITERAL_CSTRING("FEATURE_FAILURE_DEVICE_TOO_OLD")); } - } else if (adapterVendorID == u"0x8086") { // Intel + } else if (adapterVendorID == u"0x8086" || + adapterVendorID == u"mesa/i965") { // Intel const uint16_t supportedDevices[] = { 0x191d, // HD Graphics P530 0x192d, // Iris Pro Graphics P555 @@ -2605,6 +2606,18 @@ static FeatureState& WebRenderHardwareQualificationStatus( featureWebRenderQualified.Disable( FeatureStatus::Blocked, "Device too old", NS_LITERAL_CSTRING("FEATURE_FAILURE_DEVICE_TOO_OLD")); + } else if (adapterVendorID == u"mesa/i965") { + const int32_t maxPixels = 3440 * 1440; // UWQHD + int32_t pixels = aScreenSize.width * aScreenSize.height; + if (pixels > maxPixels) { + featureWebRenderQualified.Disable( + FeatureStatus::Blocked, "Screen size too large", + NS_LITERAL_CSTRING("FEATURE_FAILURE_SCREEN_SIZE_TOO_LARGE")); + } else if (pixels <= 0) { + featureWebRenderQualified.Disable( + FeatureStatus::Blocked, "Screen size unknown", + NS_LITERAL_CSTRING("FEATURE_FAILURE_SCREEN_SIZE_UNKNOWN")); + } } #endif } else { @@ -2653,7 +2666,8 @@ void gfxPlatform::InitWebRenderConfig() { nsCString failureId; FeatureState& featureWebRenderQualified = - WebRenderHardwareQualificationStatus(HasBattery(), failureId); + WebRenderHardwareQualificationStatus(GetScreenSize(), HasBattery(), + failureId); FeatureState& featureWebRender = gfxConfig::GetFeature(Feature::WEBRENDER); featureWebRender.DisableByDefault( @@ -2748,6 +2762,16 @@ void gfxPlatform::InitWebRenderConfig() { WebRenderDebugPrefChangeCallback, WR_DEBUG_PREF); } } +#if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID) + else if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) { + // Hardware compositing should be disabled by default if we aren't using + // WebRender. We had to check if it is enabled at all, because it may + // already have been forced disabled (e.g. safe mode, headless). It may + // still be forced on by the user, and if so, this should have no effect. + gfxConfig::Disable(Feature::HW_COMPOSITING, FeatureStatus::Blocked, + "Acceleration blocked by platform"); + } +#endif #ifdef XP_WIN if (Preferences::GetBool("gfx.webrender.dcomp-win.enabled", false)) { diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 34cf85857b72..896dff0452ff 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -736,7 +736,7 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener { gfxPlatform(); virtual ~gfxPlatform(); - virtual bool HasBattery() { return true; } + virtual bool HasBattery() { return false; } virtual void InitAcceleration(); virtual void InitWebRenderConfig(); diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp index 144af010f76a..0a6ecc42342d 100644 --- a/gfx/thebes/gfxPlatformGtk.cpp +++ b/gfx/thebes/gfxPlatformGtk.cpp @@ -330,7 +330,7 @@ uint32_t gfxPlatformGtk::MaxGenericSubstitions() { } bool gfxPlatformGtk::AccelerateLayersByDefault() { - return gfxPrefs::WebRenderAll(); + return true; } void gfxPlatformGtk::GetPlatformCMSOutputProfile(void*& mem, size_t& size) { diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 2220909cbf98..41823ded73d4 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -307,6 +307,14 @@ typedef enum JSGCParamKey { */ JSGC_MIN_NURSERY_BYTES = 31, + /* + * The minimum time to allow between triggering last ditch GCs in seconds. + * + * Default: 60 seconds + * Pref: None + */ + JSGC_MIN_LAST_DITCH_GC_PERIOD = 32, + } JSGCParamKey; /* diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 574d7a88a1ce..b10510e4e8d6 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -488,7 +488,8 @@ static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) { _("allocationThreshold", JSGC_ALLOCATION_THRESHOLD, true) \ _("minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT, true) \ _("maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT, true) \ - _("compactingEnabled", JSGC_COMPACTING_ENABLED, true) + _("compactingEnabled", JSGC_COMPACTING_ENABLED, true) \ + _("minLastDitchGCPeriod", JSGC_MIN_LAST_DITCH_GC_PERIOD, true) static const struct ParamInfo { const char* name; diff --git a/js/src/frontend/BytecodeControlStructures.cpp b/js/src/frontend/BytecodeControlStructures.cpp index eb1007251b7f..727a707fb762 100644 --- a/js/src/frontend/BytecodeControlStructures.cpp +++ b/js/src/frontend/BytecodeControlStructures.cpp @@ -41,7 +41,7 @@ LoopControl::LoopControl(BytecodeEmitter* bce, StatementKind loopKind) LoopControl* enclosingLoop = findNearest(enclosing()); - stackDepth_ = bce->stackDepth; + stackDepth_ = bce->bytecodeSection().stackDepth(); loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1; int loopSlots; @@ -81,7 +81,7 @@ bool LoopControl::emitContinueTarget(BytecodeEmitter* bce) { bool LoopControl::emitSpecialBreakForDone(BytecodeEmitter* bce) { // This doesn't pop stack values, nor handle any other controls. // Should be called on the toplevel of the loop. - MOZ_ASSERT(bce->stackDepth == stackDepth_); + MOZ_ASSERT(bce->bytecodeSection().stackDepth() == stackDepth_); MOZ_ASSERT(bce->innermostNestableControl == this); if (!bce->newSrcNote(SRC_BREAK)) { @@ -109,7 +109,7 @@ bool LoopControl::emitLoopHead(BytecodeEmitter* bce, } } - head_ = {bce->offset()}; + head_ = {bce->bytecodeSection().offset()}; ptrdiff_t off; if (!bce->emitJumpTargetOp(JSOP_LOOPHEAD, &off)) { return false; @@ -126,7 +126,7 @@ bool LoopControl::emitLoopEntry(BytecodeEmitter* bce, } } - JumpTarget entry = {bce->offset()}; + JumpTarget entry = {bce->bytecodeSection().offset()}; bce->patchJumpsToTarget(entryJump_, entry); MOZ_ASSERT(loopDepth_ > 0); @@ -135,7 +135,8 @@ bool LoopControl::emitLoopEntry(BytecodeEmitter* bce, if (!bce->emitJumpTargetOp(JSOP_LOOPENTRY, &off)) { return false; } - SetLoopEntryDepthHintAndFlags(bce->code(off), loopDepth_, canIonOsr_); + SetLoopEntryDepthHintAndFlags(bce->bytecodeSection().code(off), loopDepth_, + canIonOsr_); return true; } diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 0c2bab81ff09..af1e9316b6b9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -91,6 +91,24 @@ static bool ParseNodeRequiresSpecialLineNumberNotes(ParseNode* pn) { kind == ParseNodeKind::Function; } +BytecodeEmitter::BytecodeSection::BytecodeSection(JSContext* cx, + uint32_t lineNum) + : code_(cx), + notes_(cx), + tryNoteList_(cx), + scopeNoteList_(cx), + resumeOffsetList_(cx), + currentLine_(lineNum) {} + +BytecodeEmitter::PerScriptData::PerScriptData(JSContext* cx) + : scopeList_(cx), + numberList_(cx), + atomIndices_(cx->frontendCollectionPool()) {} + +bool BytecodeEmitter::PerScriptData::init(JSContext* cx) { + return atomIndices_.acquire(cx); +} + BytecodeEmitter::BytecodeEmitter( BytecodeEmitter* parent, SharedContext* sc, HandleScript script, Handle lazyScript, uint32_t lineNum, EmitterMode emitterMode, @@ -100,23 +118,17 @@ BytecodeEmitter::BytecodeEmitter( parent(parent), script(cx, script), lazyScript(cx, lazyScript), - code_(cx), - notes_(cx), - currentLine_(lineNum), + bytecodeSection_(cx, lineNum), + perScriptData_(cx), fieldInitializers_(fieldInitializers), - atomIndices(cx->frontendCollectionPool()), firstLine(lineNum), - numberList(cx), - scopeList(cx), - tryNoteList(cx), - scopeNoteList(cx), - resumeOffsetList(cx), emitterMode(emitterMode) { MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript); if (sc->isFunctionBox()) { // Functions have IC entries for type monitoring |this| and arguments. - numICEntries = sc->asFunctionBox()->function()->nargs() + 1; + bytecodeSection().setNumICEntries(sc->asFunctionBox()->function()->nargs() + + 1); } } @@ -148,7 +160,7 @@ void BytecodeEmitter::initFromBodyPosition(TokenPos bodyPosition) { setFunctionBodyEndPos(bodyPosition.end); } -bool BytecodeEmitter::init() { return atomIndices.acquire(cx); } +bool BytecodeEmitter::init() { return perScriptData_.init(cx); } template T* BytecodeEmitter::findInnermostNestableControl() const { @@ -194,9 +206,7 @@ bool BytecodeEmitter::markStepBreakpoint() { // We track the location of the most recent separator for use in // markSimpleBreakpoint. Note that this means that the position must already // be set before markStepBreakpoint is called. - lastSeparatorOffet_ = code().length(); - lastSeparatorLine_ = currentLine_; - lastSeparatorColumn_ = lastColumn_; + bytecodeSection().updateSeparatorPosition(); return true; } @@ -210,10 +220,7 @@ bool BytecodeEmitter::markSimpleBreakpoint() { // expression start, we need to skip marking it breakable in order to avoid // having two breakpoints with the same line/column position. // Note: This assumes that the position for the call has already been set. - bool isDuplicateLocation = - lastSeparatorLine_ == currentLine_ && lastSeparatorColumn_ == lastColumn_; - - if (!isDuplicateLocation) { + if (!bytecodeSection().isDuplicateLocation()) { if (!newSrcNote(SRC_BREAKPOINT)) { return false; } @@ -223,9 +230,9 @@ bool BytecodeEmitter::markSimpleBreakpoint() { } bool BytecodeEmitter::emitCheck(JSOp op, ptrdiff_t delta, ptrdiff_t* offset) { - *offset = code().length(); + *offset = bytecodeSection().code().length(); - if (!code().growByUninitialized(delta)) { + if (!bytecodeSection().code().growByUninitialized(delta)) { ReportOutOfMemory(cx); return false; } @@ -233,30 +240,30 @@ bool BytecodeEmitter::emitCheck(JSOp op, ptrdiff_t delta, ptrdiff_t* offset) { // If op is JOF_TYPESET (see the type barriers comment in TypeInference.h), // reserve a type set to store its result. if (CodeSpec[op].format & JOF_TYPESET) { - if (typesetCount < JSScript::MaxBytecodeTypeSets) { - typesetCount++; + if (bytecodeSection().typesetCount() < JSScript::MaxBytecodeTypeSets) { + bytecodeSection().addTypesetCount(); } } if (BytecodeOpHasIC(op)) { - numICEntries++; + bytecodeSection().addNumICEntries(); } return true; } -void BytecodeEmitter::updateDepth(ptrdiff_t target) { +void BytecodeEmitter::BytecodeSection::updateDepth(ptrdiff_t target) { jsbytecode* pc = code(target); int nuses = StackUses(pc); int ndefs = StackDefs(pc); - stackDepth -= nuses; - MOZ_ASSERT(stackDepth >= 0); - stackDepth += ndefs; + stackDepth_ -= nuses; + MOZ_ASSERT(stackDepth_ >= 0); + stackDepth_ += ndefs; - if ((uint32_t)stackDepth > maxStackDepth) { - maxStackDepth = stackDepth; + if ((uint32_t)stackDepth_ > maxStackDepth_) { + maxStackDepth_ = stackDepth_; } } @@ -280,9 +287,9 @@ bool BytecodeEmitter::emit1(JSOp op) { return false; } - jsbytecode* code = this->code(offset); + jsbytecode* code = bytecodeSection().code(offset); code[0] = jsbytecode(op); - updateDepth(offset); + bytecodeSection().updateDepth(offset); return true; } @@ -294,10 +301,10 @@ bool BytecodeEmitter::emit2(JSOp op, uint8_t op1) { return false; } - jsbytecode* code = this->code(offset); + jsbytecode* code = bytecodeSection().code(offset); code[0] = jsbytecode(op); code[1] = jsbytecode(op1); - updateDepth(offset); + bytecodeSection().updateDepth(offset); return true; } @@ -313,11 +320,11 @@ bool BytecodeEmitter::emit3(JSOp op, jsbytecode op1, jsbytecode op2) { return false; } - jsbytecode* code = this->code(offset); + jsbytecode* code = bytecodeSection().code(offset); code[0] = jsbytecode(op); code[1] = op1; code[2] = op2; - updateDepth(offset); + bytecodeSection().updateDepth(offset); return true; } @@ -330,7 +337,7 @@ bool BytecodeEmitter::emitN(JSOp op, size_t extra, ptrdiff_t* offset) { return false; } - jsbytecode* code = this->code(off); + jsbytecode* code = bytecodeSection().code(off); code[0] = jsbytecode(op); /* The remaining |extra| bytes are set by the caller */ @@ -339,7 +346,7 @@ bool BytecodeEmitter::emitN(JSOp op, size_t extra, ptrdiff_t* offset) { * operand yet to be stored in the extra bytes after op. */ if (CodeSpec[op].nuses >= 0) { - updateDepth(off); + bytecodeSection().updateDepth(off); } if (offset) { @@ -351,7 +358,7 @@ bool BytecodeEmitter::emitN(JSOp op, size_t extra, ptrdiff_t* offset) { bool BytecodeEmitter::emitJumpTargetOp(JSOp op, ptrdiff_t* off) { MOZ_ASSERT(BytecodeIsJumpTarget(op)); - size_t numEntries = numICEntries; + size_t numEntries = bytecodeSection().numICEntries(); if (MOZ_UNLIKELY(numEntries > UINT32_MAX)) { reportError(nullptr, JSMSG_NEED_DIET, js_script_str); return false; @@ -361,21 +368,22 @@ bool BytecodeEmitter::emitJumpTargetOp(JSOp op, ptrdiff_t* off) { return false; } - SET_ICINDEX(code(*off), numEntries); + SET_ICINDEX(bytecodeSection().code(*off), numEntries); return true; } bool BytecodeEmitter::emitJumpTarget(JumpTarget* target) { - ptrdiff_t off = offset(); + ptrdiff_t off = bytecodeSection().offset(); // Alias consecutive jump targets. - if (off == lastTarget.offset + ptrdiff_t(JSOP_JUMPTARGET_LENGTH)) { - target->offset = lastTarget.offset; + if (off == bytecodeSection().lastTargetOffset() + + ptrdiff_t(JSOP_JUMPTARGET_LENGTH)) { + target->offset = bytecodeSection().lastTargetOffset(); return true; } target->offset = off; - lastTarget.offset = off; + bytecodeSection().setLastTargetOffset(off); ptrdiff_t opOff; return emitJumpTargetOp(JSOP_JUMPTARGET, &opOff); @@ -387,11 +395,11 @@ bool BytecodeEmitter::emitJumpNoFallthrough(JSOp op, JumpList* jump) { return false; } - jsbytecode* code = this->code(offset); + jsbytecode* code = bytecodeSection().code(offset); code[0] = jsbytecode(op); MOZ_ASSERT(-1 <= jump->offset && jump->offset < offset); - jump->push(this->code(0), offset); - updateDepth(offset); + jump->push(bytecodeSection().code(0), offset); + bytecodeSection().updateDepth(offset); return true; } @@ -425,11 +433,12 @@ bool BytecodeEmitter::emitBackwardJump(JSOp op, JumpTarget target, } void BytecodeEmitter::patchJumpsToTarget(JumpList jump, JumpTarget target) { - MOZ_ASSERT(-1 <= jump.offset && jump.offset <= offset()); - MOZ_ASSERT(0 <= target.offset && target.offset <= offset()); - MOZ_ASSERT_IF(jump.offset != -1 && target.offset + 4 <= offset(), - BytecodeIsJumpTarget(JSOp(*code(target.offset)))); - jump.patchAll(code(0), target); + MOZ_ASSERT(-1 <= jump.offset && jump.offset <= bytecodeSection().offset()); + MOZ_ASSERT(0 <= target.offset && target.offset <= bytecodeSection().offset()); + MOZ_ASSERT_IF( + jump.offset != -1 && target.offset + 4 <= bytecodeSection().offset(), + BytecodeIsJumpTarget(JSOp(*bytecodeSection().code(target.offset)))); + jump.patchAll(bytecodeSection().code(0), target); } bool BytecodeEmitter::emitJumpTargetAndPatch(JumpList jump) { @@ -459,7 +468,7 @@ bool BytecodeEmitter::emitCall(JSOp op, uint16_t argc, ParseNode* pn) { } bool BytecodeEmitter::emitDupAt(unsigned slotFromTop) { - MOZ_ASSERT(slotFromTop < unsigned(stackDepth)); + MOZ_ASSERT(slotFromTop < unsigned(bytecodeSection().stackDepth())); if (slotFromTop == 0) { return emit1(JSOP_DUP); @@ -475,7 +484,7 @@ bool BytecodeEmitter::emitDupAt(unsigned slotFromTop) { return false; } - jsbytecode* pc = code(off); + jsbytecode* pc = bytecodeSection().code(off); SET_UINT24(pc, slotFromTop); return true; } @@ -516,14 +525,14 @@ bool BytecodeEmitter::updateLineNumberNotes(uint32_t offset) { ErrorReporter* er = &parser->errorReporter(); bool onThisLine; - if (!er->isOnThisLine(offset, currentLine(), &onThisLine)) { + if (!er->isOnThisLine(offset, bytecodeSection().currentLine(), &onThisLine)) { er->errorNoOffset(JSMSG_OUT_OF_MEMORY); return false; } if (!onThisLine) { unsigned line = er->lineAt(offset); - unsigned delta = line - currentLine(); + unsigned delta = line - bytecodeSection().currentLine(); /* * Encode any change in the current source line number by using @@ -536,7 +545,7 @@ bool BytecodeEmitter::updateLineNumberNotes(uint32_t offset) { * unsigned delta_ wrap to a very large number, which triggers a * SRC_SETLINE. */ - setCurrentLine(line); + bytecodeSection().setCurrentLine(line); if (delta >= LengthOfSetLine(line)) { if (!newSrcNote2(SRC_SETLINE, ptrdiff_t(line))) { return false; @@ -549,7 +558,7 @@ bool BytecodeEmitter::updateLineNumberNotes(uint32_t offset) { } while (--delta != 0); } - updateSeparatorPosition(); + bytecodeSection().updateSeparatorPositionIfPresent(); } return true; } @@ -566,7 +575,8 @@ bool BytecodeEmitter::updateSourceCoordNotes(uint32_t offset) { } uint32_t columnIndex = parser->errorReporter().columnAt(offset); - ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(lastColumn_); + ptrdiff_t colspan = + ptrdiff_t(columnIndex) - ptrdiff_t(bytecodeSection().lastColumn()); if (colspan != 0) { // If the column span is so large that we can't store it, then just // discard this information. This can happen with minimized or otherwise @@ -579,20 +589,12 @@ bool BytecodeEmitter::updateSourceCoordNotes(uint32_t offset) { if (!newSrcNote2(SRC_COLSPAN, SN_COLSPAN_TO_OFFSET(colspan))) { return false; } - lastColumn_ = columnIndex; - updateSeparatorPosition(); + bytecodeSection().setLastColumn(columnIndex); + bytecodeSection().updateSeparatorPositionIfPresent(); } return true; } -/* Updates the last separator position, if present */ -void BytecodeEmitter::updateSeparatorPosition() { - if (!inPrologue() && lastSeparatorOffet_ == code().length()) { - lastSeparatorLine_ = currentLine_; - lastSeparatorColumn_ = lastColumn_; - } -} - Maybe BytecodeEmitter::getOffsetForLoop(ParseNode* nextpn) { if (!nextpn) { return Nothing(); @@ -626,7 +628,7 @@ bool BytecodeEmitter::emitUint32Operand(JSOp op, uint32_t operand) { if (!emitN(op, 4, &off)) { return false; } - SET_UINT32(code(off), operand); + SET_UINT32(bytecodeSection().code(off), operand); return true; } @@ -663,17 +665,18 @@ class NonLocalExitControl { public: NonLocalExitControl(BytecodeEmitter* bce, Kind kind) : bce_(bce), - savedScopeNoteIndex_(bce->scopeNoteList.length()), - savedDepth_(bce->stackDepth), + savedScopeNoteIndex_(bce->bytecodeSection().scopeNoteList().length()), + savedDepth_(bce->bytecodeSection().stackDepth()), openScopeNoteIndex_(bce->innermostEmitterScope()->noteIndex()), kind_(kind) {} ~NonLocalExitControl() { - for (uint32_t n = savedScopeNoteIndex_; n < bce_->scopeNoteList.length(); - n++) { - bce_->scopeNoteList.recordEnd(n, bce_->offset()); + for (uint32_t n = savedScopeNoteIndex_; + n < bce_->bytecodeSection().scopeNoteList().length(); n++) { + bce_->bytecodeSection().scopeNoteList().recordEnd( + n, bce_->bytecodeSection().offset()); } - bce_->stackDepth = savedDepth_; + bce_->bytecodeSection().setStackDepth(savedDepth_); } MOZ_MUST_USE bool prepareForNonLocalJump(NestableControl* target); @@ -695,11 +698,12 @@ bool NonLocalExitControl::leaveScope(EmitterScope* es) { if (es->enclosingInFrame()) { enclosingScopeIndex = es->enclosingInFrame()->index(); } - if (!bce_->scopeNoteList.append(enclosingScopeIndex, bce_->offset(), - openScopeNoteIndex_)) { + if (!bce_->bytecodeSection().scopeNoteList().append( + enclosingScopeIndex, bce_->bytecodeSection().offset(), + openScopeNoteIndex_)) { return false; } - openScopeNoteIndex_ = bce_->scopeNoteList.length() - 1; + openScopeNoteIndex_ = bce_->bytecodeSection().scopeNoteList().length() - 1; return true; } @@ -838,7 +842,7 @@ bool NonLocalExitControl::prepareForNonLocalJump(NestableControl* target) { } // Close FOR_OF_ITERCLOSE trynotes. - ptrdiff_t end = bce_->offset(); + ptrdiff_t end = bce_->bytecodeSection().offset(); for (ptrdiff_t start : forOfIterCloseScopeStarts) { if (!bce_->addTryNote(JSTRY_FOR_OF_ITERCLOSE, 0, start, end)) { return false; @@ -884,10 +888,10 @@ bool BytecodeEmitter::emitIndex32(JSOp op, uint32_t index) { return false; } - jsbytecode* code = this->code(offset); + jsbytecode* code = bytecodeSection().code(offset); code[0] = jsbytecode(op); SET_UINT32_INDEX(code, index); - updateDepth(offset); + bytecodeSection().updateDepth(offset); return true; } @@ -902,10 +906,10 @@ bool BytecodeEmitter::emitIndexOp(JSOp op, uint32_t index) { return false; } - jsbytecode* code = this->code(offset); + jsbytecode* code = bytecodeSection().code(offset); code[0] = jsbytecode(op); SET_UINT32_INDEX(code, index); - updateDepth(offset); + bytecodeSection().updateDepth(offset); return true; } @@ -940,24 +944,24 @@ bool BytecodeEmitter::emitAtomOp(uint32_t atomIndex, JSOp op) { bool BytecodeEmitter::emitInternedScopeOp(uint32_t index, JSOp op) { MOZ_ASSERT(JOF_OPTYPE(op) == JOF_SCOPE); - MOZ_ASSERT(index < scopeList.length()); + MOZ_ASSERT(index < perScriptData().scopeList().length()); return emitIndex32(op, index); } bool BytecodeEmitter::emitInternedObjectOp(uint32_t index, JSOp op) { MOZ_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT); - MOZ_ASSERT(index < objectList.length); + MOZ_ASSERT(index < perScriptData().objectList().length); return emitIndex32(op, index); } bool BytecodeEmitter::emitObjectOp(ObjectBox* objbox, JSOp op) { - return emitInternedObjectOp(objectList.add(objbox), op); + return emitInternedObjectOp(perScriptData().objectList().add(objbox), op); } bool BytecodeEmitter::emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op) { - uint32_t index = objectList.add(objbox1); - objectList.add(objbox2); + uint32_t index = perScriptData().objectList().add(objbox1); + perScriptData().objectList().add(objbox2); return emitInternedObjectOp(index, op); } @@ -974,7 +978,7 @@ bool BytecodeEmitter::emitLocalOp(JSOp op, uint32_t slot) { return false; } - SET_LOCALNO(code(off), slot); + SET_LOCALNO(bytecodeSection().code(off), slot); return true; } @@ -985,7 +989,7 @@ bool BytecodeEmitter::emitArgOp(JSOp op, uint16_t slot) { return false; } - SET_ARGNO(code(off), slot); + SET_ARGNO(bytecodeSection().code(off), slot); return true; } @@ -1000,7 +1004,7 @@ bool BytecodeEmitter::emitEnvCoordOp(JSOp op, EnvironmentCoordinate ec) { return false; } - jsbytecode* pc = code(off); + jsbytecode* pc = bytecodeSection().code(off); SET_ENVCOORD_HOPS(pc, ec.hops()); pc += ENVCOORD_HOPS_LEN; SET_ENVCOORD_SLOT(pc, ec.slot()); @@ -1680,13 +1684,13 @@ bool BytecodeEmitter::emitNewInit() { return false; } - jsbytecode* code = this->code(offset); + jsbytecode* code = bytecodeSection().code(offset); code[0] = JSOP_NEWINIT; code[1] = 0; code[2] = 0; code[3] = 0; code[4] = 0; - updateDepth(offset); + bytecodeSection().updateDepth(offset); return true; } @@ -1716,7 +1720,7 @@ bool BytecodeEmitter::iteratorResultShape(unsigned* shape) { return false; } - *shape = objectList.add(objbox); + *shape = perScriptData().objectList().add(objbox); return true; } @@ -2017,10 +2021,10 @@ bool BytecodeEmitter::emitDouble(double d) { return false; } - jsbytecode* code = this->code(offset); + jsbytecode* code = bytecodeSection().code(offset); code[0] = jsbytecode(JSOP_DOUBLE); SET_INLINE_VALUE(code, DoubleValue(d)); - updateDepth(offset); + bytecodeSection().updateDepth(offset); return true; } @@ -2047,13 +2051,13 @@ bool BytecodeEmitter::emitNumberOp(double dval) { if (!emitN(JSOP_UINT24, 3, &off)) { return false; } - SET_UINT24(code(off), u); + SET_UINT24(bytecodeSection().code(off), u); } else { ptrdiff_t off; if (!emitN(JSOP_INT32, 4, &off)) { return false; } - SET_INT32(code(off), ival); + SET_INT32(bytecodeSection().code(off), ival); } return true; } @@ -2254,13 +2258,13 @@ bool BytecodeEmitter::allocateResumeIndex(ptrdiff_t offset, "resumeIndex * sizeof(uintptr_t) must fit in an int32. JIT code relies " "on this when loading resume entries from BaselineScript"); - *resumeIndex = resumeOffsetList.length(); + *resumeIndex = bytecodeSection().resumeOffsetList().length(); if (*resumeIndex > MaxResumeIndex) { reportError(nullptr, JSMSG_TOO_MANY_RESUME_INDEXES); return false; } - return resumeOffsetList.append(offset); + return bytecodeSection().resumeOffsetList().append(offset); } bool BytecodeEmitter::allocateResumeIndexRange(mozilla::Span offsets, @@ -2293,15 +2297,15 @@ bool BytecodeEmitter::emitYieldOp(JSOp op) { } if (op == JSOP_INITIALYIELD || op == JSOP_YIELD) { - numYields++; + bytecodeSection().addNumYields(); } uint32_t resumeIndex; - if (!allocateResumeIndex(offset(), &resumeIndex)) { + if (!allocateResumeIndex(bytecodeSection().offset(), &resumeIndex)) { return false; } - SET_RESUMEINDEX(code(off), resumeIndex); + SET_RESUMEINDEX(bytecodeSection().code(off), resumeIndex); return emit1(JSOP_DEBUGAFTERYIELD); } @@ -2569,7 +2573,7 @@ bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, } #ifdef DEBUG - int depth = stackDepth; + int depth = bytecodeSection().stackDepth(); #endif switch (target->getKind()) { @@ -2647,7 +2651,7 @@ bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, MOZ_CRASH("emitDestructuringLHSRef: bad lhs kind"); } - MOZ_ASSERT(stackDepth == depth + int(*emitted)); + MOZ_ASSERT(bytecodeSection().stackDepth() == depth + int(*emitted)); return true; } @@ -2810,7 +2814,7 @@ bool BytecodeEmitter::emitIteratorNext( "can run user-modifiable iteration code"); // [stack] ... NEXT ITER - MOZ_ASSERT(this->stackDepth >= 2); + MOZ_ASSERT(bytecodeSection().stackDepth() >= 2); if (!emitCall(JSOP_CALL, 0, callSourceCoordOffset)) { // [stack] ... RESULT @@ -2833,7 +2837,7 @@ bool BytecodeEmitter::emitIteratorNext( bool BytecodeEmitter::emitPushNotUndefinedOrNull() { // [stack] V - MOZ_ASSERT(this->stackDepth > 0); + MOZ_ASSERT(bytecodeSection().stackDepth() > 0); if (!emit1(JSOP_DUP)) { // [stack] V V @@ -3081,7 +3085,7 @@ bool BytecodeEmitter::emitIteratorCloseInScope( template bool BytecodeEmitter::wrapWithDestructuringTryNote(int32_t iterDepth, InnerEmitter emitter) { - MOZ_ASSERT(this->stackDepth >= iterDepth); + MOZ_ASSERT(bytecodeSection().stackDepth() >= iterDepth); // Pad a nop at the beginning of the bytecode covered by the trynote so // that when unwinding environments, we may unwind to the scope @@ -3092,11 +3096,11 @@ bool BytecodeEmitter::wrapWithDestructuringTryNote(int32_t iterDepth, return false; } - ptrdiff_t start = offset(); + ptrdiff_t start = bytecodeSection().offset(); if (!emitter(this)) { return false; } - ptrdiff_t end = offset(); + ptrdiff_t end = bytecodeSection().offset(); if (start != end) { return addTryNote(JSTRY_DESTRUCTURING, iterDepth, start, end); } @@ -3202,7 +3206,7 @@ bool BytecodeEmitter::emitInitializer(ParseNode* initializer, bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, DestructuringFlavor flav) { MOZ_ASSERT(pattern->isKind(ParseNodeKind::ArrayExpr)); - MOZ_ASSERT(this->stackDepth != 0); + MOZ_ASSERT(bytecodeSection().stackDepth() != 0); // Here's pseudo code for |let [a, b, , c=y, ...d] = x;| // @@ -3326,7 +3330,7 @@ bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, // JSTRY_DESTRUCTURING expects the iterator and the done value // to be the second to top and the top of the stack, respectively. // IteratorClose is called upon exception only if done is false. - int32_t tryNoteDepth = stackDepth; + int32_t tryNoteDepth = bytecodeSection().stackDepth(); for (ParseNode* member : pattern->contents()) { bool isFirst = member == pattern->head(); @@ -3635,7 +3639,7 @@ bool BytecodeEmitter::emitDestructuringOpsObject(ListNode* pattern, MOZ_ASSERT(pattern->isKind(ParseNodeKind::ObjectExpr)); // [stack] ... RHS - MOZ_ASSERT(this->stackDepth > 0); + MOZ_ASSERT(bytecodeSection().stackDepth() > 0); if (!emit1(JSOP_CHECKOBJCOERCIBLE)) { // [stack] ... RHS @@ -3815,7 +3819,7 @@ bool BytecodeEmitter::emitDestructuringObjRestExclusionSet(ListNode* pattern) { MOZ_ASSERT(pattern->isKind(ParseNodeKind::ObjectExpr)); MOZ_ASSERT(pattern->last()->isKind(ParseNodeKind::Spread)); - ptrdiff_t offset = this->offset(); + ptrdiff_t offset = bytecodeSection().offset(); if (!emitNewInit()) { return false; } @@ -4690,11 +4694,11 @@ MOZ_MUST_USE bool BytecodeEmitter::emitGoSub(JumpList* jump) { } uint32_t resumeIndex; - if (!allocateResumeIndex(offset(), &resumeIndex)) { + if (!allocateResumeIndex(bytecodeSection().offset(), &resumeIndex)) { return false; } - SET_RESUMEINDEX(code(off), resumeIndex); + SET_RESUMEINDEX(bytecodeSection().code(off), resumeIndex); return true; } @@ -4899,7 +4903,7 @@ bool BytecodeEmitter::emitWith(BinaryNode* withNode) { } bool BytecodeEmitter::emitCopyDataProperties(CopyOption option) { - DebugOnly depth = this->stackDepth; + DebugOnly depth = bytecodeSection().stackDepth(); uint32_t argc; if (option == CopyOption::Filtered) { @@ -4954,15 +4958,15 @@ bool BytecodeEmitter::emitCopyDataProperties(CopyOption option) { return false; } - MOZ_ASSERT(depth - int(argc) == this->stackDepth); + MOZ_ASSERT(depth - int(argc) == bytecodeSection().stackDepth()); return true; } bool BytecodeEmitter::emitBigIntOp(BigInt* bigint) { - if (!numberList.append(BigIntValue(bigint))) { + if (!perScriptData().numberList().append(BigIntValue(bigint))) { return false; } - return emitIndex32(JSOP_BIGINT, numberList.length() - 1); + return emitIndex32(JSOP_BIGINT, perScriptData().numberList().length() - 1); } bool BytecodeEmitter::emitIterator() { @@ -5145,11 +5149,11 @@ bool BytecodeEmitter::emitSpread(bool allowSelfHosted) { // when we reach this point on the loop backedge (if spreading produces at // least one value), we've additionally pushed a RESULT iteration value. // Increment manually to reflect this. - this->stackDepth++; + bytecodeSection().setStackDepth(bytecodeSection().stackDepth() + 1); { #ifdef DEBUG - auto loopDepth = this->stackDepth; + auto loopDepth = bytecodeSection().stackDepth(); #endif // Emit code to assign result.value to the iteration variable. @@ -5162,7 +5166,7 @@ bool BytecodeEmitter::emitSpread(bool allowSelfHosted) { return false; } - MOZ_ASSERT(this->stackDepth == loopDepth - 1); + MOZ_ASSERT(bytecodeSection().stackDepth() == loopDepth - 1); // Spread operations can't contain |continue|, so don't bother setting loop // and enclosing "update" offsets, as we do with for-loops. @@ -5199,7 +5203,7 @@ bool BytecodeEmitter::emitSpread(bool allowSelfHosted) { return false; } - MOZ_ASSERT(this->stackDepth == loopDepth); + MOZ_ASSERT(bytecodeSection().stackDepth() == loopDepth); } // Let Ion know where the closing jump of this loop is. @@ -5212,8 +5216,8 @@ bool BytecodeEmitter::emitSpread(bool allowSelfHosted) { MOZ_ASSERT(loopInfo.breaks.offset == -1); MOZ_ASSERT(loopInfo.continues.offset == -1); - if (!addTryNote(JSTRY_FOR_OF, stackDepth, loopInfo.headOffset(), - loopInfo.breakTargetOffset())) { + if (!addTryNote(JSTRY_FOR_OF, bytecodeSection().stackDepth(), + loopInfo.headOffset(), loopInfo.breakTargetOffset())) { return false; } @@ -5234,7 +5238,7 @@ bool BytecodeEmitter::emitInitializeForInOrOfTarget(TernaryNode* forHead) { MOZ_ASSERT(forHead->isKind(ParseNodeKind::ForIn) || forHead->isKind(ParseNodeKind::ForOf)); - MOZ_ASSERT(this->stackDepth >= 1, + MOZ_ASSERT(bytecodeSection().stackDepth() >= 1, "must have a per-iteration value for initializing"); ParseNode* target = forHead->kid1(); @@ -5282,14 +5286,14 @@ bool BytecodeEmitter::emitInitializeForInOrOfTarget(TernaryNode* forHead) { // iteration value *before* initializing. Thus the initializing // value may be buried under a bind-specific value on the stack. // Swap it to the top of the stack. - MOZ_ASSERT(stackDepth >= 2); + MOZ_ASSERT(bytecodeSection().stackDepth() >= 2); if (!emit1(JSOP_SWAP)) { return false; } } else { // In cases of emitting a frame slot or environment slot, // nothing needs be done. - MOZ_ASSERT(stackDepth >= 1); + MOZ_ASSERT(bytecodeSection().stackDepth() >= 1); } if (!noe.emitAssignment()) { return false; @@ -5967,7 +5971,7 @@ bool BytecodeEmitter::emitReturn(UnaryNode* returnNode) { * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an * extra JSOP_RETRVAL after the fixups. */ - ptrdiff_t top = offset(); + ptrdiff_t top = bytecodeSection().offset(); bool needsFinalYield = sc->isFunctionBox() && sc->asFunctionBox()->needsFinalYield(); @@ -6028,12 +6032,13 @@ bool BytecodeEmitter::emitReturn(UnaryNode* returnNode) { return false; } } else if (isDerivedClassConstructor) { - MOZ_ASSERT(code()[top] == JSOP_SETRVAL); + MOZ_ASSERT(bytecodeSection().code()[top] == JSOP_SETRVAL); if (!emit1(JSOP_RETRVAL)) { return false; } - } else if (top + static_cast(JSOP_RETURN_LENGTH) != offset()) { - code()[top] = JSOP_SETRVAL; + } else if (top + static_cast(JSOP_RETURN_LENGTH) != + bytecodeSection().offset()) { + bytecodeSection().code()[top] = JSOP_SETRVAL; if (!emit1(JSOP_RETRVAL)) { return false; } @@ -6213,7 +6218,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { } int32_t savedDepthTemp; - int32_t startDepth = stackDepth; + int32_t startDepth = bytecodeSection().stackDepth(); MOZ_ASSERT(startDepth >= 3); TryEmitter tryCatch(this, TryEmitter::Kind::TryCatchFinally, @@ -6245,7 +6250,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { return false; } - MOZ_ASSERT(this->stackDepth == startDepth); + MOZ_ASSERT(bytecodeSection().stackDepth() == startDepth); // Step 7.a.vi. // Step 7.b.ii.7. @@ -6278,7 +6283,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { return false; } - MOZ_ASSERT(stackDepth == startDepth); + MOZ_ASSERT(bytecodeSection().stackDepth() == startDepth); if (!emit1(JSOP_EXCEPTION)) { // [stack] NEXT ITER RESULT EXCEPTION @@ -6297,7 +6302,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { return false; } - savedDepthTemp = stackDepth; + savedDepthTemp = bytecodeSection().stackDepth(); InternalIfEmitter ifThrowMethodIsNotDefined(this); if (!emitPushNotUndefinedOrNull()) { // [stack] NEXT ITER RESULT EXCEPTION ITER THROW @@ -6348,7 +6353,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { // [stack] NEXT ITER RESULT return false; } - MOZ_ASSERT(this->stackDepth == startDepth); + MOZ_ASSERT(bytecodeSection().stackDepth() == startDepth); JumpList checkResult; // Note that there is no GOSUB to the finally block here. If the iterator has @@ -6359,7 +6364,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { return false; } - stackDepth = savedDepthTemp; + bytecodeSection().setStackDepth(savedDepthTemp); if (!ifThrowMethodIsNotDefined.emitElse()) { // [stack] NEXT ITER RESULT EXCEPTION ITER THROW return false; @@ -6384,12 +6389,12 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { return false; } - stackDepth = savedDepthTemp; + bytecodeSection().setStackDepth(savedDepthTemp); if (!ifThrowMethodIsNotDefined.emitEnd()) { return false; } - stackDepth = startDepth; + bytecodeSection().setStackDepth(startDepth); if (!tryCatch.emitFinally()) { return false; } @@ -6516,7 +6521,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { // [stack] NEXT ITER OLDRESULT FTYPE FVALUE return false; } - savedDepthTemp = this->stackDepth; + savedDepthTemp = bytecodeSection().stackDepth(); if (!ifReturnDone.emitElse()) { // [stack] NEXT ITER OLDRESULT FTYPE FVALUE RESULT return false; @@ -6538,7 +6543,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { return false; } } - this->stackDepth = savedDepthTemp; + bytecodeSection().setStackDepth(savedDepthTemp); if (!ifReturnDone.emitEnd()) { return false; } @@ -6613,7 +6618,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { // [stack] NEXT ITER RESULT return false; } - MOZ_ASSERT(this->stackDepth == startDepth); + MOZ_ASSERT(bytecodeSection().stackDepth() == startDepth); // Steps 7.a.iv-v. // Steps 7.b.ii.5-6. @@ -6658,7 +6663,7 @@ bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { return false; } - MOZ_ASSERT(this->stackDepth == startDepth - 2); + MOZ_ASSERT(bytecodeSection().stackDepth() == startDepth - 2); return true; } @@ -6707,7 +6712,7 @@ bool BytecodeEmitter::emitExpressionStatement(UnaryNode* exprStmt) { if (innermostNestableControl && innermostNestableControl->is() && innermostNestableControl->as().startOffset() >= - offset()) { + bytecodeSection().offset()) { useful = true; } } @@ -8182,8 +8187,8 @@ bool BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH, "newinit and newobject must have equal length to edit in-place"); - uint32_t index = objectList.add(objbox); - jsbytecode* code = this->code(offset); + uint32_t index = perScriptData().objectList().add(objbox); + jsbytecode* code = bytecodeSection().code(offset); MOZ_ASSERT(code[0] == JSOP_NEWINIT); code[0] = JSOP_NEWOBJECT; @@ -9233,7 +9238,8 @@ bool BytecodeEmitter::emitTree( break; case ParseNodeKind::RegExpExpr: - if (!emitRegExp(objectList.add(pn->as().objbox()))) { + if (!emitRegExp(perScriptData().objectList().add( + pn->as().objbox()))) { return false; } break; @@ -9334,11 +9340,13 @@ static bool AllocSrcNote(JSContext* cx, SrcNotesVector& notes, bool BytecodeEmitter::addTryNote(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end) { MOZ_ASSERT(!inPrologue()); - return tryNoteList.append(kind, stackDepth, start, end); + return bytecodeSection().tryNoteList().append(kind, stackDepth, start, end); } bool BytecodeEmitter::newSrcNote(SrcNoteType type, unsigned* indexp) { - SrcNotesVector& notes = this->notes(); + // Prologue shouldn't have source notes. + MOZ_ASSERT(!inPrologue()); + SrcNotesVector& notes = bytecodeSection().notes(); unsigned index; if (!AllocSrcNote(cx, notes, &index)) { return false; @@ -9348,9 +9356,9 @@ bool BytecodeEmitter::newSrcNote(SrcNoteType type, unsigned* indexp) { * Compute delta from the last annotated bytecode's offset. If it's too * big to fit in sn, allocate one or more xdelta notes and reset sn. */ - ptrdiff_t offset = this->offset(); - ptrdiff_t delta = offset - lastNoteOffset(); - lastNoteOffset_ = offset; + ptrdiff_t offset = bytecodeSection().offset(); + ptrdiff_t delta = offset - bytecodeSection().lastNoteOffset(); + bytecodeSection().setLastNoteOffset(offset); if (delta >= SN_DELTA_LIMIT) { do { ptrdiff_t xdelta = Min(delta, SN_XDELTA_MASK); @@ -9420,7 +9428,7 @@ bool BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, return false; } - SrcNotesVector& notes = this->notes(); + SrcNotesVector& notes = bytecodeSection().notes(); /* Find the offset numbered which (i.e., skip exactly which offsets). */ jssrcnote* sn = ¬es[index]; @@ -9458,10 +9466,10 @@ bool BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, } void BytecodeEmitter::copySrcNotes(jssrcnote* destination, uint32_t nsrcnotes) { - unsigned count = notes_.length(); + unsigned count = bytecodeSection().notes().length(); // nsrcnotes includes SN_MAKE_TERMINATOR in addition to the srcnotes. MOZ_ASSERT(nsrcnotes == count + 1); - PodCopy(destination, notes_.begin(), count); + PodCopy(destination, bytecodeSection().notes().begin(), count); SN_MAKE_TERMINATOR(&destination[count]); } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 7d4c9e93d2f9..83cd4a7fb6dd 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -124,29 +124,249 @@ struct MOZ_STACK_CLASS BytecodeEmitter { Rooted lazyScript; private: - BytecodeVector code_; /* bytecode */ - SrcNotesVector notes_; /* source notes, see below */ + // Bytecode and all data directly associated with specific opcode/index inside + // bytecode is stored in this class. + class BytecodeSection { + public: + BytecodeSection(JSContext* cx, uint32_t lineNum); - // Code offset for last source note - ptrdiff_t lastNoteOffset_ = 0; + // ---- Bytecode ---- - // Line number for srcnotes. - // - // WARNING: If this becomes out of sync with already-emitted srcnotes, - // we can get undefined behavior. - uint32_t currentLine_ = 0; + BytecodeVector& code() { return code_; } + const BytecodeVector& code() const { return code_; } - // Zero-based column index on currentLine of last SRC_COLSPAN-annotated - // opcode. - // - // WARNING: If this becomes out of sync with already-emitted srcnotes, - // we can get undefined behavior. - uint32_t lastColumn_ = 0; + jsbytecode* code(ptrdiff_t offset) { return code_.begin() + offset; } + ptrdiff_t offset() const { return code_.end() - code_.begin(); } - uint32_t lastSeparatorOffet_ = 0; - uint32_t lastSeparatorLine_ = 0; - uint32_t lastSeparatorColumn_ = 0; + // ---- Source notes ---- + SrcNotesVector& notes() { return notes_; } + const SrcNotesVector& notes() const { return notes_; } + + ptrdiff_t lastNoteOffset() const { return lastNoteOffset_; } + void setLastNoteOffset(ptrdiff_t offset) { lastNoteOffset_ = offset; } + + // ---- Jump ---- + + ptrdiff_t lastTargetOffset() const { return lastTarget_.offset; } + void setLastTargetOffset(ptrdiff_t offset) { lastTarget_.offset = offset; } + + // Check if the last emitted opcode is a jump target. + bool lastOpcodeIsJumpTarget() const { + return offset() - lastTarget_.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH); + } + + // JumpTarget should not be part of the emitted statement, as they can be + // aliased by multiple statements. If we included the jump target as part of + // the statement we might have issues where the enclosing statement might + // not contain all the opcodes of the enclosed statements. + ptrdiff_t lastNonJumpTargetOffset() const { + return lastOpcodeIsJumpTarget() ? lastTarget_.offset : offset(); + } + + // ---- Stack ---- + + int32_t stackDepth() const { return stackDepth_; } + void setStackDepth(int32_t depth) { stackDepth_ = depth; } + + uint32_t maxStackDepth() const { return maxStackDepth_; } + + void updateDepth(ptrdiff_t target); + + // ---- Try notes ---- + + CGTryNoteList& tryNoteList() { return tryNoteList_; }; + const CGTryNoteList& tryNoteList() const { return tryNoteList_; }; + + // ---- Scope ---- + + CGScopeNoteList& scopeNoteList() { return scopeNoteList_; }; + const CGScopeNoteList& scopeNoteList() const { return scopeNoteList_; }; + + // ---- Generator ---- + + CGResumeOffsetList& resumeOffsetList() { return resumeOffsetList_; } + const CGResumeOffsetList& resumeOffsetList() const { + return resumeOffsetList_; + } + + uint32_t numYields() const { return numYields_; } + void addNumYields() { numYields_++; } + + // ---- Line and column ---- + + uint32_t currentLine() const { return currentLine_; } + uint32_t lastColumn() const { return lastColumn_; } + void setCurrentLine(uint32_t line) { + currentLine_ = line; + lastColumn_ = 0; + } + void setLastColumn(uint32_t column) { lastColumn_ = column; } + + void updateSeparatorPosition() { + lastSeparatorOffet_ = code().length(); + lastSeparatorLine_ = currentLine_; + lastSeparatorColumn_ = lastColumn_; + } + + void updateSeparatorPositionIfPresent() { + if (lastSeparatorOffet_ == code().length()) { + lastSeparatorLine_ = currentLine_; + lastSeparatorColumn_ = lastColumn_; + } + } + + bool isDuplicateLocation() const { + return lastSeparatorLine_ == currentLine_ && + lastSeparatorColumn_ == lastColumn_; + } + + // ---- JIT ---- + + size_t numICEntries() const { return numICEntries_; } + void addNumICEntries() { numICEntries_++; } + void setNumICEntries(size_t entries) { numICEntries_ = entries; } + + uint16_t typesetCount() const { return typesetCount_; } + void addTypesetCount() { typesetCount_++; } + + private: + // ---- Bytecode ---- + + // Bytecode. + BytecodeVector code_; + + // ---- Source notes ---- + + // Source notes + SrcNotesVector notes_; + + // Code offset for last source note + ptrdiff_t lastNoteOffset_ = 0; + + // ---- Jump ---- + + // Last jump target emitted. + JumpTarget lastTarget_ = {-1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH)}; + + // ---- Stack ---- + + // Maximum number of expression stack slots so far. + uint32_t maxStackDepth_ = 0; + + // Current stack depth in script frame. + int32_t stackDepth_ = 0; + + // ---- Try notes ---- + + // List of emitted try notes. + CGTryNoteList tryNoteList_; + + // ---- Scope ---- + + // List of emitted block scope notes. + CGScopeNoteList scopeNoteList_; + + // ---- Generator ---- + + // Certain ops (yield, await, gosub) have an entry in the script's + // resumeOffsets list. This can be used to map from the op's resumeIndex to + // the bytecode offset of the next pc. This indirection makes it easy to + // resume in the JIT (because BaselineScript stores a resumeIndex => native + // code array). + CGResumeOffsetList resumeOffsetList_; + + // Number of yield instructions emitted. Does not include JSOP_AWAIT. + uint32_t numYields_ = 0; + + // ---- Line and column ---- + + // Line number for srcnotes. + // + // WARNING: If this becomes out of sync with already-emitted srcnotes, + // we can get undefined behavior. + uint32_t currentLine_; + + // Zero-based column index on currentLine_ of last SRC_COLSPAN-annotated + // opcode. + // + // WARNING: If this becomes out of sync with already-emitted srcnotes, + // we can get undefined behavior. + uint32_t lastColumn_ = 0; + + // The offset, line and column numbers of the last opcode for the + // breakpoint for step execution. + uint32_t lastSeparatorOffet_ = 0; + uint32_t lastSeparatorLine_ = 0; + uint32_t lastSeparatorColumn_ = 0; + + // ---- JIT ---- + + // Number of JOF_IC opcodes emitted. + size_t numICEntries_ = 0; + + // Number of JOF_TYPESET opcodes generated. + uint16_t typesetCount_ = 0; + }; + + BytecodeSection bytecodeSection_; + + public: + BytecodeSection& bytecodeSection() { return bytecodeSection_; } + const BytecodeSection& bytecodeSection() const { return bytecodeSection_; } + + private: + // Data that is not directly associated with specific opcode/index inside + // bytecode, but referred from bytecode is stored in this class. + class PerScriptData { + public: + explicit PerScriptData(JSContext* cx); + + MOZ_MUST_USE bool init(JSContext* cx); + + // ---- Scope ---- + + CGScopeList& scopeList() { return scopeList_; } + const CGScopeList& scopeList() const { return scopeList_; } + + // ---- Literals ---- + + CGNumberList& numberList() { return numberList_; } + const CGNumberList& numberList() const { return numberList_; } + + CGObjectList& objectList() { return objectList_; } + const CGObjectList& objectList() const { return objectList_; } + + PooledMapPtr& atomIndices() { return atomIndices_; } + const PooledMapPtr& atomIndices() const { + return atomIndices_; + } + + private: + // ---- Scope ---- + + // List of emitted scopes. + CGScopeList scopeList_; + + // ---- Literals ---- + + // List of double and bigint values used by script. + CGNumberList numberList_; + + // List of emitted objects. + CGObjectList objectList_; + + // Map from atom to index. + PooledMapPtr atomIndices_; + }; + + PerScriptData perScriptData_; + + public: + PerScriptData& perScriptData() { return perScriptData_; } + const PerScriptData& perScriptData() const { return perScriptData_; } + + private: // switchToMain sets this to the bytecode offset of the main section. mozilla::Maybe mainOffset_ = {}; @@ -154,22 +374,14 @@ struct MOZ_STACK_CLASS BytecodeEmitter { const FieldInitializers fieldInitializers_; public: - // Last jump target emitted. - JumpTarget lastTarget = {-1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH)}; - // Private storage for parser wrapper. DO NOT REFERENCE INTERNALLY. May not be // initialized. Use |parser| instead. mozilla::Maybe ep_ = {}; BCEParserHandle* parser = nullptr; - PooledMapPtr atomIndices; /* literals indexed for mapping */ unsigned firstLine = 0; /* first line, for JSScript::initFromEmitter */ uint32_t maxFixedSlots = 0; /* maximum number of fixed frame slots so far */ - uint32_t maxStackDepth = - 0; /* maximum number of expression stack slots so far */ - - int32_t stackDepth = 0; /* current stack depth in script frame */ uint32_t bodyScopeIndex = UINT32_MAX; /* index into scopeList of the body scope */ @@ -195,28 +407,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter { return innermostEmitterScope_; } - CGNumberList numberList; /* double and bigint values used by script */ - CGObjectList objectList; /* list of emitted objects */ - CGScopeList scopeList; /* list of emitted scopes */ - CGTryNoteList tryNoteList; /* list of emitted try notes */ - CGScopeNoteList scopeNoteList; /* list of emitted block scope notes */ - - // Certain ops (yield, await, gosub) have an entry in the script's - // resumeOffsets list. This can be used to map from the op's resumeIndex to - // the bytecode offset of the next pc. This indirection makes it easy to - // resume in the JIT (because BaselineScript stores a resumeIndex => native - // code array). - CGResumeOffsetList resumeOffsetList; - - // Number of JOF_IC opcodes emitted. - size_t numICEntries = 0; - - // Number of yield instructions emitted. Does not include JSOP_AWAIT. - uint32_t numYields = 0; - - // Number of JOF_TYPESET opcodes generated. - uint16_t typesetCount = 0; - // Script contains singleton initializer JSOP_OBJECT. bool hasSingletons = false; @@ -360,24 +550,26 @@ struct MOZ_STACK_CLASS BytecodeEmitter { varEmitterScope = emitterScope; } - Scope* outermostScope() const { return scopeList.vector[0]; } + Scope* outermostScope() const { + return perScriptData().scopeList().vector[0]; + } Scope* innermostScope() const; Scope* bodyScope() const { - MOZ_ASSERT(bodyScopeIndex < scopeList.length()); - return scopeList.vector[bodyScopeIndex]; + MOZ_ASSERT(bodyScopeIndex < perScriptData().scopeList().length()); + return perScriptData().scopeList().vector[bodyScopeIndex]; } MOZ_ALWAYS_INLINE MOZ_MUST_USE bool makeAtomIndex(JSAtom* atom, uint32_t* indexp) { - MOZ_ASSERT(atomIndices); - AtomIndexMap::AddPtr p = atomIndices->lookupForAdd(atom); + MOZ_ASSERT(perScriptData().atomIndices()); + AtomIndexMap::AddPtr p = perScriptData().atomIndices()->lookupForAdd(atom); if (p) { *indexp = p->value(); return true; } - uint32_t index = atomIndices->count(); - if (!atomIndices->add(p, atom, index)) { + uint32_t index = perScriptData().atomIndices()->count(); + if (!perScriptData().atomIndices()->add(p, atom, index)) { ReportOutOfMemory(cx); return false; } @@ -400,45 +592,13 @@ struct MOZ_STACK_CLASS BytecodeEmitter { void tellDebuggerAboutCompiledScript(JSContext* cx); - BytecodeVector& code() { return code_; } - const BytecodeVector& code() const { return code_; } - - jsbytecode* code(ptrdiff_t offset) { return code_.begin() + offset; } - ptrdiff_t offset() const { return code_.end() - code_.begin(); } - uint32_t mainOffset() const { return *mainOffset_; } bool inPrologue() const { return mainOffset_.isNothing(); } void switchToMain() { MOZ_ASSERT(inPrologue()); - mainOffset_.emplace(code_.length()); - } - - SrcNotesVector& notes() { - // Prologue shouldn't have source notes. - MOZ_ASSERT(!inPrologue()); - return notes_; - } - ptrdiff_t lastNoteOffset() const { return lastNoteOffset_; } - unsigned currentLine() const { return currentLine_; } - - void setCurrentLine(uint32_t line) { - currentLine_ = line; - lastColumn_ = 0; - } - - // Check if the last emitted opcode is a jump target. - bool lastOpcodeIsJumpTarget() const { - return offset() - lastTarget.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH); - } - - // JumpTarget should not be part of the emitted statement, as they can be - // aliased by multiple statements. If we included the jump target as part of - // the statement we might have issues where the enclosing statement might - // not contain all the opcodes of the enclosed statements. - ptrdiff_t lastNonJumpTargetOffset() const { - return lastOpcodeIsJumpTarget() ? lastTarget.offset : offset(); + mainOffset_.emplace(bytecodeSection().code().length()); } void setFunctionBodyEndPos(uint32_t pos) { @@ -509,12 +669,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter { MOZ_MUST_USE bool emitFunctionScript(FunctionNode* funNode, TopLevelFunction isTopLevel); - void updateDepth(ptrdiff_t target); MOZ_MUST_USE bool markStepBreakpoint(); MOZ_MUST_USE bool markSimpleBreakpoint(); MOZ_MUST_USE bool updateLineNumberNotes(uint32_t offset); MOZ_MUST_USE bool updateSourceCoordNotes(uint32_t offset); - void updateSeparatorPosition(); JSOp strictifySetNameOp(JSOp op); diff --git a/js/src/frontend/CForEmitter.cpp b/js/src/frontend/CForEmitter.cpp index e2be22fa3a86..fc6dce61297b 100644 --- a/js/src/frontend/CForEmitter.cpp +++ b/js/src/frontend/CForEmitter.cpp @@ -75,7 +75,7 @@ bool CForEmitter::emitBody(Cond cond, const Maybe& bodyPos) { return false; } - biasedTop_ = bce_->offset(); + biasedTop_ = bce_->bytecodeSection().offset(); if (cond_ == Cond::Present) { // Goto the loop condition, which branches back to iterate. @@ -163,11 +163,11 @@ bool CForEmitter::emitCond(const Maybe& forPos, // Restore the absolute line number for source note readers. if (endPos) { uint32_t lineNum = bce_->parser->errorReporter().lineAt(*endPos); - if (bce_->currentLine() != lineNum) { + if (bce_->bytecodeSection().currentLine() != lineNum) { if (!bce_->newSrcNote2(SRC_SETLINE, ptrdiff_t(lineNum))) { return false; } - bce_->setCurrentLine(lineNum); + bce_->bytecodeSection().setCurrentLine(lineNum); } } } @@ -176,7 +176,7 @@ bool CForEmitter::emitCond(const Maybe& forPos, tdzCache_.reset(); } - condOffset_ = bce_->offset(); + condOffset_ = bce_->bytecodeSection().offset(); if (cond_ == Cond::Present) { if (!loopInfo_->emitLoopEntry(bce_, condPos)) { @@ -228,7 +228,8 @@ bool CForEmitter::emitEnd() { return false; } - if (!bce_->addTryNote(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(), + if (!bce_->addTryNote(JSTRY_LOOP, bce_->bytecodeSection().stackDepth(), + loopInfo_->headOffset(), loopInfo_->breakTargetOffset())) { return false; } diff --git a/js/src/frontend/DoWhileEmitter.cpp b/js/src/frontend/DoWhileEmitter.cpp index 3a57036cf8c0..35d86b312f4d 100644 --- a/js/src/frontend/DoWhileEmitter.cpp +++ b/js/src/frontend/DoWhileEmitter.cpp @@ -75,7 +75,8 @@ bool DoWhileEmitter::emitEnd() { return false; } - if (!bce_->addTryNote(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(), + if (!bce_->addTryNote(JSTRY_LOOP, bce_->bytecodeSection().stackDepth(), + loopInfo_->headOffset(), loopInfo_->breakTargetOffset())) { return false; } diff --git a/js/src/frontend/EmitterScope.cpp b/js/src/frontend/EmitterScope.cpp index 7c31ab46438e..6f0bf4bb0ea4 100644 --- a/js/src/frontend/EmitterScope.cpp +++ b/js/src/frontend/EmitterScope.cpp @@ -342,8 +342,8 @@ bool EmitterScope::internScope(BytecodeEmitter* bce, ScopeCreator createScope) { return false; } hasEnvironment_ = scope->hasEnvironment(); - scopeIndex_ = bce->scopeList.length(); - return bce->scopeList.append(scope); + scopeIndex_ = bce->perScriptData().scopeList().length(); + return bce->perScriptData().scopeList().append(scope); } template @@ -351,18 +351,18 @@ bool EmitterScope::internBodyScope(BytecodeEmitter* bce, ScopeCreator createScope) { MOZ_ASSERT(bce->bodyScopeIndex == UINT32_MAX, "There can be only one body scope"); - bce->bodyScopeIndex = bce->scopeList.length(); + bce->bodyScopeIndex = bce->perScriptData().scopeList().length(); return internScope(bce, createScope); } bool EmitterScope::appendScopeNote(BytecodeEmitter* bce) { MOZ_ASSERT(ScopeKindIsInBody(scope(bce)->kind()) && enclosingInFrame(), "Scope notes are not needed for body-level scopes."); - noteIndex_ = bce->scopeNoteList.length(); - return bce->scopeNoteList.append(index(), bce->offset(), - enclosingInFrame() - ? enclosingInFrame()->noteIndex() - : ScopeNote::NoScopeNoteIndex); + noteIndex_ = bce->bytecodeSection().scopeNoteList().length(); + return bce->bytecodeSection().scopeNoteList().append( + index(), bce->bytecodeSection().offset(), + enclosingInFrame() ? enclosingInFrame()->noteIndex() + : ScopeNote::NoScopeNoteIndex); } bool EmitterScope::deadZoneFrameSlotRange(BytecodeEmitter* bce, @@ -1057,9 +1057,10 @@ bool EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal) { if (ScopeKindIsInBody(kind)) { // The extra function var scope is never popped once it's pushed, // so its scope note extends until the end of any possible code. - uint32_t offset = - kind == ScopeKind::FunctionBodyVar ? UINT32_MAX : bce->offset(); - bce->scopeNoteList.recordEnd(noteIndex_, offset); + uint32_t offset = kind == ScopeKind::FunctionBodyVar + ? UINT32_MAX + : bce->bytecodeSection().offset(); + bce->bytecodeSection().scopeNoteList().recordEnd(noteIndex_, offset); } } @@ -1067,7 +1068,7 @@ bool EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal) { } Scope* EmitterScope::scope(const BytecodeEmitter* bce) const { - return bce->scopeList.vector[index()]; + return bce->perScriptData().scopeList().vector[index()]; } NameLocation EmitterScope::lookup(BytecodeEmitter* bce, JSAtom* name) { diff --git a/js/src/frontend/ExpressionStatementEmitter.cpp b/js/src/frontend/ExpressionStatementEmitter.cpp index ef70db1beccf..c786f3486091 100644 --- a/js/src/frontend/ExpressionStatementEmitter.cpp +++ b/js/src/frontend/ExpressionStatementEmitter.cpp @@ -29,7 +29,7 @@ bool ExpressionStatementEmitter::prepareForExpr( } #ifdef DEBUG - depth_ = bce_->stackDepth; + depth_ = bce_->bytecodeSection().stackDepth(); state_ = State::Expr; #endif return true; @@ -37,7 +37,7 @@ bool ExpressionStatementEmitter::prepareForExpr( bool ExpressionStatementEmitter::emitEnd() { MOZ_ASSERT(state_ == State::Expr); - MOZ_ASSERT(bce_->stackDepth == depth_ + 1); + MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == depth_ + 1); // [stack] VAL diff --git a/js/src/frontend/ForInEmitter.cpp b/js/src/frontend/ForInEmitter.cpp index 9ca13747b9a3..627fa668b5b5 100644 --- a/js/src/frontend/ForInEmitter.cpp +++ b/js/src/frontend/ForInEmitter.cpp @@ -94,7 +94,7 @@ bool ForInEmitter::emitInitialize() { } #ifdef DEBUG - loopDepth_ = bce_->stackDepth; + loopDepth_ = bce_->bytecodeSection().stackDepth(); #endif MOZ_ASSERT(loopDepth_ >= 2); @@ -112,7 +112,7 @@ bool ForInEmitter::emitInitialize() { bool ForInEmitter::emitBody() { MOZ_ASSERT(state_ == State::Initialize); - MOZ_ASSERT(bce_->stackDepth == loopDepth_, + MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_, "iterator and iterval must be left on the stack"); #ifdef DEBUG @@ -124,7 +124,7 @@ bool ForInEmitter::emitBody() { bool ForInEmitter::emitEnd(const Maybe& forPos) { MOZ_ASSERT(state_ == State::Body); - loopInfo_->setContinueTarget(bce_->offset()); + loopInfo_->setContinueTarget(bce_->bytecodeSection().offset()); if (forPos) { // Make sure this code is attributed to the "for". @@ -171,8 +171,9 @@ bool ForInEmitter::emitEnd(const Maybe& forPos) { return false; } - if (!bce_->addTryNote(JSTRY_FOR_IN, bce_->stackDepth, loopInfo_->headOffset(), - bce_->offset())) { + if (!bce_->addTryNote(JSTRY_FOR_IN, bce_->bytecodeSection().stackDepth(), + loopInfo_->headOffset(), + bce_->bytecodeSection().offset())) { return false; } diff --git a/js/src/frontend/ForOfEmitter.cpp b/js/src/frontend/ForOfEmitter.cpp index 3efabeed38d6..dac23f9e5ba7 100644 --- a/js/src/frontend/ForOfEmitter.cpp +++ b/js/src/frontend/ForOfEmitter.cpp @@ -58,7 +58,7 @@ bool ForOfEmitter::emitInitialize(const Maybe& forPos) { } } - int32_t iterDepth = bce_->stackDepth; + int32_t iterDepth = bce_->bytecodeSection().stackDepth(); // For-of loops have the iterator next method, the iterator itself, and // the result.value on the stack. @@ -111,7 +111,7 @@ bool ForOfEmitter::emitInitialize(const Maybe& forPos) { } #ifdef DEBUG - loopDepth_ = bce_->stackDepth; + loopDepth_ = bce_->bytecodeSection().stackDepth(); #endif // Make sure this code is attributed to the "for". @@ -195,7 +195,7 @@ bool ForOfEmitter::emitInitialize(const Maybe& forPos) { bool ForOfEmitter::emitBody() { MOZ_ASSERT(state_ == State::Initialize); - MOZ_ASSERT(bce_->stackDepth == loopDepth_, + MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_, "the stack must be balanced around the initializing " "operation"); @@ -218,14 +218,14 @@ bool ForOfEmitter::emitBody() { bool ForOfEmitter::emitEnd(const Maybe& iteratedPos) { MOZ_ASSERT(state_ == State::Body); - MOZ_ASSERT(bce_->stackDepth == loopDepth_, + MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_, "the stack must be balanced around the for-of body"); if (!loopInfo_->emitEndCodeNeedingIteratorClose(bce_)) { return false; } - loopInfo_->setContinueTarget(bce_->offset()); + loopInfo_->setContinueTarget(bce_->bytecodeSection().offset()); // We use the iterated value's position to attribute JSOP_LOOPENTRY, // which corresponds to the iteration protocol. @@ -244,7 +244,7 @@ bool ForOfEmitter::emitEnd(const Maybe& iteratedPos) { return false; } - MOZ_ASSERT(bce_->stackDepth == loopDepth_); + MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_); // Let Ion know where the closing jump of this loop is. if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::ForOf::BackJumpOffset, @@ -256,7 +256,8 @@ bool ForOfEmitter::emitEnd(const Maybe& iteratedPos) { return false; } - if (!bce_->addTryNote(JSTRY_FOR_OF, bce_->stackDepth, loopInfo_->headOffset(), + if (!bce_->addTryNote(JSTRY_FOR_OF, bce_->bytecodeSection().stackDepth(), + loopInfo_->headOffset(), loopInfo_->breakTargetOffset())) { return false; } diff --git a/js/src/frontend/ForOfLoopControl.cpp b/js/src/frontend/ForOfLoopControl.cpp index f8cb520ea1bd..473242e8d875 100644 --- a/js/src/frontend/ForOfLoopControl.cpp +++ b/js/src/frontend/ForOfLoopControl.cpp @@ -30,7 +30,7 @@ bool ForOfLoopControl::emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce) { } MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX); - numYieldsAtBeginCodeNeedingIterClose_ = bce->numYields; + numYieldsAtBeginCodeNeedingIterClose_ = bce->bytecodeSection().numYields(); return true; } @@ -45,7 +45,7 @@ bool ForOfLoopControl::emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce) { // [stack] ITER ... EXCEPTION return false; } - unsigned slotFromTop = bce->stackDepth - iterDepth_; + unsigned slotFromTop = bce->bytecodeSection().stackDepth() - iterDepth_; if (!bce->emitDupAt(slotFromTop)) { // [stack] ITER ... EXCEPTION ITER return false; @@ -69,7 +69,8 @@ bool ForOfLoopControl::emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce) { return false; } - MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_)); + MOZ_ASSERT(slotFromTop == + unsigned(bce->bytecodeSection().stackDepth() - iterDepth_)); if (!bce->emitDupAt(slotFromTop)) { // [stack] ITER ... EXCEPTION ITER return false; @@ -92,7 +93,7 @@ bool ForOfLoopControl::emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce) { // If any yields were emitted, then this for-of loop is inside a star // generator and must handle the case of Generator.return. Like in // yield*, it is handled with a finally block. - uint32_t numYieldsEmitted = bce->numYields; + uint32_t numYieldsEmitted = bce->bytecodeSection().numYields(); if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) { if (!tryCatch_->emitFinally()) { return false; @@ -135,12 +136,12 @@ bool ForOfLoopControl::emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce) { bool ForOfLoopControl::emitIteratorCloseInInnermostScopeWithTryNote( BytecodeEmitter* bce, CompletionKind completionKind /* = CompletionKind::Normal */) { - ptrdiff_t start = bce->offset(); + ptrdiff_t start = bce->bytecodeSection().offset(); if (!emitIteratorCloseInScope(bce, *bce->innermostEmitterScope(), completionKind)) { return false; } - ptrdiff_t end = bce->offset(); + ptrdiff_t end = bce->bytecodeSection().offset(); return bce->addTryNote(JSTRY_FOR_OF_ITERCLOSE, 0, start, end); } @@ -193,7 +194,7 @@ bool ForOfLoopControl::emitPrepareForNonLocalJumpFromScope( return false; } - *tryNoteStart = bce->offset(); + *tryNoteStart = bce->bytecodeSection().offset(); if (!emitIteratorCloseInScope(bce, currentScope, CompletionKind::Normal)) { // [stack] UNDEF return false; diff --git a/js/src/frontend/FunctionEmitter.cpp b/js/src/frontend/FunctionEmitter.cpp index 0aefbdb94854..ec1f69486cf4 100644 --- a/js/src/frontend/FunctionEmitter.cpp +++ b/js/src/frontend/FunctionEmitter.cpp @@ -220,7 +220,7 @@ bool FunctionEmitter::emitAsmJSModule() { bool FunctionEmitter::emitFunction() { // Make the function object a literal in the outer script's pool. - unsigned index = bce_->objectList.add(funbox_); + unsigned index = bce_->perScriptData().objectList().add(funbox_); // [stack] diff --git a/js/src/frontend/IfEmitter.cpp b/js/src/frontend/IfEmitter.cpp index 6925ba96cf40..f9b9f7f656dd 100644 --- a/js/src/frontend/IfEmitter.cpp +++ b/js/src/frontend/IfEmitter.cpp @@ -41,10 +41,10 @@ bool BranchEmitterBase::emitThenInternal(SrcNoteType type) { // To restore stack depth in else part, save depth of the then part. #ifdef DEBUG // If DEBUG, this is also necessary to calculate |pushed_|. - thenDepth_ = bce_->stackDepth; + thenDepth_ = bce_->bytecodeSection().stackDepth(); #else if (type == SRC_COND || type == SRC_IF_ELSE) { - thenDepth_ = bce_->stackDepth; + thenDepth_ = bce_->bytecodeSection().stackDepth(); } #endif @@ -59,10 +59,10 @@ bool BranchEmitterBase::emitThenInternal(SrcNoteType type) { void BranchEmitterBase::calculateOrCheckPushed() { #ifdef DEBUG if (!calculatedPushed_) { - pushed_ = bce_->stackDepth - thenDepth_; + pushed_ = bce_->bytecodeSection().stackDepth() - thenDepth_; calculatedPushed_ = true; } else { - MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_); + MOZ_ASSERT(pushed_ == bce_->bytecodeSection().stackDepth() - thenDepth_); } #endif } @@ -92,7 +92,7 @@ bool BranchEmitterBase::emitElseInternal() { jumpAroundThen_ = JumpList(); // Restore stack depth of the then part. - bce_->stackDepth = thenDepth_; + bce_->bytecodeSection().setStackDepth(thenDepth_); // Enclose else-branch with TDZCheckCache. if (kind_ == Kind::MayContainLexicalAccessInBranch) { diff --git a/js/src/frontend/LabelEmitter.cpp b/js/src/frontend/LabelEmitter.cpp index 79a62da171c1..30d863d6e974 100644 --- a/js/src/frontend/LabelEmitter.cpp +++ b/js/src/frontend/LabelEmitter.cpp @@ -27,7 +27,7 @@ bool LabelEmitter::emitLabel(JSAtom* name) { return false; } - controlInfo_.emplace(bce_, name, bce_->offset()); + controlInfo_.emplace(bce_, name, bce_->bytecodeSection().offset()); #ifdef DEBUG state_ = State::Label; @@ -39,8 +39,8 @@ bool LabelEmitter::emitEnd() { MOZ_ASSERT(state_ == State::Label); // Patch the JSOP_LABEL offset. - jsbytecode* labelpc = bce_->code(top_); - int32_t offset = bce_->lastNonJumpTargetOffset() - top_; + jsbytecode* labelpc = bce_->bytecodeSection().code(top_); + int32_t offset = bce_->bytecodeSection().lastNonJumpTargetOffset() - top_; MOZ_ASSERT(*labelpc == JSOP_LABEL); SET_CODE_OFFSET(labelpc, offset); diff --git a/js/src/frontend/ObjectEmitter.cpp b/js/src/frontend/ObjectEmitter.cpp index b19483eb1639..df42cee8767c 100644 --- a/js/src/frontend/ObjectEmitter.cpp +++ b/js/src/frontend/ObjectEmitter.cpp @@ -412,7 +412,7 @@ bool ObjectEmitter::emitObject(size_t propertyCount) { // Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing // a new object and defining (in source order) each property on the object // (or mutating the object's [[Prototype]], in the case of __proto__). - top_ = bce_->offset(); + top_ = bce_->bytecodeSection().offset(); if (!bce_->emitNewInit()) { // [stack] OBJ return false; diff --git a/js/src/frontend/SwitchEmitter.cpp b/js/src/frontend/SwitchEmitter.cpp index 9e62613b8491..58d17e7c1932 100644 --- a/js/src/frontend/SwitchEmitter.cpp +++ b/js/src/frontend/SwitchEmitter.cpp @@ -151,7 +151,7 @@ bool SwitchEmitter::emitCond() { // After entering the scope if necessary, push the switch control. controlInfo_.emplace(bce_, StatementKind::Switch); - top_ = bce_->offset(); + top_ = bce_->bytecodeSection().offset(); if (!caseOffsets_.resize(caseCount_)) { ReportOutOfMemory(bce_->cx); @@ -164,7 +164,7 @@ bool SwitchEmitter::emitCond() { return false; } - MOZ_ASSERT(top_ == bce_->offset()); + MOZ_ASSERT(top_ == bce_->bytecodeSection().offset()); if (!bce_->emitN(JSOP_CONDSWITCH, 0)) { return false; } @@ -181,7 +181,7 @@ bool SwitchEmitter::emitTable(const TableGenerator& tableGen) { // After entering the scope if necessary, push the switch control. controlInfo_.emplace(bce_, StatementKind::Switch); - top_ = bce_->offset(); + top_ = bce_->bytecodeSection().offset(); // The note has one offset that tells total switch code length. if (!bce_->newSrcNote2(SRC_TABLESWITCH, 0, ¬eIndex_)) { @@ -193,14 +193,14 @@ bool SwitchEmitter::emitTable(const TableGenerator& tableGen) { return false; } - MOZ_ASSERT(top_ == bce_->offset()); + MOZ_ASSERT(top_ == bce_->bytecodeSection().offset()); if (!bce_->emitN(JSOP_TABLESWITCH, JSOP_TABLESWITCH_LENGTH - sizeof(jsbytecode))) { return false; } // Skip default offset. - jsbytecode* pc = bce_->code(top_ + JUMP_OFFSET_LEN); + jsbytecode* pc = bce_->bytecodeSection().code(top_ + JUMP_OFFSET_LEN); // Fill in switch bounds, which we know fit in 16-bit offsets. SET_JUMP_OFFSET(pc, tableGen.low()); @@ -223,9 +223,9 @@ bool SwitchEmitter::emitCaseOrDefaultJump(uint32_t caseIndex, bool isDefault) { if (caseIndex > 0) { // Link the last JSOP_CASE's SRC_NEXTCASE to current JSOP_CASE for the // benefit of IonBuilder. - if (!bce_->setSrcNoteOffset(caseNoteIndex_, - SrcNote::NextCase::NextCaseOffset, - bce_->offset() - lastCaseOffset_)) { + if (!bce_->setSrcNoteOffset( + caseNoteIndex_, SrcNote::NextCase::NextCaseOffset, + bce_->bytecodeSection().offset() - lastCaseOffset_)) { return false; } } @@ -243,11 +243,12 @@ bool SwitchEmitter::emitCaseOrDefaultJump(uint32_t caseIndex, bool isDefault) { if (caseIndex == 0) { // Switch note's second offset is to first JSOP_CASE. - unsigned noteCount = bce_->notes().length(); + unsigned noteCount = bce_->bytecodeSection().notes().length(); if (!bce_->setSrcNoteOffset(noteIndex_, 1, lastCaseOffset_ - top_)) { return false; } - unsigned noteCountDelta = bce_->notes().length() - noteCount; + unsigned noteCountDelta = + bce_->bytecodeSection().notes().length() - noteCount; if (noteCountDelta != 0) { caseNoteIndex_ += noteCountDelta; } @@ -398,7 +399,7 @@ bool SwitchEmitter::emitEnd() { defaultJumpTargetOffset_); } else { // Fill in the default jump target. - pc = bce_->code(top_); + pc = bce_->bytecodeSection().code(top_); SET_JUMP_OFFSET(pc, defaultJumpTargetOffset_.offset - top_); pc += JUMP_OFFSET_LEN; } @@ -408,8 +409,9 @@ bool SwitchEmitter::emitEnd() { static_assert(unsigned(SrcNote::TableSwitch::EndOffset) == unsigned(SrcNote::CondSwitch::EndOffset), "{TableSwitch,CondSwitch}::EndOffset should be same"); - if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::TableSwitch::EndOffset, - bce_->lastNonJumpTargetOffset() - top_)) { + if (!bce_->setSrcNoteOffset( + noteIndex_, SrcNote::TableSwitch::EndOffset, + bce_->bytecodeSection().lastNonJumpTargetOffset() - top_)) { return false; } diff --git a/js/src/frontend/TryEmitter.cpp b/js/src/frontend/TryEmitter.cpp index 2de77536e139..d194c14810ca 100644 --- a/js/src/frontend/TryEmitter.cpp +++ b/js/src/frontend/TryEmitter.cpp @@ -53,7 +53,7 @@ bool TryEmitter::emitTry() { // For that we store in a try note associated with the catch or // finally block the stack depth upon the try entry. The interpreter // uses this depth to properly unwind the stack and the scope chain. - depth_ = bce_->stackDepth; + depth_ = bce_->bytecodeSection().stackDepth(); // Record the try location, then emit the try block. if (!bce_->newSrcNote(SRC_TRY, ¬eIndex_)) { @@ -62,7 +62,7 @@ bool TryEmitter::emitTry() { if (!bce_->emit1(JSOP_TRY)) { return false; } - tryStart_ = bce_->offset(); + tryStart_ = bce_->bytecodeSection().offset(); #ifdef DEBUG state_ = State::Try; @@ -72,7 +72,7 @@ bool TryEmitter::emitTry() { bool TryEmitter::emitTryEnd() { MOZ_ASSERT(state_ == State::Try); - MOZ_ASSERT(depth_ == bce_->stackDepth); + MOZ_ASSERT(depth_ == bce_->bytecodeSection().stackDepth()); // GOSUB to finally, if present. if (hasFinally() && controlInfo_) { @@ -82,8 +82,9 @@ bool TryEmitter::emitTryEnd() { } // Source note points to the jump at the end of the try block. - if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::Try::EndOfTryJumpOffset, - bce_->offset() - tryStart_ + JSOP_TRY_LENGTH)) { + if (!bce_->setSrcNoteOffset( + noteIndex_, SrcNote::Try::EndOfTryJumpOffset, + bce_->bytecodeSection().offset() - tryStart_ + JSOP_TRY_LENGTH)) { return false; } @@ -105,7 +106,7 @@ bool TryEmitter::emitCatch() { return false; } - MOZ_ASSERT(bce_->stackDepth == depth_); + MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == depth_); if (controlKind_ == ControlKind::Syntactic) { // Clear the frame's return value that might have been set by the @@ -138,7 +139,7 @@ bool TryEmitter::emitCatchEnd() { if (!bce_->emitGoSub(&controlInfo_->gosubs)) { return false; } - MOZ_ASSERT(bce_->stackDepth == depth_); + MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == depth_); // Jump over the finally block. if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_)) { @@ -177,7 +178,7 @@ bool TryEmitter::emitFinally( } } - MOZ_ASSERT(bce_->stackDepth == depth_); + MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == depth_); if (!bce_->emitJumpTarget(&finallyStart_)) { return false; @@ -253,7 +254,7 @@ bool TryEmitter::emitEnd() { } } - MOZ_ASSERT(bce_->stackDepth == depth_); + MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == depth_); // ReconstructPCStack needs a NOP here to mark the end of the last // catch block. diff --git a/js/src/frontend/WhileEmitter.cpp b/js/src/frontend/WhileEmitter.cpp index c73ef4a5da57..f76d4010641b 100644 --- a/js/src/frontend/WhileEmitter.cpp +++ b/js/src/frontend/WhileEmitter.cpp @@ -100,7 +100,8 @@ bool WhileEmitter::emitEnd() { return false; } - if (!bce_->addTryNote(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(), + if (!bce_->addTryNote(JSTRY_LOOP, bce_->bytecodeSection().stackDepth(), + loopInfo_->headOffset(), loopInfo_->breakTargetOffset())) { return false; } diff --git a/js/src/gc/Allocator.cpp b/js/src/gc/Allocator.cpp index 684a42b48b2d..b51270037235 100644 --- a/js/src/gc/Allocator.cpp +++ b/js/src/gc/Allocator.cpp @@ -7,6 +7,7 @@ #include "gc/Allocator.h" #include "mozilla/DebugOnly.h" +#include "mozilla/TimeStamp.h" #include "gc/GCInternals.h" #include "gc/GCTrace.h" @@ -22,6 +23,9 @@ #include "gc/PrivateIterators-inl.h" #include "vm/JSObject-inl.h" +using mozilla::TimeDuration; +using mozilla::TimeStamp; + using namespace js; using namespace gc; @@ -268,19 +272,16 @@ T* GCRuntime::tryNewTenuredThing(JSContext* cx, AllocKind kind, // chunks available it may also allocate new memory directly. t = reinterpret_cast(refillFreeListFromAnyThread(cx, kind)); - if (MOZ_UNLIKELY(!t && allowGC)) { - if (!cx->helperThread()) { - // We have no memory available for a new chunk; perform an - // all-compartments, non-incremental, shrinking GC and wait for - // sweeping to finish. - JS::PrepareForFullGC(cx); - cx->runtime()->gc.gc(GC_SHRINK, JS::GCReason::LAST_DITCH); - cx->runtime()->gc.waitBackgroundSweepOrAllocEnd(); - + if (MOZ_UNLIKELY(!t)) { + if (allowGC) { + cx->runtime()->gc.attemptLastDitchGC(cx); t = tryNewTenuredThing(cx, kind, thingSize); } if (!t) { - ReportOutOfMemory(cx); + if (allowGC) { + ReportOutOfMemory(cx); + } + return nullptr; } } } @@ -294,6 +295,28 @@ T* GCRuntime::tryNewTenuredThing(JSContext* cx, AllocKind kind, return t; } +void GCRuntime::attemptLastDitchGC(JSContext* cx) { + // Either there was no memory available for a new chunk or the heap hit its + // size limit. Try to perform an all-compartments, non-incremental, shrinking + // GC and wait for it to finish. + + if (cx->helperThread()) { + return; + } + + if (!lastLastDitchTime.IsNull() && + TimeStamp::Now() - lastLastDitchTime <= tunables.minLastDitchGCPeriod()) { + return; + } + + JS::PrepareForFullGC(cx); + gc(GC_SHRINK, JS::GCReason::LAST_DITCH); + waitBackgroundAllocEnd(); + waitBackgroundFreeEnd(); + + lastLastDitchTime = mozilla::TimeStamp::Now(); +} + template bool GCRuntime::checkAllocatorState(JSContext* cx, AllocKind kind) { if (allowGC) { diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index 6a0229377fce..ae17d38ce577 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -364,6 +364,9 @@ static const float PretenureThreshold = 0.6f; /* JSGC_PRETENURE_GROUP_THRESHOLD */ static const float PretenureGroupThreshold = 3000; +/* JSGC_MIN_LAST_DITCH_GC_PERIOD */ +static const TimeDuration MinLastDitchGCPeriod = TimeDuration::FromSeconds(60); + } // namespace TuningDefaults } // namespace gc } // namespace js @@ -1521,6 +1524,9 @@ bool GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, } pretenureGroupThreshold_ = value; break; + case JSGC_MIN_LAST_DITCH_GC_PERIOD: + minLastDitchGCPeriod_ = TimeDuration::FromSeconds(value); + break; default: MOZ_CRASH("Unknown GC parameter."); } @@ -1613,7 +1619,8 @@ GCSchedulingTunables::GCSchedulingTunables() nurseryFreeThresholdForIdleCollectionFraction_( TuningDefaults::NurseryFreeThresholdForIdleCollectionFraction), pretenureThreshold_(TuningDefaults::PretenureThreshold), - pretenureGroupThreshold_(TuningDefaults::PretenureGroupThreshold) {} + pretenureGroupThreshold_(TuningDefaults::PretenureGroupThreshold), + minLastDitchGCPeriod_(TuningDefaults::MinLastDitchGCPeriod) {} void GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock) { switch (key) { @@ -1708,6 +1715,9 @@ void GCSchedulingTunables::resetParameter(JSGCParamKey key, case JSGC_PRETENURE_GROUP_THRESHOLD: pretenureGroupThreshold_ = TuningDefaults::PretenureGroupThreshold; break; + case JSGC_MIN_LAST_DITCH_GC_PERIOD: + minLastDitchGCPeriod_ = TuningDefaults::MinLastDitchGCPeriod; + break; default: MOZ_CRASH("Unknown GC parameter."); } @@ -1783,6 +1793,8 @@ uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) { return uint32_t(tunables.pretenureThreshold() * 100); case JSGC_PRETENURE_GROUP_THRESHOLD: return tunables.pretenureGroupThreshold(); + case JSGC_MIN_LAST_DITCH_GC_PERIOD: + return tunables.minLastDitchGCPeriod().ToSeconds(); default: MOZ_CRASH("Unknown parameter key"); } diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 924a6002f06c..0811336803b2 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -310,8 +310,7 @@ class GCRuntime { bool isForegroundSweeping() const { return state() == State::Sweep; } bool isBackgroundSweeping() { return sweepTask.isRunning(); } void waitBackgroundSweepEnd(); - void waitBackgroundSweepOrAllocEnd() { - waitBackgroundSweepEnd(); + void waitBackgroundAllocEnd() { allocTask.cancelAndWait(); } void waitBackgroundFreeEnd(); @@ -535,6 +534,7 @@ class GCRuntime { AllocKind thingKind); static TenuredCell* refillFreeListFromHelperThread(JSContext* cx, AllocKind thingKind); + void attemptLastDitchGC(JSContext* cx); /* * Return the list of chunks that can be released outside the GC lock. @@ -1045,6 +1045,8 @@ class GCRuntime { minorGC(reason, gcstats::PhaseKind::EVICT_NURSERY); } + mozilla::TimeStamp lastLastDitchTime; + friend class MarkingValidator; friend class AutoEnterIteration; }; diff --git a/js/src/gc/Scheduling.h b/js/src/gc/Scheduling.h index 35a005353be3..a2e7eb8299c9 100644 --- a/js/src/gc/Scheduling.h +++ b/js/src/gc/Scheduling.h @@ -456,6 +456,14 @@ class GCSchedulingTunables { */ UnprotectedData pretenureGroupThreshold_; + /* + * JSGC_MIN_LAST_DITCH_GC_PERIOD + * + * Last ditch GC is skipped if allocation failure occurs less than this many + * seconds from the previous one. + */ + MainThreadData minLastDitchGCPeriod_; + public: GCSchedulingTunables(); @@ -502,6 +510,10 @@ class GCSchedulingTunables { float pretenureThreshold() const { return pretenureThreshold_; } uint32_t pretenureGroupThreshold() const { return pretenureGroupThreshold_; } + mozilla::TimeDuration minLastDitchGCPeriod() const { + return minLastDitchGCPeriod_; + } + MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock); void resetParameter(JSGCParamKey key, const AutoLockGC& lock); diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index fd8f6eed2302..0d54c6e8f8f2 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -789,8 +789,14 @@ bool js::gc::CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key, } CellColor keyColor = GetCellColor(key); - CellColor valueColor = - valueZone->isGCMarking() ? GetCellColor(value) : CellColor::Black; + + // Values belonging to other runtimes or in uncollected zones are treated as + // black. + CellColor valueColor = CellColor::Black; + if (value->runtimeFromAnyThread() == zone->runtimeFromAnyThread() && + valueZone->isGCMarking()) { + valueColor = GetCellColor(value); + } if (valueColor < Min(mapColor, keyColor)) { fprintf(stderr, "WeakMap value is less marked than map and key\n"); diff --git a/js/src/jit-test/tests/gc/bug-1505622.js b/js/src/jit-test/tests/gc/bug-1505622.js new file mode 100644 index 000000000000..c6eb07a59a19 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1505622.js @@ -0,0 +1,45 @@ +// Test that we don't repeatedly trigger last-ditch GCs. + +function allocUntilFail() { + gc(); + let initGCNumber = gcparam("gcNumber"); + let error; + try { + let a = []; + while (true) { + a.push(Symbol()); // Symbols are tenured. + } + } catch(err) { + error = err; + } + let finalGCNumber = gcparam("gcNumber"); + gc(); + assertEq(error, "out of memory"); + return finalGCNumber - initGCNumber; +} + +// Turn of any zeal which will disrupt GC number checks. +gczeal(0); + +// Set a small heap limit. +gcparam("maxBytes", 1024 * 1024); + +// Set the time limit for skipping last ditch GCs to 5 seconds. +gcparam("minLastDitchGCPeriod", 5); +assertEq(gcparam("minLastDitchGCPeriod"), 5); + +// Allocate up to the heap limit. This triggers a last ditch GC. +let gcCount = allocUntilFail(); +assertEq(gcCount, 1) + +// Allocate up to the limit again. The second time we fail without +// triggering a GC. +gcCount = allocUntilFail(); +assertEq(gcCount, 0) + +// Wait for time limit to expire. +sleep(6); + +// Check we trigger a GC again. +gcCount = allocUntilFail(); +assertEq(gcCount, 1) diff --git a/js/src/jit-test/tests/gc/bug-1543014.js b/js/src/jit-test/tests/gc/bug-1543014.js new file mode 100644 index 000000000000..9f91ad35260c --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1543014.js @@ -0,0 +1,11 @@ +// |jit-test| skip-if: helperThreadCount() === 0 +gczeal(0); +evalInWorker(` + var sym4 = Symbol.match; + function basicSweeping() {}; + var wm1 = new WeakMap(); + wm1.set(basicSweeping, sym4); + startgc(100000, 'shrinking'); +`); +gczeal(2); +var d1 = newGlobal({}); diff --git a/js/src/tests/lib/progressbar.py b/js/src/tests/lib/progressbar.py index 2ba90c4930fe..5faa2e19fefa 100644 --- a/js/src/tests/lib/progressbar.py +++ b/js/src/tests/lib/progressbar.py @@ -39,6 +39,8 @@ class ProgressBar(object): self.limit_digits = int(math.ceil(math.log10(self.limit))) # datetime: The start time. self.t0 = datetime.now() + # datetime: Optional, the last time update() ran. + self.last_update_time = None # Compute the width of the counters and build the format string. self.counters_width = 1 # [ @@ -79,7 +81,8 @@ class ProgressBar(object): sys.stdout.write(bar + '|') # Update the bar. - dt = datetime.now() - self.t0 + now = datetime.now() + dt = now - self.t0 dt = dt.seconds + dt.microseconds * 1e-6 sys.stdout.write('{:6.1f}s'.format(dt)) Terminal.clear_right() @@ -87,9 +90,13 @@ class ProgressBar(object): # Force redisplay, since we didn't write a \n. sys.stdout.flush() + self.last_update_time = now + def poke(self): if not self.prior: return + if datetime.now() - self.last_update_time < self.update_granularity(): + return self.update(*self.prior) def finish(self, complete=True): diff --git a/js/src/vm/CompilationAndEvaluation.cpp b/js/src/vm/CompilationAndEvaluation.cpp index 80bb89d76df1..d4ca5c02a03b 100644 --- a/js/src/vm/CompilationAndEvaluation.cpp +++ b/js/src/vm/CompilationAndEvaluation.cpp @@ -508,9 +508,10 @@ JS_PUBLIC_API bool JS::CloneAndExecuteScript(JSContext* cx, return ExecuteScript(cx, envChain, script, rval.address()); } +template static bool Evaluate(JSContext* cx, ScopeKind scopeKind, HandleObject env, const ReadOnlyCompileOptions& optionsArg, - SourceText& srcBuf, MutableHandleValue rval) { + SourceText& srcBuf, MutableHandleValue rval) { CompileOptions options(cx, optionsArg); MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); @@ -548,14 +549,8 @@ static bool Evaluate(JSContext* cx, HandleObjectVector envChain, extern JS_PUBLIC_API bool JS::EvaluateUtf8( JSContext* cx, const ReadOnlyCompileOptions& options, const char* bytes, size_t length, MutableHandle rval) { - auto chars = UniqueTwoByteChars( - UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get()); - if (!chars) { - return false; - } - - SourceText srcBuf; - if (!srcBuf.init(cx, std::move(chars), length)) { + SourceText srcBuf; + if (!srcBuf.init(cx, bytes, length, SourceOwnership::Borrowed)) { return false; } @@ -594,7 +589,19 @@ JS_PUBLIC_API bool JS::EvaluateUtf8Path( CompileOptions options(cx, optionsArg); options.setFileAndLine(filename, 1); - return EvaluateUtf8(cx, options, - reinterpret_cast(buffer.begin()), - buffer.length(), rval); + auto contents = reinterpret_cast(buffer.begin()); + size_t length = buffer.length(); + auto chars = UniqueTwoByteChars( + UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(contents, length), &length) + .get()); + if (!chars) { + return false; + } + + SourceText srcBuf; + if (!srcBuf.init(cx, std::move(chars), length)) { + return false; + } + + return Evaluate(cx, options, srcBuf, rval); } diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index 6c02540e09a7..18a74af6a27c 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -3238,12 +3238,12 @@ PrivateScriptData* PrivateScriptData::new_(JSContext* cx, uint32_t nscopes, /* static */ bool PrivateScriptData::InitFromEmitter( JSContext* cx, js::HandleScript script, frontend::BytecodeEmitter* bce) { - uint32_t nscopes = bce->scopeList.length(); - uint32_t nconsts = bce->numberList.length(); - uint32_t nobjects = bce->objectList.length; - uint32_t ntrynotes = bce->tryNoteList.length(); - uint32_t nscopenotes = bce->scopeNoteList.length(); - uint32_t nresumeoffsets = bce->resumeOffsetList.length(); + uint32_t nscopes = bce->perScriptData().scopeList().length(); + uint32_t nconsts = bce->perScriptData().numberList().length(); + uint32_t nobjects = bce->perScriptData().objectList().length; + uint32_t ntrynotes = bce->bytecodeSection().tryNoteList().length(); + uint32_t nscopenotes = bce->bytecodeSection().scopeNoteList().length(); + uint32_t nresumeoffsets = bce->bytecodeSection().resumeOffsetList().length(); // Create and initialize PrivateScriptData if (!JSScript::createPrivateScriptData(cx, script, nscopes, nconsts, nobjects, @@ -3254,22 +3254,22 @@ PrivateScriptData* PrivateScriptData::new_(JSContext* cx, uint32_t nscopes, js::PrivateScriptData* data = script->data_; if (nscopes) { - bce->scopeList.finish(data->scopes()); + bce->perScriptData().scopeList().finish(data->scopes()); } if (nconsts) { - bce->numberList.finish(data->consts()); + bce->perScriptData().numberList().finish(data->consts()); } if (nobjects) { - bce->objectList.finish(data->objects()); + bce->perScriptData().objectList().finish(data->objects()); } if (ntrynotes) { - bce->tryNoteList.finish(data->tryNotes()); + bce->bytecodeSection().tryNoteList().finish(data->tryNotes()); } if (nscopenotes) { - bce->scopeNoteList.finish(data->scopeNotes()); + bce->bytecodeSection().scopeNoteList().finish(data->scopeNotes()); } if (nresumeoffsets) { - bce->resumeOffsetList.finish(data->resumeOffsets()); + bce->bytecodeSection().resumeOffsetList().finish(data->resumeOffsets()); } return true; @@ -3565,11 +3565,12 @@ bool JSScript::fullyInitFromEmitter(JSContext* cx, HandleScript script, mozilla::MakeScopeExit([&] { script->freeScriptData(); }); /* The counts of indexed things must be checked during code generation. */ - MOZ_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT); - MOZ_ASSERT(bce->objectList.length <= INDEX_LIMIT); + MOZ_ASSERT(bce->perScriptData().atomIndices()->count() <= INDEX_LIMIT); + MOZ_ASSERT(bce->perScriptData().objectList().length <= INDEX_LIMIT); uint64_t nslots = - bce->maxFixedSlots + static_cast(bce->maxStackDepth); + bce->maxFixedSlots + + static_cast(bce->bytecodeSection().maxStackDepth()); if (nslots > UINT32_MAX) { bce->reportError(nullptr, JSMSG_NEED_DIET, js_script_str); return false; @@ -3581,7 +3582,7 @@ bool JSScript::fullyInitFromEmitter(JSContext* cx, HandleScript script, script->nfixed_ = bce->maxFixedSlots; script->nslots_ = nslots; script->bodyScopeIndex_ = bce->bodyScopeIndex; - script->numBytecodeTypeSets_ = bce->typesetCount; + script->numBytecodeTypeSets_ = bce->bytecodeSection().typesetCount(); // Initialize script flags from BytecodeEmitter script->setFlag(ImmutableFlags::Strict, bce->sc->strict()); @@ -3628,7 +3629,7 @@ bool JSScript::fullyInitFromEmitter(JSContext* cx, HandleScript script, // Part of the parse result – the scope containing each inner function – must // be stored in the inner function itself. Do this now that compilation is // complete and can no longer fail. - bce->objectList.finishInnerFunctions(); + bce->perScriptData().objectList().finishInnerFunctions(); #ifdef JS_STRUCTURED_SPEW // We want this to happen after line number initialization to allow filtering @@ -4522,12 +4523,12 @@ bool JSScript::hasBreakpointsAt(jsbytecode* pc) { /* static */ bool SharedScriptData::InitFromEmitter( JSContext* cx, js::HandleScript script, frontend::BytecodeEmitter* bce) { - uint32_t natoms = bce->atomIndices->count(); - uint32_t codeLength = bce->code().length(); + uint32_t natoms = bce->perScriptData().atomIndices()->count(); + uint32_t codeLength = bce->bytecodeSection().code().length(); // The + 1 is to account for the final SN_MAKE_TERMINATOR that is appended // when the notes are copied to their final destination by copySrcNotes. - uint32_t noteLength = bce->notes().length() + 1; + uint32_t noteLength = bce->bytecodeSection().notes().length() + 1; // Create and initialize SharedScriptData if (!script->createSharedScriptData(cx, codeLength, noteLength, natoms)) { @@ -4537,9 +4538,9 @@ bool JSScript::hasBreakpointsAt(jsbytecode* pc) { js::SharedScriptData* data = script->scriptData_; // Initialize trailing arrays - std::copy_n(bce->code().begin(), codeLength, data->code()); + std::copy_n(bce->bytecodeSection().code().begin(), codeLength, data->code()); bce->copySrcNotes(data->notes(), noteLength); - InitAtomMap(*bce->atomIndices, data->atoms()); + InitAtomMap(*bce->perScriptData().atomIndices(), data->atoms()); return true; } diff --git a/widget/GfxInfoX11.cpp b/widget/GfxInfoX11.cpp index beb720660d40..13777676add5 100644 --- a/widget/GfxInfoX11.cpp +++ b/widget/GfxInfoX11.cpp @@ -307,6 +307,34 @@ const nsTArray &GfxInfo::GetGfxDriverInfo() { GfxDriverInfo::allDevices, GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN, V(13, 15, 100, 1), "FEATURE_FAILURE_OLD_FGLRX", "fglrx 13.15.100.1"); + + //////////////////////////////////// + // FEATURE_WEBRENDER + + // Mesa baseline (chosen arbitrarily as that which ships with + // Ubuntu 18.04 LTS). + APPEND_TO_DRIVER_BLOCKLIST( + OperatingSystem::Linux, + (nsAString &)GfxDriverInfo::GetDeviceVendor(VendorMesaAll), + GfxDriverInfo::allDevices, nsIGfxInfo::FEATURE_WEBRENDER, + nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN, + V(18, 2, 8, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA", "Mesa 18.2.8.0"); + + // Disable on all NVIDIA devices for now. + APPEND_TO_DRIVER_BLOCKLIST( + OperatingSystem::Linux, + (nsAString &)GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), + GfxDriverInfo::allDevices, nsIGfxInfo::FEATURE_WEBRENDER, + nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED, + V(0, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_NO_LINUX_NVIDIA", ""); + + // Disable on all ATI devices for now. + APPEND_TO_DRIVER_BLOCKLIST( + OperatingSystem::Linux, + (nsAString &)GfxDriverInfo::GetDeviceVendor(VendorATI), + GfxDriverInfo::allDevices, nsIGfxInfo::FEATURE_WEBRENDER, + nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED, + V(0, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_NO_LINUX_ATI", ""); } return *sDriverInfo; } diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index fe91244fbacb..e5213b20a7f1 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -1602,9 +1602,9 @@ const nsTArray& GfxInfo::GetGfxDriverInfo() { //////////////////////////////////// // FEATURE_WEBRENDER - // We are blocking all non-Nvidia cards in gfxPlatform.cpp where we check - // for the WEBRENDER_QUALIFIED feature. However we also want to block some - // specific Nvidia cards for being too low-powered, so we do that here. + // We are blocking most hardware explicitly in gfxPlatform.cpp where we + // check for the WEBRENDER_QUALIFIED feature. However we also want to block + // some specific Nvidia cards for being too low-powered, so we do that here. APPEND_TO_DRIVER_BLOCKLIST2( OperatingSystem::Windows10, (nsAString&)GfxDriverInfo::GetDeviceVendor(VendorNVIDIA),