From 1cbd4cb69924b9338682ee90146e64400893584a Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 01/80] Bug 1309868 part 1 - Use const nsIContent pointer in some DOM utils. r=bholley MozReview-Commit-ID: H4g2VbWJUba --HG-- extra : source : 77e67e5163ac9c450e18d4c4e5b690cee5d3f1a3 --- dom/base/ChildIterator.cpp | 2 +- dom/base/ChildIterator.h | 22 +++++++++++++--------- dom/base/nsIContent.h | 2 +- dom/base/nsIContentInlines.h | 2 +- dom/xbl/nsBindingManager.cpp | 2 +- dom/xbl/nsBindingManager.h | 2 +- layout/style/ServoElementSnapshot.h | 2 +- 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/dom/base/ChildIterator.cpp b/dom/base/ChildIterator.cpp index eac73d490c8e..e7f4538738ae 100644 --- a/dom/base/ChildIterator.cpp +++ b/dom/base/ChildIterator.cpp @@ -562,7 +562,7 @@ IsNativeAnonymousImplementationOfPseudoElement(nsIContent* aContent) } /* static */ bool -StyleChildrenIterator::IsNeeded(Element* aElement) +StyleChildrenIterator::IsNeeded(const Element* aElement) { // If the node is in an anonymous subtree, we conservatively return true to // handle insertion points. diff --git a/dom/base/ChildIterator.h b/dom/base/ChildIterator.h index 53d1fb3dc0a2..ffff8dce5f42 100644 --- a/dom/base/ChildIterator.h +++ b/dom/base/ChildIterator.h @@ -35,7 +35,8 @@ namespace dom { class ExplicitChildIterator { public: - explicit ExplicitChildIterator(nsIContent* aParent, bool aStartAtBeginning = true) + explicit ExplicitChildIterator(const nsIContent* aParent, + bool aStartAtBeginning = true) : mParent(aParent), mChild(nullptr), mDefaultChild(nullptr), @@ -99,7 +100,7 @@ protected: // The parent of the children being iterated. For the FlattenedChildIterator, // if there is a binding attached to the original parent, mParent points to // the element for the binding. - nsIContent* mParent; + const nsIContent* mParent; // The current child. When we encounter an insertion point, // mChild remains as the insertion point whose content we're iterating (and @@ -136,7 +137,8 @@ protected: class FlattenedChildIterator : public ExplicitChildIterator { public: - explicit FlattenedChildIterator(nsIContent* aParent, bool aStartAtBeginning = true) + explicit FlattenedChildIterator(const nsIContent* aParent, + bool aStartAtBeginning = true) : ExplicitChildIterator(aParent, aStartAtBeginning), mXBLInvolved(false) { Init(false); @@ -155,7 +157,8 @@ protected: * This constructor is a hack to help AllChildrenIterator which sometimes * doesn't want to consider XBL. */ - FlattenedChildIterator(nsIContent* aParent, uint32_t aFlags, bool aStartAtBeginning = true) + FlattenedChildIterator(const nsIContent* aParent, uint32_t aFlags, + bool aStartAtBeginning = true) : ExplicitChildIterator(aParent, aStartAtBeginning), mXBLInvolved(false) { bool ignoreXBL = aFlags & nsIContent::eAllButXBL; @@ -181,7 +184,8 @@ protected: class AllChildrenIterator : private FlattenedChildIterator { public: - AllChildrenIterator(nsIContent* aNode, uint32_t aFlags, bool aStartAtBeginning = true) : + AllChildrenIterator(const nsIContent* aNode, uint32_t aFlags, + bool aStartAtBeginning = true) : FlattenedChildIterator(aNode, aFlags, aStartAtBeginning), mOriginalContent(aNode), mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0), mFlags(aFlags), mPhase(aStartAtBeginning ? eAtBegin : eAtEnd) { } @@ -211,7 +215,7 @@ public: nsIContent* GetNextChild(); nsIContent* GetPreviousChild(); - nsIContent* Parent() const { return mOriginalContent; } + const nsIContent* Parent() const { return mOriginalContent; } enum IteratorPhase { @@ -229,7 +233,7 @@ private: void AppendNativeAnonymousChildren(); void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame); - nsIContent* mOriginalContent; + const nsIContent* mOriginalContent; // mAnonKids is an array of native anonymous children, mAnonKidsIdx is index // in the array. If mAnonKidsIdx < mAnonKids.Length() and mPhase is @@ -262,7 +266,7 @@ private: */ class StyleChildrenIterator : private AllChildrenIterator { public: - explicit StyleChildrenIterator(nsIContent* aContent) + explicit StyleChildrenIterator(const nsIContent* aContent) : AllChildrenIterator(aContent, nsIContent::eAllChildren) { MOZ_COUNT_CTOR(StyleChildrenIterator); @@ -273,7 +277,7 @@ public: // Returns true if we cannot find all the children we need to style by // traversing the siblings of the first child. - static bool IsNeeded(Element* aParent); + static bool IsNeeded(const Element* aParent); }; } // namespace dom diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index d842b4aa8e1d..f05c47a61552 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -247,7 +247,7 @@ public: /** * Returns true if in a chrome document */ - virtual bool IsInChromeDocument(); + virtual bool IsInChromeDocument() const; /** * Get the namespace that this element's tag is defined in diff --git a/dom/base/nsIContentInlines.h b/dom/base/nsIContentInlines.h index 16fde9e56e26..368a0422b116 100644 --- a/dom/base/nsIContentInlines.h +++ b/dom/base/nsIContentInlines.h @@ -19,7 +19,7 @@ nsIContent::IsInHTMLDocument() const } inline bool -nsIContent::IsInChromeDocument() +nsIContent::IsInChromeDocument() const { return nsContentUtils::IsChromeDoc(OwnerDoc()); } diff --git a/dom/xbl/nsBindingManager.cpp b/dom/xbl/nsBindingManager.cpp index 40e357163a01..405c7aac7b1e 100644 --- a/dom/xbl/nsBindingManager.cpp +++ b/dom/xbl/nsBindingManager.cpp @@ -125,7 +125,7 @@ nsBindingManager::~nsBindingManager(void) } nsXBLBinding* -nsBindingManager::GetBindingWithContent(nsIContent* aContent) +nsBindingManager::GetBindingWithContent(const nsIContent* aContent) { nsXBLBinding* binding = aContent ? aContent->GetXBLBinding() : nullptr; return binding ? binding->GetBindingWithContent() : nullptr; diff --git a/dom/xbl/nsBindingManager.h b/dom/xbl/nsBindingManager.h index a70d888b9ce1..a71ff21887bb 100644 --- a/dom/xbl/nsBindingManager.h +++ b/dom/xbl/nsBindingManager.h @@ -46,7 +46,7 @@ public: explicit nsBindingManager(nsIDocument* aDocument); - nsXBLBinding* GetBindingWithContent(nsIContent* aContent); + nsXBLBinding* GetBindingWithContent(const nsIContent* aContent); void AddBoundContent(nsIContent* aContent); void RemoveBoundContent(nsIContent* aContent); diff --git a/layout/style/ServoElementSnapshot.h b/layout/style/ServoElementSnapshot.h index e1368fc96a3c..638b2fd311b2 100644 --- a/layout/style/ServoElementSnapshot.h +++ b/layout/style/ServoElementSnapshot.h @@ -143,7 +143,7 @@ public: return nullptr; } - bool IsInChromeDocument() + bool IsInChromeDocument() const { return mIsInChromeDocument; } From 3c3f76a8e15f46579cfbe6f9f1b1ffcb89c58a5c Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 02/80] Bug 1309868 part 2 - Use const nsIContent pointer in some layout utils. r=heycam MozReview-Commit-ID: 5GMave4FS48 --HG-- extra : source : 60d203eaa11b53f14a8c3736498f991d8e3615b1 --- layout/base/ServoRestyleManager.cpp | 2 +- layout/base/ServoRestyleManager.h | 2 +- layout/base/nsLayoutUtils.cpp | 4 ++-- layout/base/nsLayoutUtils.h | 4 ++-- layout/generic/nsIFrame.h | 2 +- layout/style/nsCSSRuleProcessor.cpp | 2 +- layout/style/nsCSSRuleProcessor.h | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index 7249fe533dc7..0dc52a81cda2 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -223,7 +223,7 @@ MarkChildrenAsDirtyForServo(nsIContent* aContent) } /* static */ nsIFrame* -ServoRestyleManager::FrameForPseudoElement(nsIContent* aContent, +ServoRestyleManager::FrameForPseudoElement(const nsIContent* aContent, nsIAtom* aPseudoTagOrNull) { MOZ_ASSERT_IF(aPseudoTagOrNull, aContent->IsElement()); diff --git a/layout/base/ServoRestyleManager.h b/layout/base/ServoRestyleManager.h index 17e102a50816..5c2a7eee71d2 100644 --- a/layout/base/ServoRestyleManager.h +++ b/layout/base/ServoRestyleManager.h @@ -93,7 +93,7 @@ public: * Right now only supports a null tag, before or after. If the pseudo-element * is not null, the content needs to be an element. */ - static nsIFrame* FrameForPseudoElement(nsIContent* aContent, + static nsIFrame* FrameForPseudoElement(const nsIContent* aContent, nsIAtom* aPseudoTagOrNull); protected: diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 0d09c9e2379b..c15d7d539dff 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1477,7 +1477,7 @@ nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame) /*static*/ nsIFrame* nsLayoutUtils::GetBeforeFrameForContent(nsIFrame* aFrame, - nsIContent* aContent) + const nsIContent* aContent) { // We need to call GetGenConPseudos() on the first continuation/ib-split. // Find it, for symmetry with GetAfterFrameForContent. @@ -1516,7 +1516,7 @@ nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame) /*static*/ nsIFrame* nsLayoutUtils::GetAfterFrameForContent(nsIFrame* aFrame, - nsIContent* aContent) + const nsIContent* aContent) { // We need to call GetGenConPseudos() on the first continuation, // but callers are likely to pass the last. diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 1214aa39c344..ea8b791bfb5f 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -281,7 +281,7 @@ public: * @return the ::before frame or nullptr if there isn't one */ static nsIFrame* GetBeforeFrameForContent(nsIFrame* aGenConParentFrame, - nsIContent* aContent); + const nsIContent* aContent); /** * GetBeforeFrame returns the outermost ::before frame of the given frame, if @@ -304,7 +304,7 @@ public: * @return the ::after frame or nullptr if there isn't one */ static nsIFrame* GetAfterFrameForContent(nsIFrame* aGenConParentFrame, - nsIContent* aContent); + const nsIContent* aContent); /** * GetAfterFrame returns the outermost ::after frame of the given frame, if one diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 15f0c98e8caf..2777378c7fcf 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -2940,7 +2940,7 @@ public: * @param aParentContent the content node corresponding to the parent frame * @return whether the frame is a pseudo frame */ - bool IsPseudoFrame(nsIContent* aParentContent) { + bool IsPseudoFrame(const nsIContent* aParentContent) { return mContent == aParentContent; } diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 8417c1697305..56ea20cfde86 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1257,7 +1257,7 @@ nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& a /* static */ bool -nsCSSRuleProcessor::IsLink(Element* aElement) +nsCSSRuleProcessor::IsLink(const Element* aElement) { EventStates state = aElement->StyleState(); return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED); diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h index 62c9cc160c5f..e207a71afea3 100644 --- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -116,7 +116,7 @@ public: /* * Helper to test whether a node is a link */ - static bool IsLink(mozilla::dom::Element* aElement); + static bool IsLink(const mozilla::dom::Element* aElement); /** * Returns true if the given aElement matches aSelector. From fe470c1653bd761c6a025fc09a86c27e81ec2d5b Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 03/80] Bug 1309868 part 3 - Make NonOwningStyleContextSource store const ServoComputedValues. r=heycam MozReview-Commit-ID: G84fYXLbQyt --HG-- extra : source : 85ba0fdfc05ec11dd08eba5a1b1f6a9d7e8acd8f --- layout/style/ServoStyleSet.cpp | 6 +++--- layout/style/StyleAnimationValue.cpp | 2 +- layout/style/StyleContextSource.h | 6 +++--- layout/style/nsStyleContext.cpp | 6 +++--- layout/style/nsStyleContext.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index e8f3865feeb8..ad985d16b04a 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -146,7 +146,7 @@ ServoStyleSet::ResolveStyleForText(nsIContent* aTextNode, if (parent->IsGeneratedContentContainerForBefore() || parent->IsGeneratedContentContainerForAfter()) { MOZ_ASSERT(aParentContext); - ServoComputedValues* parentComputedValues = + const ServoComputedValues* parentComputedValues = aParentContext->StyleSource().AsServoComputedValues(); computedValues = Servo_ComputedValues_Inherit(parentComputedValues).Consume(); @@ -163,7 +163,7 @@ ServoStyleSet::ResolveStyleForOtherNonElement(nsStyleContext* aParentContext) { // The parent context can be null if the non-element share a style context // with the root of an anonymous subtree. - ServoComputedValues* parent = + const ServoComputedValues* parent = aParentContext ? aParentContext->StyleSource().AsServoComputedValues() : nullptr; RefPtr computedValues = Servo_ComputedValues_Inherit(parent).Consume(); @@ -208,7 +208,7 @@ ServoStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, aFlags == nsStyleSet::eSkipParentDisplayBasedStyleFixup); bool skipFixup = aFlags & nsStyleSet::eSkipParentDisplayBasedStyleFixup; - ServoComputedValues* parentStyle = + const ServoComputedValues* parentStyle = aParentContext ? aParentContext->StyleSource().AsServoComputedValues() : nullptr; RefPtr computedValues = diff --git a/layout/style/StyleAnimationValue.cpp b/layout/style/StyleAnimationValue.cpp index c6f5fd4b919e..60e90ecd9e0b 100644 --- a/layout/style/StyleAnimationValue.cpp +++ b/layout/style/StyleAnimationValue.cpp @@ -3244,7 +3244,7 @@ StyleAnimationValue::ComputeValues( return false; } - RefPtr previousStyle = + const ServoComputedValues* previousStyle = aStyleContext->StyleSource().AsServoComputedValues(); // FIXME: Servo bindings don't yet represent const-ness so we just diff --git a/layout/style/StyleContextSource.h b/layout/style/StyleContextSource.h index 41fece9bc215..5f00e925c724 100644 --- a/layout/style/StyleContextSource.h +++ b/layout/style/StyleContextSource.h @@ -29,7 +29,7 @@ struct NonOwningStyleContextSource { MOZ_IMPLICIT NonOwningStyleContextSource(nsRuleNode* aRuleNode) : mBits(reinterpret_cast(aRuleNode)) {} - explicit NonOwningStyleContextSource(ServoComputedValues* aComputedValues) + explicit NonOwningStyleContextSource(const ServoComputedValues* aComputedValues) : mBits(reinterpret_cast(aComputedValues) | 1) {} bool operator==(const NonOwningStyleContextSource& aOther) const { @@ -65,7 +65,7 @@ struct NonOwningStyleContextSource return reinterpret_cast(mBits); } - ServoComputedValues* AsServoComputedValues() const { + const ServoComputedValues* AsServoComputedValues() const { MOZ_ASSERT(IsServoComputedValues()); return reinterpret_cast(mBits & ~1); } @@ -147,7 +147,7 @@ struct OwningStyleContextSource NonOwningStyleContextSource AsRaw() const { return mRaw; } nsRuleNode* AsGeckoRuleNode() const { return mRaw.AsGeckoRuleNode(); } ServoComputedValues* AsServoComputedValues() const { - return mRaw.AsServoComputedValues(); + return const_cast(mRaw.AsServoComputedValues()); } bool MatchesNoRules() const { return mRaw.MatchesNoRules(); } diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index 8602b09650e8..56563597cba5 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -1281,7 +1281,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aNewContext, class MOZ_STACK_CLASS FakeStyleContext { public: - explicit FakeStyleContext(ServoComputedValues* aComputedValues) + explicit FakeStyleContext(const ServoComputedValues* aComputedValues) : mComputedValues(aComputedValues) {} mozilla::NonOwningStyleContextSource StyleSource() const { @@ -1302,11 +1302,11 @@ public: #undef STYLE_STRUCT private: - ServoComputedValues* MOZ_NON_OWNING_REF mComputedValues; + const ServoComputedValues* MOZ_NON_OWNING_REF mComputedValues; }; nsChangeHint -nsStyleContext::CalcStyleDifference(ServoComputedValues* aNewComputedValues, +nsStyleContext::CalcStyleDifference(const ServoComputedValues* aNewComputedValues, nsChangeHint aParentHintsNotHandledForDescendants, uint32_t* aEqualStructs, uint32_t* aSamePointerStructs) diff --git a/layout/style/nsStyleContext.h b/layout/style/nsStyleContext.h index ff14b1188a16..6d641ac175fd 100644 --- a/layout/style/nsStyleContext.h +++ b/layout/style/nsStyleContext.h @@ -383,7 +383,7 @@ public: * Like the above, but allows comparing ServoComputedValues instead of needing * a full-fledged style context. */ - nsChangeHint CalcStyleDifference(ServoComputedValues* aNewComputedValues, + nsChangeHint CalcStyleDifference(const ServoComputedValues* aNewComputedValues, nsChangeHint aParentHintsNotHandledForDescendants, uint32_t* aEqualStructs, uint32_t* aSamePointerStructs); From be6a724c94e515be7ea342d6e60e1a616369d182 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 04/80] Bug 1309868 part 4 - Use mutable reference for Gecko_GetNextStyleChild. r=heycam MozReview-Commit-ID: FIrh34PSOZy --HG-- extra : source : 4ba2850ebc605a8e006f0331a06d59d515c115b2 --- layout/style/ServoBindings.cpp | 2 +- layout/style/ServoBindings.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index aa936555861b..74c8a146bbe7 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -146,7 +146,7 @@ Gecko_DropStyleChildrenIterator(StyleChildrenIteratorOwned aIterator) } RawGeckoNodeBorrowed -Gecko_GetNextStyleChild(StyleChildrenIteratorBorrowed aIterator) +Gecko_GetNextStyleChild(StyleChildrenIteratorBorrowedMut aIterator) { MOZ_ASSERT(aIterator); return aIterator->GetNextChild(); diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 45be3c1b3ed5..65da56046bd8 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -176,7 +176,7 @@ RawGeckoElementBorrowedOrNull Gecko_GetDocumentElement(RawGeckoDocumentBorrowed // faster) sibling traversal. StyleChildrenIteratorOwnedOrNull Gecko_MaybeCreateStyleChildrenIterator(RawGeckoNodeBorrowed node); void Gecko_DropStyleChildrenIterator(StyleChildrenIteratorOwned it); -RawGeckoNodeBorrowedOrNull Gecko_GetNextStyleChild(StyleChildrenIteratorBorrowed it); +RawGeckoNodeBorrowedOrNull Gecko_GetNextStyleChild(StyleChildrenIteratorBorrowedMut it); // Selector Matching. uint8_t Gecko_ElementState(RawGeckoElementBorrowed element); From 2e8b536effbe52cbaf111c3f9990cd756811e534 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 05/80] Bug 1309868 part 5 - Use const pointer in C++ side for immutable borrowed reference. r=manishearth,heycam MozReview-Commit-ID: 3aSOgckBtS7 --HG-- extra : source : 75117a5bef6ed1bd81864059f45429838686b2c7 --- layout/style/ServoBindings.cpp | 8 ++++---- layout/style/ServoBindings.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index 74c8a146bbe7..be1d8ba136e3 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -133,7 +133,7 @@ Gecko_MaybeCreateStyleChildrenIterator(RawGeckoNodeBorrowed aNode) return nullptr; } - Element* el = aNode->AsElement(); + const Element* el = aNode->AsElement(); return StyleChildrenIterator::IsNeeded(el) ? new StyleChildrenIterator(el) : nullptr; } @@ -224,13 +224,13 @@ Gecko_GetNodeFlags(RawGeckoNodeBorrowed aNode) void Gecko_SetNodeFlags(RawGeckoNodeBorrowed aNode, uint32_t aFlags) { - aNode->SetFlags(aFlags); + const_cast(aNode)->SetFlags(aFlags); } void Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed aNode, uint32_t aFlags) { - aNode->UnsetFlags(aFlags); + const_cast(aNode)->UnsetFlags(aFlags); } nsStyleContext* @@ -279,7 +279,7 @@ Gecko_StoreStyleDifference(RawGeckoNodeBorrowed aNode, nsChangeHint aChangeHintT MOZ_ASSERT(aNode->IsDirtyForServo(), "Change hint stored in a not-dirty node"); - Element* aElement = aNode->AsElement(); + const Element* aElement = aNode->AsElement(); nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); if (!primaryFrame) { // If there's no primary frame, that means that either this content is diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 65da56046bd8..026ac8bff4e0 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -75,8 +75,8 @@ using mozilla::dom::StyleChildrenIterator; // The "Arc" types are Servo-managed Arcs, which are passed // over FFI as Strong (which is nullable). // Note that T != ServoType, rather T is ArcInner -#define DECL_BORROWED_REF_TYPE_FOR(type_) typedef type_* type_##Borrowed; -#define DECL_NULLABLE_BORROWED_REF_TYPE_FOR(type_) typedef type_* type_##BorrowedOrNull; +#define DECL_BORROWED_REF_TYPE_FOR(type_) typedef type_ const* type_##Borrowed; +#define DECL_NULLABLE_BORROWED_REF_TYPE_FOR(type_) typedef type_ const* type_##BorrowedOrNull; #define DECL_BORROWED_MUT_REF_TYPE_FOR(type_) typedef type_* type_##BorrowedMut; #define DECL_NULLABLE_BORROWED_MUT_REF_TYPE_FOR(type_) typedef type_* type_##BorrowedMutOrNull; From 81f82ea36e533c72880642cb88bf7cc5527ee63e Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 06/80] Bug 1309109 part 1 - Change underlying type of StyleBackendType to uint8_t. r=heycam MozReview-Commit-ID: B6TmOq6elfs --HG-- extra : source : f7771920c448c382f215356275cb321a6943972e --- layout/style/StyleBackendType.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/style/StyleBackendType.h b/layout/style/StyleBackendType.h index 1775c72aaaef..f02b862133f8 100644 --- a/layout/style/StyleBackendType.h +++ b/layout/style/StyleBackendType.h @@ -12,7 +12,7 @@ namespace mozilla { /** * Enumeration that represents one of the two supported style system backends. */ -enum class StyleBackendType : int +enum class StyleBackendType : uint8_t { Gecko = 1, Servo From dc967ae16a161a3ac760637b180d989b995ec4a8 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 07/80] Bug 1309109 part 2 - Move immutable and container from css::Declaration to a new base class. r=heycam MozReview-Commit-ID: 9gCrBrK70Z3 --HG-- extra : source : d8df818860eb827fecef2e03da27f18513e78179 --- layout/style/Declaration.cpp | 13 +--- layout/style/Declaration.h | 79 ++-------------------- layout/style/DeclarationBlock.h | 113 ++++++++++++++++++++++++++++++++ layout/style/moz.build | 1 + 4 files changed, 122 insertions(+), 84 deletions(-) create mode 100644 layout/style/DeclarationBlock.h diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index bccf3caada06..09c917b06236 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -58,14 +58,9 @@ ImportantStyleData::List(FILE* out, int32_t aIndent) const } #endif -Declaration::Declaration() - : mImmutable(false) -{ - mContainer.mRaw = uintptr_t(0); -} - Declaration::Declaration(const Declaration& aCopy) - : mOrder(aCopy.mOrder), + : DeclarationBlock(aCopy), + mOrder(aCopy.mOrder), mVariableOrder(aCopy.mVariableOrder), mData(aCopy.mData ? aCopy.mData->Clone() : nullptr), mImportantData(aCopy.mImportantData ? @@ -75,10 +70,8 @@ Declaration::Declaration(const Declaration& aCopy) nullptr), mImportantVariables(aCopy.mImportantVariables ? new CSSVariableDeclarations(*aCopy.mImportantVariables) : - nullptr), - mImmutable(false) + nullptr) { - mContainer.mRaw = uintptr_t(0); } Declaration::~Declaration() diff --git a/layout/style/Declaration.h b/layout/style/Declaration.h index 6bfa2106e623..c77c995f87c3 100644 --- a/layout/style/Declaration.h +++ b/layout/style/Declaration.h @@ -18,6 +18,7 @@ #endif #include "mozilla/Attributes.h" +#include "mozilla/DeclarationBlock.h" #include "mozilla/MemoryReporting.h" #include "CSSVariableDeclarations.h" #include "nsCSSDataBlock.h" @@ -82,14 +83,16 @@ private: // be copied before it can be modified, which is taken care of by // |EnsureMutable|. -class Declaration final : public nsIStyleRule { +class Declaration final : public DeclarationBlock + , public nsIStyleRule +{ public: /** * Construct an |Declaration| that is in an invalid state (null * |mData|) and cannot be used until its |CompressFrom| method or * |InitializeEmpty| method is called. */ - Declaration(); + Declaration() : DeclarationBlock(StyleBackendType::Gecko) {} Declaration(const Declaration& aCopy); @@ -276,31 +279,11 @@ public: return !!mData->ValueFor(aProperty); } - /** - * Return whether |this| may be modified. - */ - bool IsMutable() const { - return !mImmutable; - } - /** * Copy |this|, if necessary to ensure that it can be modified. */ already_AddRefed EnsureMutable(); - /** - * Crash if |this| cannot be modified. - */ - void AssertMutable() const { - MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable"); - } - - /** - * Mark this declaration as unmodifiable. It's 'const' so it can - * be called from ToString. - */ - void SetImmutable() const { mImmutable = true; } - /** * Clear the data, in preparation for its replacement with entirely * new data by a call to |CompressFrom|. @@ -315,37 +298,6 @@ public: mVariableOrder.Clear(); } - void SetOwningRule(Rule* aRule) { - MOZ_ASSERT(!mContainer.mOwningRule || !aRule, - "should never overwrite one rule with another"); - mContainer.mOwningRule = aRule; - } - - Rule* GetOwningRule() const { - if (mContainer.mRaw & 0x1) { - return nullptr; - } - return mContainer.mOwningRule; - } - - void SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aHTMLCSSStyleSheet) { - MOZ_ASSERT(!mContainer.mHTMLCSSStyleSheet || !aHTMLCSSStyleSheet, - "should never overwrite one sheet with another"); - mContainer.mHTMLCSSStyleSheet = aHTMLCSSStyleSheet; - if (aHTMLCSSStyleSheet) { - mContainer.mRaw |= uintptr_t(1); - } - } - - nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const { - if (!(mContainer.mRaw & 0x1)) { - return nullptr; - } - auto c = mContainer; - c.mRaw &= ~uintptr_t(1); - return c.mHTMLCSSStyleSheet; - } - ImportantStyleData* GetImportantStyleData() { if (HasImportantData()) { return &mImportantStyleData; @@ -438,29 +390,8 @@ private: // may be null nsAutoPtr mImportantVariables; - union { - // We only ever have one of these since we have an - // nsHTMLCSSStyleSheet only for style attributes, and style - // attributes never have an owning rule. - - // It's an nsHTMLCSSStyleSheet if the low bit is set. - - uintptr_t mRaw; - - // The style rule that owns this declaration. May be null. - Rule* mOwningRule; - - // The nsHTMLCSSStyleSheet that is responsible for this declaration. - // Only non-null for style attributes. - nsHTMLCSSStyleSheet* mHTMLCSSStyleSheet; - } mContainer; - friend class ImportantStyleData; ImportantStyleData mImportantStyleData; - - // set when declaration put in the rule tree; - // also by ToString (hence the 'mutable'). - mutable bool mImmutable; }; inline ::mozilla::css::Declaration* diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h new file mode 100644 index 000000000000..8c03bd0b761b --- /dev/null +++ b/layout/style/DeclarationBlock.h @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * representation of a declaration block in a CSS stylesheet, or of + * a style attribute + */ + +#ifndef mozilla_DeclarationBlock_h +#define mozilla_DeclarationBlock_h + +#include "mozilla/StyleBackendType.h" + +class nsHTMLCSSStyleSheet; + +namespace mozilla { + +namespace css { +class Rule; +} // namespace css + +class DeclarationBlock +{ +protected: + explicit DeclarationBlock(StyleBackendType aType) + : mImmutable(false), mType(aType) { mContainer.mRaw = 0; } + + DeclarationBlock(const DeclarationBlock& aCopy) + : DeclarationBlock(aCopy.mType) {} + +public: + /** + * Return whether |this| may be modified. + */ + bool IsMutable() const { + return !mImmutable; + } + + /** + * Crash if |this| cannot be modified. + */ + void AssertMutable() const { + MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable"); + } + + /** + * Mark this declaration as unmodifiable. It's 'const' so it can + * be called from ToString. + */ + void SetImmutable() const { mImmutable = true; } + + void SetOwningRule(css::Rule* aRule) { + MOZ_ASSERT(!mContainer.mOwningRule || !aRule, + "should never overwrite one rule with another"); + mContainer.mOwningRule = aRule; + } + + css::Rule* GetOwningRule() const { + if (mContainer.mRaw & 0x1) { + return nullptr; + } + return mContainer.mOwningRule; + } + + void SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aHTMLCSSStyleSheet) { + MOZ_ASSERT(!mContainer.mHTMLCSSStyleSheet || !aHTMLCSSStyleSheet, + "should never overwrite one sheet with another"); + mContainer.mHTMLCSSStyleSheet = aHTMLCSSStyleSheet; + if (aHTMLCSSStyleSheet) { + mContainer.mRaw |= uintptr_t(1); + } + } + + nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const { + if (!(mContainer.mRaw & 0x1)) { + return nullptr; + } + auto c = mContainer; + c.mRaw &= ~uintptr_t(1); + return c.mHTMLCSSStyleSheet; + } + +private: + union { + // We only ever have one of these since we have an + // nsHTMLCSSStyleSheet only for style attributes, and style + // attributes never have an owning rule. + + // It's an nsHTMLCSSStyleSheet if the low bit is set. + + uintptr_t mRaw; + + // The style rule that owns this declaration. May be null. + css::Rule* mOwningRule; + + // The nsHTMLCSSStyleSheet that is responsible for this declaration. + // Only non-null for style attributes. + nsHTMLCSSStyleSheet* mHTMLCSSStyleSheet; + } mContainer; + + // set when declaration put in the rule tree; + // also by ToString (hence the 'mutable'). + mutable bool mImmutable; + + const StyleBackendType mType; +}; + +} // namespace mozilla + +#endif // mozilla_DeclarationBlock_h diff --git a/layout/style/moz.build b/layout/style/moz.build index 0acb9e4e31a2..bbbc3d83114a 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -86,6 +86,7 @@ EXPORTS.mozilla += [ 'CSSVariableDeclarations.h', 'CSSVariableResolver.h', 'CSSVariableValues.h', + 'DeclarationBlock.h', 'HandleRefPtr.h', 'IncrementalClearCOMRuleArray.h', 'LayerAnimationInfo.h', From 00100a750f0abadba51b51c175544c2b8e133bb2 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 08/80] Bug 1309109 part 3 - Rename ServoDeclarationBlock to RawServoDeclarationBlock. r=heycam So that the new subclass of DeclarationBlock could just be ServoDeclarationBlock. MozReview-Commit-ID: 55KgfmWmmyU --HG-- extra : source : 27c55355da341fce112b15d966eb9b2fa2c8834b --- dom/animation/KeyframeEffectReadOnly.h | 4 ++-- dom/animation/KeyframeUtils.cpp | 2 +- dom/base/nsAttrValue.cpp | 4 ++-- dom/base/nsAttrValue.h | 6 +++--- dom/base/nsAttrValueInlines.h | 4 ++-- layout/style/ServoBindingHelpers.h | 2 +- layout/style/ServoBindingList.h | 22 +++++++++++----------- layout/style/ServoBindings.cpp | 4 ++-- layout/style/ServoBindings.h | 7 ++++--- layout/style/StyleAnimationValue.cpp | 8 ++++---- layout/style/StyleAnimationValue.h | 8 ++++---- layout/style/nsHTMLCSSStyleSheet.cpp | 2 +- 12 files changed, 37 insertions(+), 36 deletions(-) diff --git a/dom/animation/KeyframeEffectReadOnly.h b/dom/animation/KeyframeEffectReadOnly.h index 1e506ff67e06..efbdb5144501 100644 --- a/dom/animation/KeyframeEffectReadOnly.h +++ b/dom/animation/KeyframeEffectReadOnly.h @@ -20,7 +20,7 @@ #include "mozilla/EffectCompositor.h" #include "mozilla/KeyframeEffectParams.h" #include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords -#include "mozilla/ServoBindingHelpers.h" // ServoDeclarationBlock and +#include "mozilla/ServoBindingHelpers.h" // RawServoDeclarationBlock and // associated RefPtrTraits #include "mozilla/StyleAnimationValue.h" #include "mozilla/dom/AnimationEffectReadOnly.h" @@ -67,7 +67,7 @@ struct PropertyValuePair // The specified value when using the Servo backend. However, even when // using the Servo backend, we still fill in |mValue| in the case where we // fail to parse the value since we use it to store the original string. - RefPtr mServoDeclarationBlock; + RefPtr mServoDeclarationBlock; bool operator==(const PropertyValuePair& aOther) const { return mProperty == aOther.mProperty && diff --git a/dom/animation/KeyframeUtils.cpp b/dom/animation/KeyframeUtils.cpp index ae603354c804..9b6424971322 100644 --- a/dom/animation/KeyframeUtils.cpp +++ b/dom/animation/KeyframeUtils.cpp @@ -1006,7 +1006,7 @@ MakePropertyValuePair(nsCSSPropertyID aProperty, const nsAString& aStringValue, nsCString baseString; aDocument->GetDocumentURI()->GetSpec(baseString); - RefPtr servoDeclarationBlock = + RefPtr servoDeclarationBlock = Servo_ParseProperty( reinterpret_cast(name.get()), name.Length(), reinterpret_cast(value.get()), value.Length(), diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp index fef6857ba807..c32e1eb88597 100644 --- a/dom/base/nsAttrValue.cpp +++ b/dom/base/nsAttrValue.cpp @@ -463,7 +463,7 @@ nsAttrValue::SetTo(css::Declaration* aValue, const nsAString* aSerialized) } void -nsAttrValue::SetTo(already_AddRefed aValue, +nsAttrValue::SetTo(already_AddRefed aValue, const nsAString* aSerialized) { MiscContainer* cont = EnsureEmptyMiscContainer(); @@ -1773,7 +1773,7 @@ nsAttrValue::ParseStyleAttribute(const nsAString& aString, if (ownerDoc->GetStyleBackendType() == StyleBackendType::Servo) { NS_ConvertUTF16toUTF8 value(aString); - RefPtr decl = Servo_ParseStyleAttribute( + RefPtr decl = Servo_ParseStyleAttribute( reinterpret_cast(value.get()), value.Length(), sheet).Consume(); MOZ_ASSERT(decl); diff --git a/dom/base/nsAttrValue.h b/dom/base/nsAttrValue.h index a00d369c21d1..a622f7cfb1e5 100644 --- a/dom/base/nsAttrValue.h +++ b/dom/base/nsAttrValue.h @@ -35,7 +35,7 @@ class nsAString; class nsIDocument; class nsStyledElement; struct MiscContainer; -struct ServoDeclarationBlock; +struct RawServoDeclarationBlock; namespace mozilla { namespace css { @@ -151,7 +151,7 @@ public: void SetTo(int32_t aInt, const nsAString* aSerialized); void SetTo(double aValue, const nsAString* aSerialized); void SetTo(mozilla::css::Declaration* aValue, const nsAString* aSerialized); - void SetTo(already_AddRefed aDeclarationBlock, + void SetTo(already_AddRefed aDeclarationBlock, const nsAString* aSerialized); void SetTo(mozilla::css::URLValue* aValue, const nsAString* aSerialized); void SetTo(const nsIntMargin& aValue); @@ -204,7 +204,7 @@ public: inline float GetPercentValue() const; inline AtomArray* GetAtomArrayValue() const; inline mozilla::css::Declaration* GetGeckoCSSDeclarationValue() const; - inline ServoDeclarationBlock* GetServoCSSDeclarationValue() const; + inline RawServoDeclarationBlock* GetServoCSSDeclarationValue() const; inline mozilla::css::URLValue* GetURLValue() const; inline mozilla::css::ImageValue* GetImageValue() const; inline double GetDoubleValue() const; diff --git a/dom/base/nsAttrValueInlines.h b/dom/base/nsAttrValueInlines.h index 554a3ff1a0ab..1f65314ad59e 100644 --- a/dom/base/nsAttrValueInlines.h +++ b/dom/base/nsAttrValueInlines.h @@ -32,7 +32,7 @@ struct MiscContainer final uint32_t mEnumValue; int32_t mPercent; mozilla::css::Declaration* mGeckoCSSDeclaration; - ServoDeclarationBlock* mServoCSSDeclaration; + RawServoDeclarationBlock* mServoCSSDeclaration; mozilla::css::URLValue* mURL; mozilla::css::ImageValue* mImage; nsAttrValue::AtomArray* mAtomArray; @@ -155,7 +155,7 @@ nsAttrValue::GetGeckoCSSDeclarationValue() const return GetMiscContainer()->mValue.mGeckoCSSDeclaration; } -inline ServoDeclarationBlock* +inline RawServoDeclarationBlock* nsAttrValue::GetServoCSSDeclarationValue() const { NS_PRECONDITION(Type() == eServoCSSDeclaration, "wrong type"); diff --git a/layout/style/ServoBindingHelpers.h b/layout/style/ServoBindingHelpers.h index eb903b9ba853..b2263ea62421 100644 --- a/layout/style/ServoBindingHelpers.h +++ b/layout/style/ServoBindingHelpers.h @@ -29,7 +29,7 @@ namespace mozilla { DEFINE_REFPTR_TRAITS(StyleSheet, RawServoStyleSheet); DEFINE_REFPTR_TRAITS(ComputedValues, ServoComputedValues); -DEFINE_REFPTR_TRAITS(DeclarationBlock, ServoDeclarationBlock); +DEFINE_REFPTR_TRAITS(DeclarationBlock, RawServoDeclarationBlock); #undef DEFINE_REFPTR_TRAITS diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index fc2abfcf6235..b318e9980877 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -49,7 +49,7 @@ SERVO_BINDING_FUNC(Servo_StyleSet_InsertStyleSheetBefore, void, // Animations API SERVO_BINDING_FUNC(Servo_ParseProperty, - ServoDeclarationBlockStrong, + RawServoDeclarationBlockStrong, const uint8_t* property_bytes, uint32_t property_length, const uint8_t* value_bytes, @@ -61,28 +61,28 @@ SERVO_BINDING_FUNC(Servo_ParseProperty, ThreadSafePrincipalHolder* principal) SERVO_BINDING_FUNC(Servo_RestyleWithAddedDeclaration, ServoComputedValuesStrong, - ServoDeclarationBlockBorrowed declarations, + RawServoDeclarationBlockBorrowed declarations, ServoComputedValuesBorrowed previous_style) // Style attribute -SERVO_BINDING_FUNC(Servo_ParseStyleAttribute, ServoDeclarationBlockStrong, +SERVO_BINDING_FUNC(Servo_ParseStyleAttribute, RawServoDeclarationBlockStrong, const uint8_t* bytes, uint32_t length, nsHTMLCSSStyleSheet* cache) SERVO_BINDING_FUNC(Servo_DeclarationBlock_AddRef, void, - ServoDeclarationBlockBorrowed declarations) + RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_Release, void, - ServoDeclarationBlockBorrowed declarations) + RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_Equals, bool, - ServoDeclarationBlockBorrowed a, - ServoDeclarationBlockBorrowed b) + RawServoDeclarationBlockBorrowed a, + RawServoDeclarationBlockBorrowed b) SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetCache, nsHTMLCSSStyleSheet*, - ServoDeclarationBlockBorrowed declarations) + RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetImmutable, void, - ServoDeclarationBlockBorrowed declarations) + RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_ClearCachePointer, void, - ServoDeclarationBlockBorrowed declarations) + RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_SerializeOneValue, void, - ServoDeclarationBlockBorrowed declarations, + RawServoDeclarationBlockBorrowed declarations, nsString* buffer) // CSS supports() diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index be1d8ba136e3..616d0e71f273 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -44,7 +44,7 @@ using namespace mozilla::dom; IMPL_STRONG_REF_TYPE_FOR(ServoComputedValues) IMPL_STRONG_REF_TYPE_FOR(RawServoStyleSheet) -IMPL_STRONG_REF_TYPE_FOR(ServoDeclarationBlock) +IMPL_STRONG_REF_TYPE_FOR(RawServoDeclarationBlock) #undef IMPL_STRONG_REF_TYPE_FOR @@ -312,7 +312,7 @@ Gecko_StoreStyleDifference(RawGeckoNodeBorrowed aNode, nsChangeHint aChangeHintT #endif } -ServoDeclarationBlock* +RawServoDeclarationBlock* Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed aElement) { const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style); diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 026ac8bff4e0..0b131851ce9c 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -50,7 +50,7 @@ struct nsStyleGradientStop; class nsStyleGradient; class nsStyleCoord; struct nsStyleDisplay; -struct ServoDeclarationBlock; +struct RawServoDeclarationBlock; namespace mozilla { namespace dom { @@ -101,7 +101,7 @@ using mozilla::dom::StyleChildrenIterator; DECL_ARC_REF_TYPE_FOR(ServoComputedValues) DECL_ARC_REF_TYPE_FOR(RawServoStyleSheet) -DECL_ARC_REF_TYPE_FOR(ServoDeclarationBlock) +DECL_ARC_REF_TYPE_FOR(RawServoDeclarationBlock) DECL_OWNED_REF_TYPE_FOR(RawServoStyleSet) DECL_NULLABLE_OWNED_REF_TYPE_FOR(StyleChildrenIterator) @@ -216,7 +216,8 @@ SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, #undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS // Style attributes. -ServoDeclarationBlockBorrowedOrNull Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed element); +RawServoDeclarationBlockBorrowedOrNull +Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed element); // Atoms. nsIAtom* Gecko_Atomize(const char* aString, uint32_t aLength); diff --git a/layout/style/StyleAnimationValue.cpp b/layout/style/StyleAnimationValue.cpp index 60e90ecd9e0b..13624893ffb7 100644 --- a/layout/style/StyleAnimationValue.cpp +++ b/layout/style/StyleAnimationValue.cpp @@ -27,7 +27,7 @@ #include "mozilla/dom/Element.h" #include "mozilla/FloatingPoint.h" #include "mozilla/Likely.h" -#include "mozilla/ServoBindings.h" // ServoDeclarationBlock +#include "mozilla/ServoBindings.h" // RawServoDeclarationBlock #include "gfxMatrix.h" #include "gfxQuaternion.h" #include "nsIDocument.h" @@ -3233,12 +3233,12 @@ StyleAnimationValue::ComputeValues( nsCSSPropertyID aProperty, CSSEnabledState aEnabledState, nsStyleContext* aStyleContext, - const ServoDeclarationBlock& aDeclarations, + const RawServoDeclarationBlock& aDeclarations, nsTArray& aValues) { MOZ_ASSERT(aStyleContext->PresContext()->StyleSet()->IsServo(), "Should be using ServoStyleSet if we have a" - " ServoDeclarationBlock"); + " RawServoDeclarationBlock"); if (!nsCSSProps::IsEnabled(aProperty, aEnabledState)) { return false; @@ -3249,7 +3249,7 @@ StyleAnimationValue::ComputeValues( // FIXME: Servo bindings don't yet represent const-ness so we just // cast it away for now. - auto declarations = const_cast(&aDeclarations); + auto declarations = const_cast(&aDeclarations); RefPtr computedValues = Servo_RestyleWithAddedDeclaration(declarations, previousStyle).Consume(); if (!computedValues) { diff --git a/layout/style/StyleAnimationValue.h b/layout/style/StyleAnimationValue.h index 1d2f6b7438d4..3a2ac4db6150 100644 --- a/layout/style/StyleAnimationValue.h +++ b/layout/style/StyleAnimationValue.h @@ -20,7 +20,7 @@ class nsIFrame; class nsStyleContext; class gfx3DMatrix; -struct ServoDeclarationBlock; +struct RawServoDeclarationBlock; namespace mozilla { @@ -237,14 +237,14 @@ public: nsTArray& aResult); /** - * A variant of ComputeValues that takes a ServoDeclarationBlock as the - * specified value. + * A variant of ComputeValues that takes a RawServoDeclarationBlock + * as the specified value. */ static MOZ_MUST_USE bool ComputeValues(nsCSSPropertyID aProperty, mozilla::CSSEnabledState aEnabledState, nsStyleContext* aStyleContext, - const ServoDeclarationBlock& aDeclarations, + const RawServoDeclarationBlock& aDeclarations, nsTArray& aValues); /** diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index 83283ce55d61..a941307972ae 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -45,7 +45,7 @@ nsHTMLCSSStyleSheet::~nsHTMLCSSStyleSheet() break; } case nsAttrValue::eServoCSSDeclaration: { - ServoDeclarationBlock* declarations = + RawServoDeclarationBlock* declarations = value->mValue.mServoCSSDeclaration; Servo_DeclarationBlock_ClearCachePointer(declarations); break; From 9f07f8de0b636e9adfc6723a9cb631f5946f9215 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 09/80] Bug 1309109 part 4 - Add ServoDeclarationBlock class. r=heycam MozReview-Commit-ID: 8I9sbt0oMhx --HG-- extra : source : b039f929a98ad954d83fab930806dc3fd6ce9762 --- layout/style/DeclarationBlock.h | 9 ++++++ layout/style/DeclarationBlockInlines.h | 31 +++++++++++++++++++ layout/style/ServoDeclarationBlock.cpp | 23 ++++++++++++++ layout/style/ServoDeclarationBlock.h | 42 ++++++++++++++++++++++++++ layout/style/moz.build | 3 ++ 5 files changed, 108 insertions(+) create mode 100644 layout/style/DeclarationBlockInlines.h create mode 100644 layout/style/ServoDeclarationBlock.cpp create mode 100644 layout/style/ServoDeclarationBlock.h diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h index 8c03bd0b761b..94776becff30 100644 --- a/layout/style/DeclarationBlock.h +++ b/layout/style/DeclarationBlock.h @@ -12,13 +12,17 @@ #ifndef mozilla_DeclarationBlock_h #define mozilla_DeclarationBlock_h +#include "mozilla/ServoUtils.h" #include "mozilla/StyleBackendType.h" class nsHTMLCSSStyleSheet; namespace mozilla { +class ServoDeclarationBlock; + namespace css { +class Declaration; class Rule; } // namespace css @@ -32,6 +36,11 @@ protected: : DeclarationBlock(aCopy.mType) {} public: + MOZ_DECL_STYLO_METHODS(css::Declaration, ServoDeclarationBlock) + + inline MozExternalRefCountType AddRef(); + inline MozExternalRefCountType Release(); + /** * Return whether |this| may be modified. */ diff --git a/layout/style/DeclarationBlockInlines.h b/layout/style/DeclarationBlockInlines.h new file mode 100644 index 000000000000..7f839be0dd2d --- /dev/null +++ b/layout/style/DeclarationBlockInlines.h @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_DeclarationBlockInlines_h +#define mozilla_DeclarationBlockInlines_h + +#include "mozilla/css/Declaration.h" +#include "mozilla/ServoDeclarationBlock.h" + +namespace mozilla { + +MOZ_DEFINE_STYLO_METHODS(DeclarationBlock, css::Declaration, ServoDeclarationBlock) + +MozExternalRefCountType +DeclarationBlock::AddRef() +{ + MOZ_STYLO_FORWARD(AddRef, ()) +} + +MozExternalRefCountType +DeclarationBlock::Release() +{ + MOZ_STYLO_FORWARD(Release, ()) +} + +} // namespace mozilla + +#endif // mozilla_DeclarationBlockInlines_h diff --git a/layout/style/ServoDeclarationBlock.cpp b/layout/style/ServoDeclarationBlock.cpp new file mode 100644 index 000000000000..b98cc91d3698 --- /dev/null +++ b/layout/style/ServoDeclarationBlock.cpp @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ServoDeclarationBlock.h" + +#include "mozilla/ServoBindings.h" + +namespace mozilla { + +/* static */ already_AddRefed +ServoDeclarationBlock::FromStyleAttribute(const nsAString& aString) +{ + NS_ConvertUTF16toUTF8 value(aString); + RefPtr raw = Servo_ParseStyleAttribute( + reinterpret_cast(value.get()), + value.Length(), nullptr).Consume(); + RefPtr decl = new ServoDeclarationBlock(raw.forget()); + return decl.forget(); +} + +} // namespace mozilla diff --git a/layout/style/ServoDeclarationBlock.h b/layout/style/ServoDeclarationBlock.h new file mode 100644 index 000000000000..0456ea4ca55b --- /dev/null +++ b/layout/style/ServoDeclarationBlock.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ServoDeclarationBlock_h +#define mozilla_ServoDeclarationBlock_h + +#include "mozilla/ServoBindings.h" +#include "mozilla/DeclarationBlock.h" + +namespace mozilla { + +class ServoDeclarationBlock final : public DeclarationBlock +{ +public: + NS_INLINE_DECL_REFCOUNTING(ServoDeclarationBlock) + + static already_AddRefed + FromStyleAttribute(const nsAString& aString); + + RawServoDeclarationBlock* const* RefRaw() const { + static_assert(sizeof(RefPtr) == + sizeof(RawServoDeclarationBlock*), + "RefPtr should just be a pointer"); + return reinterpret_cast(&mRaw); + } + +protected: + explicit ServoDeclarationBlock( + already_AddRefed aRaw) + : DeclarationBlock(StyleBackendType::Servo), mRaw(aRaw) {} + +private: + ~ServoDeclarationBlock() {} + + RefPtr mRaw; +}; + +} // namespace mozilla + +#endif // mozilla_ServoDeclarationBlock_h diff --git a/layout/style/moz.build b/layout/style/moz.build index bbbc3d83114a..f877ce984832 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -87,6 +87,7 @@ EXPORTS.mozilla += [ 'CSSVariableResolver.h', 'CSSVariableValues.h', 'DeclarationBlock.h', + 'DeclarationBlockInlines.h', 'HandleRefPtr.h', 'IncrementalClearCOMRuleArray.h', 'LayerAnimationInfo.h', @@ -95,6 +96,7 @@ EXPORTS.mozilla += [ 'ServoBindingHelpers.h', 'ServoBindingList.h', 'ServoBindings.h', + 'ServoDeclarationBlock.h', 'ServoElementSnapshot.h', 'ServoStyleSet.h', 'ServoStyleSheet.h', @@ -194,6 +196,7 @@ UNIFIED_SOURCES += [ 'RuleNodeCacheConditions.cpp', 'RuleProcessorCache.cpp', 'ServoBindings.cpp', + 'ServoDeclarationBlock.cpp', 'ServoElementSnapshot.cpp', 'ServoStyleSet.cpp', 'ServoStyleSheet.cpp', From 05ef8466c6f506ec7bb15dd424ccbc9a1df74da5 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 10/80] Bug 1309109 part 5 - Store ServoDeclarationBlock rather than RawServoDeclarationBlock in nsAttrValue. r=heycam MozReview-Commit-ID: HWqgjwif1qF --HG-- extra : source : cf3b1baff3d1d6233c72e32cf01cd28a2b344ebf --- dom/base/Element.cpp | 4 +- dom/base/Element.h | 5 +- dom/base/FragmentOrElement.h | 3 + dom/base/nsAttrValue.cpp | 171 +++++++---------------- dom/base/nsAttrValue.h | 15 +- dom/base/nsAttrValueInlines.h | 27 ++-- dom/base/nsStyledElement.cpp | 20 +-- dom/base/nsStyledElement.h | 8 +- dom/html/nsGenericHTMLElement.cpp | 13 +- dom/svg/nsSVGElement.cpp | 2 +- dom/xul/nsXULElement.cpp | 21 +-- editor/libeditor/CSSEditUtils.cpp | 10 +- layout/style/ServoBindings.cpp | 16 ++- layout/style/nsDOMCSSAttrDeclaration.cpp | 8 +- layout/style/nsDOMCSSDeclaration.cpp | 2 +- layout/style/nsHTMLCSSStyleSheet.cpp | 32 ++--- 16 files changed, 144 insertions(+), 213 deletions(-) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index d2c5063f7e23..d7027e60c99c 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -2014,14 +2014,14 @@ Element::IsInteractiveHTMLContent(bool aIgnoreTabindex) const return false; } -css::Declaration* +DeclarationBlock* Element::GetInlineStyleDeclaration() { return nullptr; } nsresult -Element::SetInlineStyleDeclaration(css::Declaration* aDeclaration, +Element::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration, const nsAString* aSerialized, bool aNotify) { diff --git a/dom/base/Element.h b/dom/base/Element.h index 9a3396c20fac..1dab6d3539f4 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -56,6 +56,7 @@ class nsDocument; class nsDOMStringMap; namespace mozilla { +class DeclarationBlock; namespace dom { struct AnimationFilter; struct ScrollIntoViewOptions; @@ -254,13 +255,13 @@ public: /** * Get the inline style declaration, if any, for this element. */ - virtual css::Declaration* GetInlineStyleDeclaration(); + virtual DeclarationBlock* GetInlineStyleDeclaration(); /** * Set the inline style declaration for this element. This will send * an appropriate AttributeChanged notification if aNotify is true. */ - virtual nsresult SetInlineStyleDeclaration(css::Declaration* aDeclaration, + virtual nsresult SetInlineStyleDeclaration(DeclarationBlock* aDeclaration, const nsAString* aSerialized, bool aNotify); diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index 1ebd73e1bedc..f2ffbc63ecfd 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -33,6 +33,9 @@ class nsDOMStringMap; class nsIURI; namespace mozilla { +namespace css { +class Declaration; +} // namespace css namespace dom { class Element; } // namespace dom diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp index c32e1eb88597..3488348658fc 100644 --- a/dom/base/nsAttrValue.cpp +++ b/dom/base/nsAttrValue.cpp @@ -18,7 +18,7 @@ #include "nsUnicharUtils.h" #include "mozilla/MemoryReporting.h" #include "mozilla/ServoBindingHelpers.h" -#include "mozilla/css/Declaration.h" +#include "mozilla/DeclarationBlockInlines.h" #include "nsContentUtils.h" #include "nsReadableUtils.h" #include "prprf.h" @@ -73,26 +73,16 @@ void MiscContainer::Cache() { // Not implemented for anything else yet. - MOZ_ASSERT(mType == nsAttrValue::eGeckoCSSDeclaration || - mType == nsAttrValue::eServoCSSDeclaration); + if (mType != nsAttrValue::eCSSDeclaration) { + MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); + return; + } + MOZ_ASSERT(IsRefCounted()); MOZ_ASSERT(mValue.mRefCount > 0); MOZ_ASSERT(!mValue.mCached); - nsHTMLCSSStyleSheet* sheet; - switch (mType) { - case nsAttrValue::eGeckoCSSDeclaration: - sheet = mValue.mGeckoCSSDeclaration->GetHTMLCSSStyleSheet(); - break; - case nsAttrValue::eServoCSSDeclaration: - sheet = Servo_DeclarationBlock_GetCache(mValue.mServoCSSDeclaration); - break; - default: - MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); - sheet = nullptr; - break; - } - + nsHTMLCSSStyleSheet* sheet = mValue.mCSSDeclaration->GetHTMLCSSStyleSheet(); if (!sheet) { return; } @@ -107,25 +97,17 @@ MiscContainer::Cache() mValue.mCached = 1; // This has to be immutable once it goes into the cache. - switch (mType) { - case nsAttrValue::eGeckoCSSDeclaration: - mValue.mGeckoCSSDeclaration->SetImmutable(); - break; - case nsAttrValue::eServoCSSDeclaration: - Servo_DeclarationBlock_SetImmutable(mValue.mServoCSSDeclaration); - break; - default: - MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); - break; - } + mValue.mCSSDeclaration->SetImmutable(); } void MiscContainer::Evict() { // Not implemented for anything else yet. - MOZ_ASSERT(mType == nsAttrValue::eGeckoCSSDeclaration || - mType == nsAttrValue::eServoCSSDeclaration); + if (mType != nsAttrValue::eCSSDeclaration) { + MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); + return; + } MOZ_ASSERT(IsRefCounted()); MOZ_ASSERT(mValue.mRefCount == 0); @@ -133,19 +115,7 @@ MiscContainer::Evict() return; } - nsHTMLCSSStyleSheet* sheet; - switch (mType) { - case nsAttrValue::eGeckoCSSDeclaration: - sheet = mValue.mGeckoCSSDeclaration->GetHTMLCSSStyleSheet(); - break; - case nsAttrValue::eServoCSSDeclaration: - sheet = Servo_DeclarationBlock_GetCache(mValue.mServoCSSDeclaration); - break; - default: - MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); - sheet = nullptr; - break; - } + nsHTMLCSSStyleSheet* sheet = mValue.mCSSDeclaration->GetHTMLCSSStyleSheet(); MOZ_ASSERT(sheet); nsString str; @@ -181,10 +151,11 @@ nsAttrValue::nsAttrValue(nsIAtom* aValue) SetTo(aValue); } -nsAttrValue::nsAttrValue(css::Declaration* aValue, const nsAString* aSerialized) +nsAttrValue::nsAttrValue(already_AddRefed aValue, + const nsAString* aSerialized) : mBits(0) { - SetTo(aValue, aSerialized); + SetTo(Move(aValue), aSerialized); } nsAttrValue::nsAttrValue(const nsIntMargin& aValue) @@ -343,8 +314,7 @@ nsAttrValue::SetTo(const nsAttrValue& aOther) cont->mValue.mColor = otherCont->mValue.mColor; break; } - case eGeckoCSSDeclaration: - case eServoCSSDeclaration: + case eCSSDeclaration: { MOZ_CRASH("These should be refcounted!"); } @@ -451,25 +421,13 @@ nsAttrValue::SetTo(double aValue, const nsAString* aSerialized) } void -nsAttrValue::SetTo(css::Declaration* aValue, const nsAString* aSerialized) -{ - MiscContainer* cont = EnsureEmptyMiscContainer(); - MOZ_ASSERT(cont->mValue.mRefCount == 0); - NS_ADDREF(cont->mValue.mGeckoCSSDeclaration = aValue); - cont->mType = eGeckoCSSDeclaration; - NS_ADDREF(cont); - SetMiscAtomOrString(aSerialized); - MOZ_ASSERT(cont->mValue.mRefCount == 1); -} - -void -nsAttrValue::SetTo(already_AddRefed aValue, +nsAttrValue::SetTo(already_AddRefed aValue, const nsAString* aSerialized) { MiscContainer* cont = EnsureEmptyMiscContainer(); MOZ_ASSERT(cont->mValue.mRefCount == 0); - cont->mValue.mServoCSSDeclaration = aValue.take(); - cont->mType = eServoCSSDeclaration; + cont->mValue.mCSSDeclaration = aValue.take(); + cont->mType = eCSSDeclaration; NS_ADDREF(cont); SetMiscAtomOrString(aSerialized); MOZ_ASSERT(cont->mValue.mRefCount == 1); @@ -682,17 +640,15 @@ nsAttrValue::ToString(nsAString& aResult) const break; } - case eGeckoCSSDeclaration: + case eCSSDeclaration: { - // XXXheycam Once we support CSSOM access to them, we should - // probably serialize eServoCSSDeclarations like this too. - // For now, we will return the string from the MiscContainer - // at the top of this function. aResult.Truncate(); MiscContainer *container = GetMiscContainer(); - css::Declaration *decl = container->mValue.mGeckoCSSDeclaration; - if (decl) { - decl->ToString(aResult); + DeclarationBlock* decl = container->mValue.mCSSDeclaration; + // XXXheycam Once we support CSSOM access to them, we should + // probably serialize ServoDeclarationBlock like this too. + if (decl && decl->IsGecko()) { + decl->AsGecko()->ToString(aResult); } const_cast(this)->SetMiscAtomOrString(&aResult); @@ -935,13 +891,9 @@ nsAttrValue::HashValue() const { return cont->mValue.mColor; } - case eGeckoCSSDeclaration: + case eCSSDeclaration: { - return NS_PTR_TO_INT32(cont->mValue.mGeckoCSSDeclaration); - } - case eServoCSSDeclaration: - { - return NS_PTR_TO_INT32(cont->mValue.mServoCSSDeclaration); + return NS_PTR_TO_INT32(cont->mValue.mCSSDeclaration); } // Intentionally identical, so that loading the image does not change the // hash code. @@ -1048,10 +1000,10 @@ nsAttrValue::Equals(const nsAttrValue& aOther) const } break; } - case eGeckoCSSDeclaration: + case eCSSDeclaration: { - return thisCont->mValue.mGeckoCSSDeclaration == - otherCont->mValue.mGeckoCSSDeclaration; + return thisCont->mValue.mCSSDeclaration == + otherCont->mValue.mCSSDeclaration; } case eURL: { @@ -1081,11 +1033,6 @@ nsAttrValue::Equals(const nsAttrValue& aOther) const { return thisCont->mValue.mIntMargin == otherCont->mValue.mIntMargin; } - case eServoCSSDeclaration: - { - return thisCont->mValue.mServoCSSDeclaration == - otherCont->mValue.mServoCSSDeclaration; - } default: { if (IsSVGType(thisCont->mType)) { @@ -1771,26 +1718,20 @@ nsAttrValue::ParseStyleAttribute(const nsAString& aString, } } + RefPtr decl; if (ownerDoc->GetStyleBackendType() == StyleBackendType::Servo) { - NS_ConvertUTF16toUTF8 value(aString); - RefPtr decl = Servo_ParseStyleAttribute( - reinterpret_cast(value.get()), - value.Length(), sheet).Consume(); - MOZ_ASSERT(decl); - SetTo(decl.forget(), &aString); + decl = ServoDeclarationBlock::FromStyleAttribute(aString); } else { css::Loader* cssLoader = ownerDoc->CSSLoader(); nsCSSParser cssParser(cssLoader); - - RefPtr declaration = - cssParser.ParseStyleAttribute(aString, docURI, baseURI, - aElement->NodePrincipal()); - if (!declaration) { - return false; - } - declaration->SetHTMLCSSStyleSheet(sheet); - SetTo(declaration, &aString); + decl = cssParser.ParseStyleAttribute(aString, docURI, baseURI, + aElement->NodePrincipal()); } + if (!decl) { + return false; + } + decl->SetHTMLCSSStyleSheet(sheet); + SetTo(decl.forget(), &aString); if (cachingAllowed) { MiscContainer* cont = GetMiscContainer(); @@ -1808,15 +1749,14 @@ nsAttrValue::SetMiscAtomOrString(const nsAString* aValue) "Trying to re-set atom or string!"); if (aValue) { uint32_t len = aValue->Length(); - // * We're allowing eGeckoCSSDeclaration and eServoCSSDeclaration - // attributes to store empty strings as it can be beneficial to store - // an empty style attribute as a parsed rule. + // * We're allowing eCSSDeclaration attributes to store empty + // strings as it can be beneficial to store an empty style + // attribute as a parsed rule. // * We're allowing enumerated values because sometimes the empty // string corresponds to a particular enumerated value, especially // for enumerated values that are not limited enumerated. // Add other types as needed. - NS_ASSERTION(len || Type() == eGeckoCSSDeclaration || - Type() == eServoCSSDeclaration || Type() == eEnum, + NS_ASSERTION(len || Type() == eCSSDeclaration || Type() == eEnum, "Empty string?"); MiscContainer* cont = GetMiscContainer(); if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) { @@ -1880,17 +1820,12 @@ nsAttrValue::ClearMiscContainer() } else { switch (cont->mType) { - case eGeckoCSSDeclaration: - case eServoCSSDeclaration: + case eCSSDeclaration: { MOZ_ASSERT(cont->mValue.mRefCount == 1); cont->Release(); cont->Evict(); - if (cont->mType == eGeckoCSSDeclaration) { - NS_RELEASE(cont->mValue.mGeckoCSSDeclaration); - } else { - Servo_DeclarationBlock_Release(cont->mValue.mServoCSSDeclaration); - } + NS_RELEASE(cont->mValue.mCSSDeclaration); break; } case eURL: @@ -2020,16 +1955,12 @@ nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0; } - if (Type() == eGeckoCSSDeclaration && - container->mValue.mGeckoCSSDeclaration) { - // TODO: mGeckoCSSDeclaration might be owned by another object which + if (Type() == eCSSDeclaration && container->mValue.mCSSDeclaration) { + // TODO: mCSSDeclaration might be owned by another object which // would make us count them twice, bug 677493. - //n += container->mGeckoCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf); - } else if (Type() == eServoCSSDeclaration && - container->mValue.mServoCSSDeclaration) { - // Bug 1281964: As with eGeckoCSSDeclaration, but if we do measure we'll - // need a way to call the Servo heap_size_of function for the - // declaration block. + // Bug 1281964: For ServoDeclarationBlock if we do measure we'll + // need a way to call the Servo heap_size_of function. + //n += container->mCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf); } else if (Type() == eAtomArray && container->mValue.mAtomArray) { // Don't measure each nsIAtom, they are measured separatly. n += container->mValue.mAtomArray->ShallowSizeOfIncludingThis(aMallocSizeOf); diff --git a/dom/base/nsAttrValue.h b/dom/base/nsAttrValue.h index a622f7cfb1e5..655e4ca619a4 100644 --- a/dom/base/nsAttrValue.h +++ b/dom/base/nsAttrValue.h @@ -35,11 +35,10 @@ class nsAString; class nsIDocument; class nsStyledElement; struct MiscContainer; -struct RawServoDeclarationBlock; namespace mozilla { +class DeclarationBlock; namespace css { -class Declaration; struct URLValue; struct ImageValue; } // namespace css @@ -98,8 +97,7 @@ public: ePercent = 0x0F, // 1111 // Values below here won't matter, they'll be always stored in the 'misc' // struct. - eGeckoCSSDeclaration = 0x10, - eServoCSSDeclaration, + eCSSDeclaration = 0x10, eURL, eImage, eAtomArray, @@ -125,7 +123,8 @@ public: nsAttrValue(const nsAttrValue& aOther); explicit nsAttrValue(const nsAString& aValue); explicit nsAttrValue(nsIAtom* aValue); - nsAttrValue(mozilla::css::Declaration* aValue, const nsAString* aSerialized); + nsAttrValue(already_AddRefed aValue, + const nsAString* aSerialized); explicit nsAttrValue(const nsIntMargin& aValue); ~nsAttrValue(); @@ -150,8 +149,7 @@ public: void SetTo(int16_t aInt); void SetTo(int32_t aInt, const nsAString* aSerialized); void SetTo(double aValue, const nsAString* aSerialized); - void SetTo(mozilla::css::Declaration* aValue, const nsAString* aSerialized); - void SetTo(already_AddRefed aDeclarationBlock, + void SetTo(already_AddRefed aValue, const nsAString* aSerialized); void SetTo(mozilla::css::URLValue* aValue, const nsAString* aSerialized); void SetTo(const nsIntMargin& aValue); @@ -203,8 +201,7 @@ public: inline int16_t GetEnumValue() const; inline float GetPercentValue() const; inline AtomArray* GetAtomArrayValue() const; - inline mozilla::css::Declaration* GetGeckoCSSDeclarationValue() const; - inline RawServoDeclarationBlock* GetServoCSSDeclarationValue() const; + inline mozilla::DeclarationBlock* GetCSSDeclarationValue() const; inline mozilla::css::URLValue* GetURLValue() const; inline mozilla::css::ImageValue* GetImageValue() const; inline double GetDoubleValue() const; diff --git a/dom/base/nsAttrValueInlines.h b/dom/base/nsAttrValueInlines.h index 1f65314ad59e..d2749d486ca5 100644 --- a/dom/base/nsAttrValueInlines.h +++ b/dom/base/nsAttrValueInlines.h @@ -20,7 +20,7 @@ struct MiscContainer final ValueType mType; // mStringBits points to either nsIAtom* or nsStringBuffer* and is used when - // mType isn't eGeckoCSSDeclaration. + // mType isn't eCSSDeclaration. // Note eStringBase and eAtomBase is used also to handle the type of // mStringBits. uintptr_t mStringBits; @@ -31,8 +31,7 @@ struct MiscContainer final nscolor mColor; uint32_t mEnumValue; int32_t mPercent; - mozilla::css::Declaration* mGeckoCSSDeclaration; - RawServoDeclarationBlock* mServoCSSDeclaration; + mozilla::DeclarationBlock* mCSSDeclaration; mozilla::css::URLValue* mURL; mozilla::css::ImageValue* mImage; nsAttrValue::AtomArray* mAtomArray; @@ -87,8 +86,7 @@ public: // Nothing stops us from refcounting (and sharing) other types of // MiscContainer (except eDoubleValue types) but there's no compelling // reason to. - return mType == nsAttrValue::eGeckoCSSDeclaration || - mType == nsAttrValue::eServoCSSDeclaration; + return mType == nsAttrValue::eCSSDeclaration; } inline int32_t AddRef() { @@ -148,18 +146,11 @@ nsAttrValue::GetAtomArrayValue() const return GetMiscContainer()->mValue.mAtomArray; } -inline mozilla::css::Declaration* -nsAttrValue::GetGeckoCSSDeclarationValue() const +inline mozilla::DeclarationBlock* +nsAttrValue::GetCSSDeclarationValue() const { - NS_PRECONDITION(Type() == eGeckoCSSDeclaration, "wrong type"); - return GetMiscContainer()->mValue.mGeckoCSSDeclaration; -} - -inline RawServoDeclarationBlock* -nsAttrValue::GetServoCSSDeclarationValue() const -{ - NS_PRECONDITION(Type() == eServoCSSDeclaration, "wrong type"); - return GetMiscContainer()->mValue.mServoCSSDeclaration; + NS_PRECONDITION(Type() == eCSSDeclaration, "wrong type"); + return GetMiscContainer()->mValue.mCSSDeclaration; } inline mozilla::css::URLValue* @@ -207,9 +198,7 @@ nsAttrValue::StoresOwnData() const return true; } ValueType t = Type(); - return t != eGeckoCSSDeclaration && - t != eServoCSSDeclaration && - !IsSVGType(t); + return t != eCSSDeclaration && !IsSVGType(t); } inline void diff --git a/dom/base/nsStyledElement.cpp b/dom/base/nsStyledElement.cpp index 2d759b20ec26..03d1187ab186 100644 --- a/dom/base/nsStyledElement.cpp +++ b/dom/base/nsStyledElement.cpp @@ -14,7 +14,7 @@ #include "nsDOMCSSAttrDeclaration.h" #include "nsServiceManagerUtils.h" #include "nsIDocument.h" -#include "mozilla/css/Declaration.h" +#include "mozilla/DeclarationBlockInlines.h" #include "nsCSSParser.h" #include "mozilla/css/Loader.h" #include "nsIDOMMutationEvent.h" @@ -49,7 +49,7 @@ nsStyledElement::ParseAttribute(int32_t aNamespaceID, } nsresult -nsStyledElement::SetInlineStyleDeclaration(css::Declaration* aDeclaration, +nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration, const nsAString* aSerialized, bool aNotify) { @@ -81,7 +81,7 @@ nsStyledElement::SetInlineStyleDeclaration(css::Declaration* aDeclaration, modification = !!mAttrsAndChildren.GetAttr(nsGkAtoms::style); } - nsAttrValue attrValue(aDeclaration, aSerialized); + nsAttrValue attrValue(do_AddRef(aDeclaration), aSerialized); // XXXbz do we ever end up with ADDITION here? I doubt it. uint8_t modType = modification ? @@ -93,7 +93,7 @@ nsStyledElement::SetInlineStyleDeclaration(css::Declaration* aDeclaration, aNotify, kDontCallAfterSetAttr); } -css::Declaration* +DeclarationBlock* nsStyledElement::GetInlineStyleDeclaration() { if (!MayHaveStyle()) { @@ -101,8 +101,8 @@ nsStyledElement::GetInlineStyleDeclaration() } const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); - if (attrVal && attrVal->Type() == nsAttrValue::eGeckoCSSDeclaration) { - return attrVal->GetGeckoCSSDeclarationValue(); + if (attrVal && attrVal->Type() == nsAttrValue::eCSSDeclaration) { + return attrVal->GetCSSDeclarationValue(); } return nullptr; @@ -134,13 +134,7 @@ nsStyledElement::ReparseStyleAttribute(bool aForceInDataDoc) return NS_OK; } const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); - - nsAttrValue::ValueType desiredType = - OwnerDoc()->GetStyleBackendType() == StyleBackendType::Gecko ? - nsAttrValue::eGeckoCSSDeclaration : - nsAttrValue::eServoCSSDeclaration; - - if (oldVal && oldVal->Type() != desiredType) { + if (oldVal && oldVal->Type() != nsAttrValue::eCSSDeclaration) { nsAttrValue attrValue; nsAutoString stringValue; oldVal->ToString(stringValue); diff --git a/dom/base/nsStyledElement.h b/dom/base/nsStyledElement.h index 024826b5961b..c4894d87f1e7 100644 --- a/dom/base/nsStyledElement.h +++ b/dom/base/nsStyledElement.h @@ -18,9 +18,7 @@ #include "mozilla/dom/Element.h" namespace mozilla { -namespace css { -class Declaration; -} // namespace css +class DeclarationBlock; } // namespace mozilla // IID for nsStyledElement interface @@ -46,8 +44,8 @@ public: NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override; // Element interface methods - virtual mozilla::css::Declaration* GetInlineStyleDeclaration() override; - virtual nsresult SetInlineStyleDeclaration(mozilla::css::Declaration* aDeclaration, + virtual mozilla::DeclarationBlock* GetInlineStyleDeclaration() override; + virtual nsresult SetInlineStyleDeclaration(mozilla::DeclarationBlock* aDeclaration, const nsAString* aSerialized, bool aNotify) override; diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index edf847a60978..3b425dde2ed1 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/ArrayUtils.h" +#include "mozilla/DeclarationBlockInlines.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" #include "mozilla/EventStateManager.h" @@ -187,12 +188,18 @@ nsGenericHTMLElement::CopyInnerTo(Element* aDst) value->ToString(valStr); if (name->Equals(nsGkAtoms::style, kNameSpaceID_None) && - value->Type() == nsAttrValue::eGeckoCSSDeclaration) { + value->Type() == nsAttrValue::eCSSDeclaration) { + DeclarationBlock* decl = value->GetCSSDeclarationValue(); + if (decl->IsServo()) { + MOZ_CRASH("stylo: clone not implemented"); + continue; + } + // We can't just set this as a string, because that will fail // to reparse the string into style data until the node is // inserted into the document. Clone the Rule instead. - RefPtr declClone = - new css::Declaration(*value->GetGeckoCSSDeclarationValue()); + RefPtr + declClone = new css::Declaration(*decl->AsGecko()); rv = aDst->SetInlineStyleDeclaration(declClone, &valStr, false); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/svg/nsSVGElement.cpp b/dom/svg/nsSVGElement.cpp index 021a35116375..ce849acf0fe4 100644 --- a/dom/svg/nsSVGElement.cpp +++ b/dom/svg/nsSVGElement.cpp @@ -258,7 +258,7 @@ nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, } const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); - if (oldVal && oldVal->Type() == nsAttrValue::eGeckoCSSDeclaration) { + if (oldVal && oldVal->Type() == nsAttrValue::eCSSDeclaration) { // we need to force a reparse because the baseURI of the document // may have changed, and in particular because we may be clones of // XBL anonymous content now being bound to the document we should diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 59636f7b4872..6c0a9a3d328c 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -38,6 +38,7 @@ #include "mozilla/EventListenerManager.h" #include "mozilla/EventStateManager.h" #include "mozilla/EventStates.h" +#include "mozilla/DeclarationBlockInlines.h" #include "nsFocusManager.h" #include "nsHTMLStyleSheet.h" #include "nsNameSpaceManager.h" @@ -374,14 +375,15 @@ nsXULElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const nsAttrValue attrValue; // Style rules need to be cloned. - if (originalValue->Type() == nsAttrValue::eGeckoCSSDeclaration) { - RefPtr declClone = new css::Declaration( - *originalValue->GetGeckoCSSDeclarationValue()); + if (originalValue->Type() == nsAttrValue::eCSSDeclaration) { + DeclarationBlock* decl = originalValue->GetCSSDeclarationValue(); + RefPtr + declClone = new css::Declaration(*decl->AsGecko()); nsString stringValue; originalValue->ToString(stringValue); - attrValue.SetTo(declClone, &stringValue); + attrValue.SetTo(declClone.forget(), &stringValue); } else { attrValue.SetTo(*originalValue); } @@ -1860,14 +1862,15 @@ nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype) nsAttrValue attrValue; // Style rules need to be cloned. - if (protoattr->mValue.Type() == nsAttrValue::eGeckoCSSDeclaration) { - RefPtr declClone = new css::Declaration( - *protoattr->mValue.GetGeckoCSSDeclarationValue()); + if (protoattr->mValue.Type() == nsAttrValue::eCSSDeclaration) { + DeclarationBlock* decl = protoattr->mValue.GetCSSDeclarationValue(); + RefPtr + declClone = new css::Declaration(*decl->AsGecko()); nsString stringValue; protoattr->mValue.ToString(stringValue); - attrValue.SetTo(declClone, &stringValue); + attrValue.SetTo(declClone.forget(), &stringValue); } else { attrValue.SetTo(protoattr->mValue); } @@ -2450,7 +2453,7 @@ nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue, mNodeInfo->NodeInfoManager()-> DocumentPrincipal()); if (declaration) { - mAttributes[aPos].mValue.SetTo(declaration, &aValue); + mAttributes[aPos].mValue.SetTo(declaration.forget(), &aValue); return NS_OK; } diff --git a/editor/libeditor/CSSEditUtils.cpp b/editor/libeditor/CSSEditUtils.cpp index 871b374f2903..b3259caeb474 100644 --- a/editor/libeditor/CSSEditUtils.cpp +++ b/editor/libeditor/CSSEditUtils.cpp @@ -9,7 +9,7 @@ #include "mozilla/ChangeStyleTransaction.h" #include "mozilla/HTMLEditor.h" #include "mozilla/Preferences.h" -#include "mozilla/css/Declaration.h" +#include "mozilla/DeclarationBlockInlines.h" #include "mozilla/css/StyleRule.h" #include "mozilla/dom/Element.h" #include "mozilla/mozalloc.h" @@ -544,15 +544,19 @@ CSSEditUtils::GetCSSInlinePropertyBase(nsINode* aNode, } MOZ_ASSERT(aStyleType == eSpecified); - RefPtr decl = element->GetInlineStyleDeclaration(); + RefPtr decl = element->GetInlineStyleDeclaration(); if (!decl) { return NS_OK; } + if (decl->IsServo()) { + MOZ_CRASH("stylo: not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + } nsCSSPropertyID prop = nsCSSProps::LookupProperty(nsDependentAtomString(aProperty), CSSEnabledState::eForAllContent); MOZ_ASSERT(prop != eCSSProperty_UNKNOWN); - decl->GetValue(prop, aValue); + decl->AsGecko()->GetValue(prop, aValue); return NS_OK; } diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index 616d0e71f273..1ebe4d856c6c 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -29,6 +29,7 @@ #include "mozilla/EventStates.h" #include "mozilla/ServoElementSnapshot.h" #include "mozilla/ServoRestyleManager.h" +#include "mozilla/DeclarationBlockInlines.h" #include "mozilla/dom/Element.h" using namespace mozilla; @@ -316,10 +317,21 @@ RawServoDeclarationBlock* Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed aElement) { const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style); - if (!attr || attr->Type() != nsAttrValue::eServoCSSDeclaration) { + if (!attr || attr->Type() != nsAttrValue::eCSSDeclaration) { return nullptr; } - return attr->GetServoCSSDeclarationValue(); + DeclarationBlock* decl = attr->GetCSSDeclarationValue(); + if (!decl) { + return nullptr; + } + if (decl->IsGecko()) { + // XXX This can happen at least when script sets style attribute + // since we haven't implemented Element.style for stylo. But + // we may want to turn it into an assertion after that's done. + NS_WARNING("stylo: requesting a Gecko declaration block?"); + return nullptr; + } + return decl->AsServo()->RefRaw(); } void diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp index 8105301cd77e..b593ec2a1075 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.cpp +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -92,10 +92,12 @@ nsDOMCSSAttributeDeclaration::GetCSSDeclaration(Operation aOperation) return nullptr; css::Declaration* declaration; - if (mIsSMILOverride) + if (mIsSMILOverride) { declaration = mElement->GetSMILOverrideStyleDeclaration(); - else - declaration = mElement->GetInlineStyleDeclaration(); + } else { + DeclarationBlock* decl = mElement->GetInlineStyleDeclaration(); + declaration = decl && decl->IsGecko() ? decl->AsGecko() : nullptr; + } // Notify observers that our style="" attribute is going to change // unless: diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index 088adaab4541..b7d8636b5463 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -10,7 +10,7 @@ #include "nsCSSParser.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/css/Rule.h" -#include "mozilla/css/Declaration.h" +#include "mozilla/DeclarationBlockInlines.h" #include "mozilla/dom/CSS2PropertiesBinding.h" #include "nsCSSProps.h" #include "nsCOMPtr.h" diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index a941307972ae..de901e98c4ff 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -11,6 +11,7 @@ #include "nsHTMLCSSStyleSheet.h" #include "mozilla/MemoryReporting.h" #include "mozilla/css/StyleRule.h" +#include "mozilla/DeclarationBlockInlines.h" #include "nsIStyleRuleProcessor.h" #include "nsPresContext.h" #include "nsRuleWalker.h" @@ -38,21 +39,11 @@ nsHTMLCSSStyleSheet::~nsHTMLCSSStyleSheet() // Ideally we'd just call MiscContainer::Evict, but we can't do that since // we're iterating the hashtable. - switch (value->mType) { - case nsAttrValue::eGeckoCSSDeclaration: { - css::Declaration* declaration = value->mValue.mGeckoCSSDeclaration; - declaration->SetHTMLCSSStyleSheet(nullptr); - break; - } - case nsAttrValue::eServoCSSDeclaration: { - RawServoDeclarationBlock* declarations = - value->mValue.mServoCSSDeclaration; - Servo_DeclarationBlock_ClearCachePointer(declarations); - break; - } - default: - MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); - break; + if (value->mType == nsAttrValue::eCSSDeclaration) { + DeclarationBlock* declaration = value->mValue.mCSSDeclaration; + declaration->SetHTMLCSSStyleSheet(nullptr); + } else { + MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); } value->mValue.mCached = 0; @@ -75,13 +66,12 @@ nsHTMLCSSStyleSheet::ElementRulesMatching(nsPresContext* aPresContext, nsRuleWalker* aRuleWalker) { // just get the one and only style rule from the content's STYLE attribute - css::Declaration* declaration = aElement->GetInlineStyleDeclaration(); - if (declaration) { + if (DeclarationBlock* declaration = aElement->GetInlineStyleDeclaration()) { declaration->SetImmutable(); - aRuleWalker->Forward(declaration); + aRuleWalker->Forward(declaration->AsGecko()); } - declaration = aElement->GetSMILOverrideStyleDeclaration(); + css::Declaration* declaration = aElement->GetSMILOverrideStyleDeclaration(); if (declaration) { MOZ_ASSERT(aPresContext->RestyleManager()->IsGecko(), "stylo: ElementRulesMatching must not be called when we have " @@ -107,10 +97,10 @@ nsHTMLCSSStyleSheet::PseudoElementRulesMatching(Element* aPseudoElement, MOZ_ASSERT(aPseudoElement); // just get the one and only style rule from the content's STYLE attribute - css::Declaration* declaration = aPseudoElement->GetInlineStyleDeclaration(); + DeclarationBlock* declaration = aPseudoElement->GetInlineStyleDeclaration(); if (declaration) { declaration->SetImmutable(); - aRuleWalker->Forward(declaration); + aRuleWalker->Forward(declaration->AsGecko()); } } From e110cbadaba953a75ae9f99e77075cc8447d4f8b Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 11/80] Bug 1309109 part 6 - Declare nullable strong borrowed ref type for RawServoDeclarationBlock. r=mystor MozReview-Commit-ID: 2SDPK6lp4ue --HG-- extra : source : 96f1ccc5f0b60b92d56de0ff75be011ac52cbb6f --- layout/style/ServoBindings.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 0b131851ce9c..23a9d2bbf571 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -102,6 +102,9 @@ using mozilla::dom::StyleChildrenIterator; DECL_ARC_REF_TYPE_FOR(ServoComputedValues) DECL_ARC_REF_TYPE_FOR(RawServoStyleSheet) DECL_ARC_REF_TYPE_FOR(RawServoDeclarationBlock) +// This is a reference to a reference of RawServoDeclarationBlock, which +// corresponds to Option<&Arc> in Servo side. +DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawServoDeclarationBlockStrong) DECL_OWNED_REF_TYPE_FOR(RawServoStyleSet) DECL_NULLABLE_OWNED_REF_TYPE_FOR(StyleChildrenIterator) From e49cb9930b5522fb8e8d3d21ba886fadbb66f837 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 15:29:03 +1100 Subject: [PATCH 12/80] Bug 1309109 part 7 - Remove the extra level of GeckoDeclarationBlock. r=manishearth,mystor MozReview-Commit-ID: 2TzqrAWbaeD --HG-- extra : source : fdd8dfc2cbe94dac981783c41c92188398c2f27f --- layout/style/ServoBindingList.h | 9 +-------- layout/style/ServoBindings.cpp | 5 +++-- layout/style/ServoBindings.h | 2 +- layout/style/ServoDeclarationBlock.cpp | 3 +-- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index b318e9980877..a8fd13440c70 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -66,8 +66,7 @@ SERVO_BINDING_FUNC(Servo_RestyleWithAddedDeclaration, // Style attribute SERVO_BINDING_FUNC(Servo_ParseStyleAttribute, RawServoDeclarationBlockStrong, - const uint8_t* bytes, uint32_t length, - nsHTMLCSSStyleSheet* cache) + const uint8_t* bytes, uint32_t length) SERVO_BINDING_FUNC(Servo_DeclarationBlock_AddRef, void, RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_Release, void, @@ -75,12 +74,6 @@ SERVO_BINDING_FUNC(Servo_DeclarationBlock_Release, void, SERVO_BINDING_FUNC(Servo_DeclarationBlock_Equals, bool, RawServoDeclarationBlockBorrowed a, RawServoDeclarationBlockBorrowed b) -SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetCache, nsHTMLCSSStyleSheet*, - RawServoDeclarationBlockBorrowed declarations) -SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetImmutable, void, - RawServoDeclarationBlockBorrowed declarations) -SERVO_BINDING_FUNC(Servo_DeclarationBlock_ClearCachePointer, void, - RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_SerializeOneValue, void, RawServoDeclarationBlockBorrowed declarations, nsString* buffer) diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index 1ebe4d856c6c..e9aba4e618c6 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -313,7 +313,7 @@ Gecko_StoreStyleDifference(RawGeckoNodeBorrowed aNode, nsChangeHint aChangeHintT #endif } -RawServoDeclarationBlock* +RawServoDeclarationBlockStrongBorrowedOrNull Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed aElement) { const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style); @@ -331,7 +331,8 @@ Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed aElement) NS_WARNING("stylo: requesting a Gecko declaration block?"); return nullptr; } - return decl->AsServo()->RefRaw(); + return reinterpret_cast + (&decl->AsServo()->RefRaw()); } void diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 23a9d2bbf571..3eb59006880d 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -219,7 +219,7 @@ SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, #undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS // Style attributes. -RawServoDeclarationBlockBorrowedOrNull +RawServoDeclarationBlockStrongBorrowedOrNull Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed element); // Atoms. diff --git a/layout/style/ServoDeclarationBlock.cpp b/layout/style/ServoDeclarationBlock.cpp index b98cc91d3698..616be583c3a2 100644 --- a/layout/style/ServoDeclarationBlock.cpp +++ b/layout/style/ServoDeclarationBlock.cpp @@ -14,8 +14,7 @@ ServoDeclarationBlock::FromStyleAttribute(const nsAString& aString) { NS_ConvertUTF16toUTF8 value(aString); RefPtr raw = Servo_ParseStyleAttribute( - reinterpret_cast(value.get()), - value.Length(), nullptr).Consume(); + reinterpret_cast(value.get()), value.Length()).Consume(); RefPtr decl = new ServoDeclarationBlock(raw.forget()); return decl.forget(); } From 403c55a07bd3510f12e3cbe3b4400466ab0cc384 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 18 Oct 2016 16:11:15 +1100 Subject: [PATCH 13/80] Bug 1309109 followup - Fix bustage. MozReview-Commit-ID: 7n2YM7JjRlw --- layout/style/ServoBindings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index e9aba4e618c6..172b8629387c 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -332,7 +332,7 @@ Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed aElement) return nullptr; } return reinterpret_cast - (&decl->AsServo()->RefRaw()); + (decl->AsServo()->RefRaw()); } void From e78e23c03d5b0034399205b55bcd3f71972751ba Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Mon, 17 Oct 2016 22:37:52 -0700 Subject: [PATCH 14/80] Bug 1310399 - Fix -Wshadow warning in widget/nsBaseWidget.cpp. r=jimm widget/nsBaseWidget.cpp:1970:14 [-Wshadow] declaration shadows a local variable --- widget/moz.build | 3 --- widget/nsBaseWidget.cpp | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/widget/moz.build b/widget/moz.build index 86c72d8debea..ed09fd553699 100644 --- a/widget/moz.build +++ b/widget/moz.build @@ -284,6 +284,3 @@ if CONFIG['MOZ_ENABLE_D3D10_LAYER']: DEFINES['MOZ_ENABLE_D3D10_LAYER'] = True CXXFLAGS += CONFIG['TK_CFLAGS'] - -if CONFIG['GNU_CXX']: - CXXFLAGS += ['-Wno-error=shadow'] diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 413b35439d69..ce177b965a6b 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -1967,9 +1967,8 @@ nsIWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint, bool aLongTap, } if (!aLongTap) { - nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE, - aPoint, 0, 0, nullptr); - return rv; + return SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE, + aPoint, 0, 0, nullptr); } // initiate a long tap From 8cabcf6f572ae7f47b2c0cfe4bfb8832fac1c7fa Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Fri, 14 Oct 2016 15:35:31 +0200 Subject: [PATCH 15/80] Bug 1277011 - Wasm baseline: refactor for portability. r=h4writer --HG-- extra : rebase_source : ba0c0d3c4f7ad1d075cefd9b63960fb970f7c79e --- js/src/asmjs/WasmBaselineCompile.cpp | 175 +++++++++++++-------------- 1 file changed, 82 insertions(+), 93 deletions(-) diff --git a/js/src/asmjs/WasmBaselineCompile.cpp b/js/src/asmjs/WasmBaselineCompile.cpp index f7a4010a7fef..3c9c67b09e44 100644 --- a/js/src/asmjs/WasmBaselineCompile.cpp +++ b/js/src/asmjs/WasmBaselineCompile.cpp @@ -25,13 +25,10 @@ * Unimplemented functionality: * * - Tiered compilation (bug 1277562) - * - int64 operations on 32-bit systems - * - SIMD - * - Atomics (very simple now, we have range checking) - * - current_memory, grow_memory - * - non-signaling interrupts - * - profiler support (devtools) * - ARM-32 support (bug 1277011) + * - SIMD + * - Atomics + * - profiler support (devtools) * * There are lots of machine dependencies here but they are pretty * well isolated to a segment of the compiler. Many dependencies @@ -166,6 +163,9 @@ static const Register StackPointer = RealStackPointer; // EBX not being one of the WasmTableCall registers; and needing a // temp register for load/store that has a single-byte persona. static const Register ScratchRegX86 = ebx; + +# define QUOT_REM_I64_CALLOUT + #endif class BaseCompiler @@ -2469,8 +2469,8 @@ class BaseCompiler #endif } +#ifndef QUOT_REM_I64_CALLOUT void quotientI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned) { - // This follows quotientI32, above. Label done; checkDivideByZeroI64(rhs, srcDest, &done); @@ -2478,7 +2478,7 @@ class BaseCompiler if (!isUnsigned) checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false)); -#if defined(JS_CODEGEN_X64) +# if defined(JS_CODEGEN_X64) // The caller must set up the following situation. MOZ_ASSERT(srcDest.reg.reg == rax); MOZ_ASSERT(isAvailable(rdx)); @@ -2489,12 +2489,14 @@ class BaseCompiler masm.cqo(); masm.idivq(rhs.reg.reg); } -#else +# else MOZ_CRASH("BaseCompiler platform hook: quotientI64"); -#endif +# endif masm.bind(&done); } +#endif +#ifndef QUOT_REM_I64_CALLOUT void remainderI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned) { Label done; @@ -2503,7 +2505,7 @@ class BaseCompiler if (!isUnsigned) checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true)); -#if defined(JS_CODEGEN_X64) +# if defined(JS_CODEGEN_X64) // The caller must set up the following situation. MOZ_ASSERT(srcDest.reg.reg == rax); MOZ_ASSERT(isAvailable(rdx)); @@ -2516,11 +2518,12 @@ class BaseCompiler masm.idivq(rhs.reg.reg); } masm.movq(rdx, rax); -#else +# else MOZ_CRASH("BaseCompiler platform hook: remainderI64"); -#endif +# endif masm.bind(&done); } +#endif void orI64(RegI64 rhs, RegI64 srcDest) { masm.or64(rhs.reg, srcDest.reg); @@ -3302,9 +3305,9 @@ class BaseCompiler bool emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType); MOZ_MUST_USE bool emitBinaryMathBuiltinCall(SymbolicAddress callee, ValType operandType); -#ifdef JS_NUNBOX32 - void emitDivOrModI64BuiltinCall(SymbolicAddress callee, RegI64 rhs, RegI64 srcDest, - RegI32 temp); +#ifdef QUOT_REM_I64_CALLOUT + MOZ_MUST_USE + bool emitDivOrModI64BuiltinCall(SymbolicAddress callee, ValType operandType); #endif MOZ_MUST_USE bool emitGetLocal(); @@ -3356,12 +3359,14 @@ class BaseCompiler void emitMultiplyF64(); void emitQuotientI32(); void emitQuotientU32(); - void emitQuotientI64(); - void emitQuotientU64(); void emitRemainderI32(); void emitRemainderU32(); +#ifndef QUOT_REM_I64_CALLOUT + void emitQuotientI64(); + void emitQuotientU64(); void emitRemainderI64(); void emitRemainderU64(); +#endif void emitDivideF32(); void emitDivideF64(); void emitMinI32(); @@ -3636,75 +3641,51 @@ BaseCompiler::emitQuotientU32() pushI32(r0); } +#ifndef QUOT_REM_I64_CALLOUT void BaseCompiler::emitQuotientI64() { -#ifdef JS_PUNBOX64 +# ifdef JS_PUNBOX64 RegI64 r0, r1; -# ifdef JS_CODEGEN_X64 +# ifdef JS_CODEGEN_X64 // srcDest must be rax, and rdx will be clobbered. need2xI64(specific_rax, specific_rdx); r1 = popI64(); r0 = popI64ToSpecific(specific_rax); freeI64(specific_rdx); -# else +# else pop2xI64(&r0, &r1); -# endif +# endif quotientI64(r1, r0, IsUnsigned(false)); freeI64(r1); pushI64(r0); -#else -# if defined(JS_CODEGEN_X86) - RegI64 r0, r1; - RegI32 temp; - needI64(abiReturnRegI64); - temp = needI32(); - r1 = popI64(); - r0 = popI64ToSpecific(abiReturnRegI64); - emitDivOrModI64BuiltinCall(SymbolicAddress::DivI64, r1, r0, temp); - freeI32(temp); - freeI64(r1); - pushI64(r0); # else MOZ_CRASH("BaseCompiler platform hook: emitQuotientI64"); # endif -#endif } void BaseCompiler::emitQuotientU64() { -#ifdef JS_PUNBOX64 +# ifdef JS_PUNBOX64 RegI64 r0, r1; -# ifdef JS_CODEGEN_X64 +# ifdef JS_CODEGEN_X64 // srcDest must be rax, and rdx will be clobbered. need2xI64(specific_rax, specific_rdx); r1 = popI64(); r0 = popI64ToSpecific(specific_rax); freeI64(specific_rdx); -# else +# else pop2xI64(&r0, &r1); -# endif +# endif quotientI64(r1, r0, IsUnsigned(true)); freeI64(r1); pushI64(r0); -#else -# if defined(JS_CODEGEN_X86) - RegI64 r0, r1; - RegI32 temp; - needI64(abiReturnRegI64); - temp = needI32(); - r1 = popI64(); - r0 = popI64ToSpecific(abiReturnRegI64); - emitDivOrModI64BuiltinCall(SymbolicAddress::UDivI64, r1, r0, temp); - freeI32(temp); - freeI64(r1); - pushI64(r0); # else MOZ_CRASH("BaseCompiler platform hook: emitQuotientU64"); # endif -#endif } +#endif void BaseCompiler::emitRemainderI32() @@ -3755,73 +3736,49 @@ BaseCompiler::emitRemainderU32() pushI32(r0); } +#ifndef QUOT_REM_I64_CALLOUT void BaseCompiler::emitRemainderI64() { -#ifdef JS_PUNBOX64 +# ifdef JS_PUNBOX64 RegI64 r0, r1; -# ifdef JS_CODEGEN_X64 +# ifdef JS_CODEGEN_X64 need2xI64(specific_rax, specific_rdx); r1 = popI64(); r0 = popI64ToSpecific(specific_rax); freeI64(specific_rdx); -# else +# else pop2xI64(&r0, &r1); -# endif +# endif remainderI64(r1, r0, IsUnsigned(false)); freeI64(r1); pushI64(r0); -#else -# if defined(JS_CODEGEN_X86) - RegI64 r0, r1; - RegI32 temp; - needI64(abiReturnRegI64); - temp = needI32(); - r1 = popI64(); - r0 = popI64ToSpecific(abiReturnRegI64); - emitDivOrModI64BuiltinCall(SymbolicAddress::ModI64, r1, r0, temp); - freeI32(temp); - freeI64(r1); - pushI64(r0); # else MOZ_CRASH("BaseCompiler platform hook: emitRemainderI64"); # endif -#endif } void BaseCompiler::emitRemainderU64() { -#ifdef JS_PUNBOX64 +# ifdef JS_PUNBOX64 RegI64 r0, r1; -# ifdef JS_CODEGEN_X64 +# ifdef JS_CODEGEN_X64 need2xI64(specific_rax, specific_rdx); r1 = popI64(); r0 = popI64ToSpecific(specific_rax); freeI64(specific_rdx); -# else +# else pop2xI64(&r0, &r1); -# endif +# endif remainderI64(r1, r0, IsUnsigned(true)); freeI64(r1); pushI64(r0); -#else -# if defined(JS_CODEGEN_X86) - RegI64 r0, r1; - RegI32 temp; - needI64(abiReturnRegI64); - temp = needI32(); - r1 = popI64(); - r0 = popI64ToSpecific(abiReturnRegI64); - emitDivOrModI64BuiltinCall(SymbolicAddress::UModI64, r1, r0, temp); - freeI32(temp); - freeI64(r1); - pushI64(r0); # else MOZ_CRASH("BaseCompiler platform hook: emitRemainderU64"); # endif -#endif } +#endif void BaseCompiler::emitDivideF32() @@ -5493,15 +5450,25 @@ BaseCompiler::emitBinaryMathBuiltinCall(SymbolicAddress callee, ValType operandT return true; } -#ifdef JS_NUNBOX32 -void -BaseCompiler::emitDivOrModI64BuiltinCall(SymbolicAddress callee, RegI64 rhs, RegI64 srcDest, - RegI32 temp) +#ifdef QUOT_REM_I64_CALLOUT +bool +BaseCompiler::emitDivOrModI64BuiltinCall(SymbolicAddress callee, ValType operandType) { - Label done; + MOZ_ASSERT(operandType == ValType::I64); + + if (deadCode_) + return true; sync(); + needI64(abiReturnRegI64); + + RegI32 temp = needI32(); + RegI64 rhs = popI64(); + RegI64 srcDest = popI64ToSpecific(abiReturnRegI64); + + Label done; + checkDivideByZeroI64(rhs, srcDest, &done); if (callee == SymbolicAddress::DivI64) @@ -5516,9 +5483,13 @@ BaseCompiler::emitDivOrModI64BuiltinCall(SymbolicAddress callee, RegI64 rhs, Reg masm.passABIArg(rhs.reg.low); masm.callWithABI(callee); - MOZ_ASSERT(abiReturnRegI64.reg == srcDest.reg); - masm.bind(&done); + + freeI32(temp); + freeI64(rhs); + pushI64(srcDest); + + return true; } #endif @@ -6544,13 +6515,29 @@ BaseCompiler::emitBody() case Expr::I64Mul: CHECK_NEXT(emitBinary(emitMultiplyI64, ValType::I64)); case Expr::I64DivS: +#ifdef QUOT_REM_I64_CALLOUT + CHECK_NEXT(emitDivOrModI64BuiltinCall(SymbolicAddress::DivI64, ValType::I64)); +#else CHECK_NEXT(emitBinary(emitQuotientI64, ValType::I64)); +#endif case Expr::I64DivU: +#ifdef QUOT_REM_I64_CALLOUT + CHECK_NEXT(emitDivOrModI64BuiltinCall(SymbolicAddress::UDivI64, ValType::I64)); +#else CHECK_NEXT(emitBinary(emitQuotientU64, ValType::I64)); +#endif case Expr::I64RemS: +#ifdef QUOT_REM_I64_CALLOUT + CHECK_NEXT(emitDivOrModI64BuiltinCall(SymbolicAddress::ModI64, ValType::I64)); +#else CHECK_NEXT(emitBinary(emitRemainderI64, ValType::I64)); +#endif case Expr::I64RemU: +#ifdef QUOT_REM_I64_CALLOUT + CHECK_NEXT(emitDivOrModI64BuiltinCall(SymbolicAddress::UModI64, ValType::I64)); +#else CHECK_NEXT(emitBinary(emitRemainderU64, ValType::I64)); +#endif case Expr::I64TruncSF32: CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64, ValType::F32, ValType::I64)); case Expr::I64TruncUF32: @@ -7198,3 +7185,5 @@ js::wasm::BaselineCompileFunction(IonCompileTask* task) return true; } + +#undef QUOT_REM_I64_CALLOUT From 87f566bd8daf43030f905435cd8b17531b5a97b7 Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Fri, 14 Oct 2016 15:36:29 +0200 Subject: [PATCH 16/80] Bug 1277011 - Wasm baseline: fix joinReg reservation on 32-bit platforms. r=h4writer --HG-- extra : rebase_source : 4af5ad58e5a09e46d3a6f7717d96613796c432a3 --- js/src/asmjs/WasmBaselineCompile.cpp | 32 +++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/js/src/asmjs/WasmBaselineCompile.cpp b/js/src/asmjs/WasmBaselineCompile.cpp index 3c9c67b09e44..8ab5f39fffb1 100644 --- a/js/src/asmjs/WasmBaselineCompile.cpp +++ b/js/src/asmjs/WasmBaselineCompile.cpp @@ -1729,6 +1729,20 @@ class BaseCompiler } } + void maybeReserveJoinRegI(ExprType type) { + if (type == ExprType::I32) + needI32(joinRegI32); + else if (type == ExprType::I64) + needI64(joinRegI64); + } + + void maybeUnreserveJoinRegI(ExprType type) { + if (type == ExprType::I32) + freeI32(joinRegI32); + else if (type == ExprType::I64) + freeI64(joinRegI64); + } + // Return the amount of execution stack consumed by the top numval // values on the value stack. @@ -4921,16 +4935,13 @@ BaseCompiler::emitBrIf() // allowing a conditional expression to be left on the stack and // reified here as part of the branch instruction. - // We'll need the joinreg later, so don't use it for rc. - // We assume joinRegI32 and joinRegI64 overlap. - if (type == ExprType::I32 || type == ExprType::I64) - needI32(joinRegI32); + // Don't use joinReg for rc + maybeReserveJoinRegI(type); // Condition value is on top, always I32. RegI32 rc = popI32(); - if (type == ExprType::I32 || type == ExprType::I64) - freeI32(joinRegI32); + maybeUnreserveJoinRegI(type); // Save any value in the designated join register, where the // normal block exit code will also leave it. @@ -4981,16 +4992,13 @@ BaseCompiler::emitBrTable() if (deadCode_) return true; - // We'll need the joinreg later, so don't use it for rc. - // We assume joinRegI32 and joinRegI64 overlap. - if (type == ExprType::I32 || type == ExprType::I64) - needI32(joinRegI32); + // Don't use joinReg for rc + maybeReserveJoinRegI(type); // Table switch value always on top. RegI32 rc = popI32(); - if (type == ExprType::I32 || type == ExprType::I64) - freeI32(joinRegI32); + maybeUnreserveJoinRegI(type); AnyReg r; if (!IsVoid(type)) From c0165d049a4ef8fddd169464a85def94e45bbde2 Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Mon, 17 Oct 2016 12:32:15 +0200 Subject: [PATCH 17/80] Bug 1277011 - refactor ARM code. r=h4writer --HG-- extra : rebase_source : a85435bbe275e076b814de5fd72fa9e406aeaf84 --- js/src/jit/MacroAssembler.h | 8 +- js/src/jit/arm/CodeGenerator-arm.cpp | 137 +-------------------- js/src/jit/arm/MacroAssembler-arm.cpp | 166 ++++++++++++++++++++++++++ js/src/jit/arm/MacroAssembler-arm.h | 6 + 4 files changed, 180 insertions(+), 137 deletions(-) diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 13205ce0463f..31dcb1285e65 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -1350,12 +1350,12 @@ class MacroAssembler : public MacroAssemblerSpecific void wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, Operand dstAddr) DEFINED_ON(x86); // wasm specific methods, used in both the wasm baseline compiler and ion. - void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64); - void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared); + void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm); + void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm); void outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared); - void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64); - void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared); + void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm); + void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm); void outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared); void outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared); diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index e665402736dc..13b7a0fd0370 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -3198,59 +3198,7 @@ CodeGeneratorARM::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir) auto* ool = new(alloc()) OutOfLineWasmTruncateCheck(mir, input); addOutOfLineCode(ool, mir); - - // vcvt* converts NaN into 0, so check for NaNs here. - { - if (fromType == MIRType::Double) - masm.compareDouble(input, input); - else if (fromType == MIRType::Float32) - masm.compareFloat(input, input); - else - MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); - - masm.ma_b(ool->entry(), Assembler::VFP_Unordered); - } - - ScratchDoubleScope scratchScope(masm); - ScratchRegisterScope scratchReg(masm); - FloatRegister scratch = scratchScope.uintOverlay(); - - // ARM conversion instructions clamp the value to ensure it fits within the - // target's type bounds, so every time we see those, we need to check the - // input. - if (mir->isUnsigned()) { - if (fromType == MIRType::Double) - masm.ma_vcvt_F64_U32(input, scratch); - else if (fromType == MIRType::Float32) - masm.ma_vcvt_F32_U32(input, scratch); - else - MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); - - masm.ma_vxfer(scratch, output); - - // int32_t(UINT32_MAX) == -1. - masm.ma_cmp(output, Imm32(-1), scratchReg); - masm.as_cmp(output, Imm8(0), Assembler::NotEqual); - masm.ma_b(ool->entry(), Assembler::Equal); - - masm.bind(ool->rejoin()); - return; - } - - scratch = scratchScope.sintOverlay(); - - if (fromType == MIRType::Double) - masm.ma_vcvt_F64_I32(input, scratch); - else if (fromType == MIRType::Float32) - masm.ma_vcvt_F32_I32(input, scratch); - else - MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); - - masm.ma_vxfer(scratch, output); - masm.ma_cmp(output, Imm32(INT32_MAX), scratchReg); - masm.ma_cmp(output, Imm32(INT32_MIN), scratchReg, Assembler::NotEqual); - masm.ma_b(ool->entry(), Assembler::Equal); - + masm.wasmTruncateToInt32(input, output, fromType, mir->isUnsigned(), ool->entry()); masm.bind(ool->rejoin()); } @@ -3297,86 +3245,9 @@ CodeGeneratorARM::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir) void CodeGeneratorARM::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool) { - MIRType fromType = ool->fromType(); - FloatRegister input = ool->input(); - - ScratchDoubleScope scratchScope(masm); - FloatRegister scratch; - - // Eagerly take care of NaNs. - Label inputIsNaN; - if (fromType == MIRType::Double) - masm.branchDouble(Assembler::DoubleUnordered, input, input, &inputIsNaN); - else if (fromType == MIRType::Float32) - masm.branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN); - else - MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck"); - - // Handle special values. - Label fail; - - // By default test for the following inputs and bail: - // signed: ] -Inf, INTXX_MIN - 1.0 ] and [ INTXX_MAX + 1.0 : +Inf [ - // unsigned: ] -Inf, -1.0 ] and [ UINTXX_MAX + 1.0 : +Inf [ - // Note: we cannot always represent those exact values. As a result - // this changes the actual comparison a bit. - double minValue, maxValue; - Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual; - Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual; - if (ool->toType() == MIRType::Int64) { - if (ool->isUnsigned()) { - minValue = -1; - maxValue = double(UINT64_MAX) + 1.0; - } else { - // In the float32/double range there exists no value between - // INT64_MIN and INT64_MIN - 1.0. Making INT64_MIN the lower-bound. - minValue = double(INT64_MIN); - minCond = Assembler::DoubleLessThan; - maxValue = double(INT64_MAX) + 1.0; - } - } else { - if (ool->isUnsigned()) { - minValue = -1; - maxValue = double(UINT32_MAX) + 1.0; - } else { - if (fromType == MIRType::Float32) { - // In the float32 range there exists no value between - // INT32_MIN and INT32_MIN - 1.0. Making INT32_MIN the lower-bound. - minValue = double(INT32_MIN); - minCond = Assembler::DoubleLessThan; - } else { - minValue = double(INT32_MIN) - 1.0; - } - maxValue = double(INT32_MAX) + 1.0; - } - } - - if (fromType == MIRType::Double) { - scratch = scratchScope.doubleOverlay(); - masm.loadConstantDouble(minValue, scratch); - masm.branchDouble(minCond, input, scratch, &fail); - - masm.loadConstantDouble(maxValue, scratch); - masm.branchDouble(maxCond, input, scratch, &fail); - } else { - MOZ_ASSERT(fromType == MIRType::Float32); - scratch = scratchScope.singleOverlay(); - masm.loadConstantFloat32(float(minValue), scratch); - masm.branchFloat(minCond, input, scratch, &fail); - - masm.loadConstantFloat32(float(maxValue), scratch); - masm.branchFloat(maxCond, input, scratch, &fail); - } - - // We had an actual correct value, get back to where we were. - masm.ma_b(ool->rejoin()); - - // Handle errors. - masm.bind(&fail); - masm.jump(trap(ool, wasm::Trap::IntegerOverflow)); - - masm.bind(&inputIsNaN); - masm.jump(trap(ool, wasm::Trap::InvalidConversionToInteger)); + masm.outOfLineWasmTruncateToIntCheck(ool->input(), ool->fromType(), ool->toType(), + ool->isUnsigned(), ool->rejoin(), + ool->trapOffset()); } void diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 61373a5df3c7..17134772c6ee 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -5341,8 +5341,174 @@ template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, const BaseIndex& dest, MIRType slotType); +void +MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) +{ + wasmTruncateToInt32(input, output, MIRType::Double, /* isUnsigned= */ true, oolEntry); +} + +void +MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) +{ + wasmTruncateToInt32(input, output, MIRType::Double, /* isUnsigned= */ false, oolEntry); +} + +void +MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) +{ + wasmTruncateToInt32(input, output, MIRType::Float32, /* isUnsigned= */ true, oolEntry); +} + +void +MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) +{ + wasmTruncateToInt32(input, output, MIRType::Float32, /* isUnsigned= */ false, oolEntry); +} + //}}} check_macroassembler_style +void +MacroAssemblerARM::wasmTruncateToInt32(FloatRegister input, Register output, MIRType fromType, + bool isUnsigned, Label* oolEntry) +{ + // vcvt* converts NaN into 0, so check for NaNs here. + { + if (fromType == MIRType::Double) + asMasm().compareDouble(input, input); + else if (fromType == MIRType::Float32) + asMasm().compareFloat(input, input); + else + MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); + + ma_b(oolEntry, Assembler::VFP_Unordered); + } + + ScratchDoubleScope scratchScope(asMasm()); + ScratchRegisterScope scratchReg(asMasm()); + FloatRegister scratch = scratchScope.uintOverlay(); + + // ARM conversion instructions clamp the value to ensure it fits within the + // target's type bounds, so every time we see those, we need to check the + // input. + if (isUnsigned) { + if (fromType == MIRType::Double) + ma_vcvt_F64_U32(input, scratch); + else if (fromType == MIRType::Float32) + ma_vcvt_F32_U32(input, scratch); + else + MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); + + ma_vxfer(scratch, output); + + // int32_t(UINT32_MAX) == -1. + ma_cmp(output, Imm32(-1), scratchReg); + as_cmp(output, Imm8(0), Assembler::NotEqual); + ma_b(oolEntry, Assembler::Equal); + + return; + } + + scratch = scratchScope.sintOverlay(); + + if (fromType == MIRType::Double) + ma_vcvt_F64_I32(input, scratch); + else if (fromType == MIRType::Float32) + ma_vcvt_F32_I32(input, scratch); + else + MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); + + ma_vxfer(scratch, output); + ma_cmp(output, Imm32(INT32_MAX), scratchReg); + ma_cmp(output, Imm32(INT32_MIN), scratchReg, Assembler::NotEqual); + ma_b(oolEntry, Assembler::Equal); +} + +void +MacroAssemblerARM::outOfLineWasmTruncateToIntCheck(FloatRegister input, MIRType fromType, + MIRType toType, bool isUnsigned, Label* rejoin, + wasm::TrapOffset trapOffset) +{ + ScratchDoubleScope scratchScope(asMasm()); + FloatRegister scratch; + + // Eagerly take care of NaNs. + Label inputIsNaN; + if (fromType == MIRType::Double) + asMasm().branchDouble(Assembler::DoubleUnordered, input, input, &inputIsNaN); + else if (fromType == MIRType::Float32) + asMasm().branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN); + else + MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck"); + + // Handle special values. + Label fail; + + // By default test for the following inputs and bail: + // signed: ] -Inf, INTXX_MIN - 1.0 ] and [ INTXX_MAX + 1.0 : +Inf [ + // unsigned: ] -Inf, -1.0 ] and [ UINTXX_MAX + 1.0 : +Inf [ + // Note: we cannot always represent those exact values. As a result + // this changes the actual comparison a bit. + double minValue, maxValue; + Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual; + Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual; + if (toType == MIRType::Int64) { + if (isUnsigned) { + minValue = -1; + maxValue = double(UINT64_MAX) + 1.0; + } else { + // In the float32/double range there exists no value between + // INT64_MIN and INT64_MIN - 1.0. Making INT64_MIN the lower-bound. + minValue = double(INT64_MIN); + minCond = Assembler::DoubleLessThan; + maxValue = double(INT64_MAX) + 1.0; + } + } else { + if (isUnsigned) { + minValue = -1; + maxValue = double(UINT32_MAX) + 1.0; + } else { + if (fromType == MIRType::Float32) { + // In the float32 range there exists no value between + // INT32_MIN and INT32_MIN - 1.0. Making INT32_MIN the lower-bound. + minValue = double(INT32_MIN); + minCond = Assembler::DoubleLessThan; + } else { + minValue = double(INT32_MIN) - 1.0; + } + maxValue = double(INT32_MAX) + 1.0; + } + } + + if (fromType == MIRType::Double) { + scratch = scratchScope.doubleOverlay(); + asMasm().loadConstantDouble(minValue, scratch); + asMasm().branchDouble(minCond, input, scratch, &fail); + + asMasm().loadConstantDouble(maxValue, scratch); + asMasm().branchDouble(maxCond, input, scratch, &fail); + } else { + MOZ_ASSERT(fromType == MIRType::Float32); + scratch = scratchScope.singleOverlay(); + asMasm().loadConstantFloat32(float(minValue), scratch); + asMasm().branchFloat(minCond, input, scratch, &fail); + + asMasm().loadConstantFloat32(float(maxValue), scratch); + asMasm().branchFloat(maxCond, input, scratch, &fail); + } + + // We had an actual correct value, get back to where we were. + ma_b(rejoin); + + // Handle errors. + bind(&fail); + asMasm().jump(wasm::TrapDesc(trapOffset, wasm::Trap::IntegerOverflow, + asMasm().framePushed())); + + bind(&inputIsNaN); + asMasm().jump(wasm::TrapDesc(trapOffset, wasm::Trap::InvalidConversionToInteger, + asMasm().framePushed())); +} + void MacroAssemblerARM::emitUnalignedLoad(bool isSigned, unsigned byteSize, Register ptr, Register tmp, Register dest, unsigned offset) diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 032229a4595d..63af3b4f03b8 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -100,6 +100,12 @@ class MacroAssemblerARM : public Assembler void convertInt32ToFloat32(Register src, FloatRegister dest); void convertInt32ToFloat32(const Address& src, FloatRegister dest); + void wasmTruncateToInt32(FloatRegister input, Register output, MIRType fromType, + bool isUnsigned, Label* oolEntry); + void outOfLineWasmTruncateToIntCheck(FloatRegister input, MIRType fromType, + MIRType toType, bool isUnsigned, Label* rejoin, + wasm::TrapOffset trapOffs); + // Somewhat direct wrappers for the low-level assembler funcitons // bitops. Attempt to encode a virtual alu instruction using two real // instructions. From b6838e39470d72c82b301ff1beeee262c7532f81 Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Tue, 11 Oct 2016 12:18:03 +0200 Subject: [PATCH 18/80] Bug 1309180: Add interactive disasm calls to ARM simulator. r=bbouvier --HG-- extra : amend_source : 765d7d3bcae7357bfc0425935ea9a9df6b657c61 --- js/src/jit/arm/Simulator-arm.cpp | 32 ++++++++++++++++++++++++++++++++ js/src/jit/arm/Simulator-arm.h | 23 +++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/js/src/jit/arm/Simulator-arm.cpp b/js/src/jit/arm/Simulator-arm.cpp index 43a2de559791..b652f00e5ca8 100644 --- a/js/src/jit/arm/Simulator-arm.cpp +++ b/js/src/jit/arm/Simulator-arm.cpp @@ -421,6 +421,38 @@ Simulator::Destroy(Simulator* sim) js_delete(sim); } +void +Simulator::disassemble(SimInstruction* instr, size_t n) +{ + disasm::NameConverter converter; + disasm::Disassembler dasm(converter); + disasm::EmbeddedVector buffer; + while (n-- > 0) { + dasm.InstructionDecode(buffer, + reinterpret_cast(instr)); + printf(" 0x%08x %s\n", uint32_t(instr), buffer.start()); + instr = reinterpret_cast(reinterpret_cast(instr) + 4); + } +} + +void +Simulator::disasm(SimInstruction* instr) +{ + disassemble(instr, 1); +} + +void +Simulator::disasm(SimInstruction* instr, size_t n) +{ + disassemble(instr, n); +} + +void +Simulator::disasm(SimInstruction* instr, size_t m, size_t n) +{ + disassemble(reinterpret_cast(reinterpret_cast(instr) - m * 4), n); +} + // The ArmDebugger class is used by the simulator while debugging simulated ARM // code. class ArmDebugger { diff --git a/js/src/jit/arm/Simulator-arm.h b/js/src/jit/arm/Simulator-arm.h index 7889a68e07c8..ce7e4cdeece4 100644 --- a/js/src/jit/arm/Simulator-arm.h +++ b/js/src/jit/arm/Simulator-arm.h @@ -116,6 +116,29 @@ class Simulator return Simulator::Current()->stackLimit(); } + // Disassemble some instructions starting at instr and print them + // on stdout. Useful for working within GDB after a MOZ_CRASH(), + // among other things. + // + // Typical use within a crashed instruction decoding method is simply: + // + // call Simulator::disassemble(instr, 1) + // + // or use one of the (much) more convenient inline methods below. + static void disassemble(SimInstruction* instr, size_t n); + + // Disassemble one instruction. + // "call disasm(instr)" + void disasm(SimInstruction* instr); + + // Disassemble n instructions starting at instr. + // "call disasm(instr, 3)" + void disasm(SimInstruction* instr, size_t n); + + // Skip backwards m instructions before starting, then disassemble n instructions. + // "call disasm(instr, 3, 7)" + void disasm(SimInstruction* instr, size_t m, size_t n); + uintptr_t* addressOfStackLimit(); // Accessors for register state. Reading the pc value adheres to the ARM From cfbe3fe8c3e1f5abfcf1931d3d59b385fe84b4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Mon, 19 Sep 2016 09:54:23 -0700 Subject: [PATCH 19/80] Bug 866602 - Handle four-character language codes in CanonicalizeLanguageTag. r=Waldo --HG-- extra : rebase_source : 8cc60711bde98d08e13c7ce962301dd4859edd77 --- js/src/builtin/Intl.js | 21 +++++++++++-------- .../tests/Intl/four-letter-language-codes.js | 20 ++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 js/src/tests/Intl/four-letter-language-codes.js diff --git a/js/src/builtin/Intl.js b/js/src/builtin/Intl.js index c7fac52dd8ea..936c413d5f30 100644 --- a/js/src/builtin/Intl.js +++ b/js/src/builtin/Intl.js @@ -394,15 +394,18 @@ function CanonicalizeLanguageTag(locale) { if (subtag.length === 1 && (i > 0 || subtag === "x")) break; - if (subtag.length === 4) { - // 4-character subtags are script codes; their first character - // needs to be capitalized. "hans" -> "Hans" - subtag = callFunction(std_String_toUpperCase, subtag[0]) + - callFunction(String_substring, subtag, 1); - } else if (i !== 0 && subtag.length === 2) { - // 2-character subtags that are not in initial position are region - // codes; they need to be upper case. "bu" -> "BU" - subtag = callFunction(std_String_toUpperCase, subtag); + if (i !== 0) { + if (subtag.length === 4) { + // 4-character subtags that are not in initial position are + // script codes; their first character needs to be capitalized. + // "hans" -> "Hans" + subtag = callFunction(std_String_toUpperCase, subtag[0]) + + callFunction(String_substring, subtag, 1); + } else if (subtag.length === 2) { + // 2-character subtags that are not in initial position are + // region codes; they need to be upper case. "bu" -> "BU" + subtag = callFunction(std_String_toUpperCase, subtag); + } } if (callFunction(std_Object_hasOwnProperty, langSubtagMappings, subtag)) { // Replace deprecated subtags with their preferred values. diff --git a/js/src/tests/Intl/four-letter-language-codes.js b/js/src/tests/Intl/four-letter-language-codes.js new file mode 100644 index 000000000000..b92b4c3e100e --- /dev/null +++ b/js/src/tests/Intl/four-letter-language-codes.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// So many non-existent four letter language codes to pick from. +const languageTags = { + "Flob": "flob", + "ZORK": "zork", + "Blah-latn": "blah-Latn", + "QuuX-latn-us": "quux-Latn-US", + "SPAM-gb-x-Sausages-BACON-eggs": "spam-GB-x-sausages-bacon-eggs", +}; + +for (let [tag, canonical] of Object.entries(languageTags)) { + assertEq(Intl.getCanonicalLocales(tag)[0], canonical); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); From ddfa975699b4ac0319162e054a569ec4c5769da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Fri, 14 Oct 2016 09:03:46 -0700 Subject: [PATCH 20/80] Bug 1122396 - Validate newly created instances in typed array builtins. r=evilpie --HG-- extra : rebase_source : efcbff4b5e1b6f9e142bf2bc2cfa09d7afe12771 --- js/src/builtin/TypedArray.js | 353 ++++++++++-------- js/src/js.msg | 2 + .../ecma_6/TypedArray/from_constructor.js | 39 +- js/src/tests/ecma_6/TypedArray/from_errors.js | 41 +- .../tests/ecma_6/TypedArray/from_mapping.js | 6 +- js/src/tests/ecma_6/TypedArray/from_proxy.js | 58 --- js/src/tests/ecma_6/TypedArray/from_string.js | 17 +- js/src/tests/ecma_6/TypedArray/of.js | 13 +- js/src/tests/ecma_7/TypedArray/browser.js | 0 .../ecma_7/TypedArray/filter-validation.js | 185 +++++++++ .../TypedArray/from-iterable-validation.js | 140 +++++++ .../from-non-iterable-validation.js | 140 +++++++ .../tests/ecma_7/TypedArray/map-validation.js | 149 ++++++++ .../tests/ecma_7/TypedArray/of-validation.js | 140 +++++++ js/src/tests/ecma_7/TypedArray/shell.js | 113 ++++++ .../ecma_7/TypedArray/slice-validation.js | 187 ++++++++++ .../ecma_7/TypedArray/subarray-validation.js | 110 ++++++ js/src/vm/SelfHosting.cpp | 22 +- 18 files changed, 1425 insertions(+), 290 deletions(-) delete mode 100644 js/src/tests/ecma_6/TypedArray/from_proxy.js create mode 100644 js/src/tests/ecma_7/TypedArray/browser.js create mode 100644 js/src/tests/ecma_7/TypedArray/filter-validation.js create mode 100644 js/src/tests/ecma_7/TypedArray/from-iterable-validation.js create mode 100644 js/src/tests/ecma_7/TypedArray/from-non-iterable-validation.js create mode 100644 js/src/tests/ecma_7/TypedArray/map-validation.js create mode 100644 js/src/tests/ecma_7/TypedArray/of-validation.js create mode 100644 js/src/tests/ecma_7/TypedArray/shell.js create mode 100644 js/src/tests/ecma_7/TypedArray/slice-validation.js create mode 100644 js/src/tests/ecma_7/TypedArray/subarray-validation.js diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index fdc54cade28b..9293cb33b23a 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -61,6 +61,99 @@ function IsTypedArrayEnsuringArrayBuffer(arg) { return false; } +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.3.5.1 Runtime Semantics: ValidateTypedArray ( O ) +function ValidateTypedArray(obj, error) { + if (IsObject(obj)) { + /* Steps 3-5 (non-wrapped typed arrays). */ + if (IsTypedArray(obj)) { + // GetAttachedArrayBuffer throws for detached array buffers. + GetAttachedArrayBuffer(obj); + return true; + } + + /* Steps 3-5 (wrapped typed arrays). */ + if (IsPossiblyWrappedTypedArray(obj)) { + if (PossiblyWrappedTypedArrayHasDetachedBuffer(obj)) + ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + } + + /* Steps 1-2. */ + ThrowTypeError(error); +} + +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.4.6 TypedArrayCreate ( constructor, argumentList ) +function TypedArrayCreateWithLength(constructor, length) { + // Step 1. + var newTypedArray = new constructor(length); + + // Step 2. + var isTypedArray = ValidateTypedArray(newTypedArray, JSMSG_NON_TYPED_ARRAY_RETURNED); + + // Step 3. + var len; + if (isTypedArray) { + len = TypedArrayLength(newTypedArray); + } else { + len = callFunction(CallTypedArrayMethodIfWrapped, newTypedArray, newTypedArray, + "TypedArrayLength"); + } + + if (len < length) + ThrowTypeError(JSMSG_SHORT_TYPED_ARRAY_RETURNED, length, len); + + // Step 4. + return newTypedArray; +} + +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.4.6 TypedArrayCreate ( constructor, argumentList ) +function TypedArrayCreateWithBuffer(constructor, buffer, byteOffset, length) { + // Step 1. + var newTypedArray = new constructor(buffer, byteOffset, length); + + // Step 2. + ValidateTypedArray(newTypedArray, JSMSG_NON_TYPED_ARRAY_RETURNED); + + // Step 3 (not applicable). + + // Step 4. + return newTypedArray; +} + +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList ) +function TypedArraySpeciesCreateWithLength(exemplar, length) { + // Step 1 (omitted). + + // Step 2. + var defaultConstructor = _ConstructorForTypedArray(exemplar); + + // Step 3. + var C = SpeciesConstructor(exemplar, defaultConstructor); + + // Step 4. + return TypedArrayCreateWithLength(C, length); +} + +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList ) +function TypedArraySpeciesCreateWithBuffer(exemplar, buffer, byteOffset, length) { + // Step 1 (omitted). + + // Step 2. + var defaultConstructor = _ConstructorForTypedArray(exemplar); + + // Step 3. + var C = SpeciesConstructor(exemplar, defaultConstructor); + + // Step 4. + return TypedArrayCreateWithBuffer(C, buffer, byteOffset, length); +} + // ES6 draft 20150304 %TypedArray%.prototype.copyWithin function TypedArrayCopyWithin(target, start, end = undefined) { // This function is not generic. @@ -244,12 +337,13 @@ function TypedArrayFill(value, start = 0, end = undefined) { return O; } -// ES6 draft 32 (2015-02-02) 22.2.3.9 %TypedArray%.prototype.filter(callbackfn[, thisArg]) +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// %TypedArray%.prototype.filter ( callbackfn [ , thisArg ] ) function TypedArrayFilter(callbackfn/*, thisArg*/) { // Step 1. var O = this; - // Steps 2-3. + // Step 2. // This function is not generic. // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); @@ -257,57 +351,53 @@ function TypedArrayFilter(callbackfn/*, thisArg*/) { // If we got here, `this` is either a typed array or a cross-compartment // wrapper for one. - // Step 4. + // Step 3. var len; if (isTypedArray) len = TypedArrayLength(O); else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - // Step 5. + // Step 4. if (arguments.length === 0) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.filter"); if (!IsCallable(callbackfn)) ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); - // Step 6. + // Step 5. var T = arguments.length > 1 ? arguments[1] : void 0; - // Step 7. - var defaultConstructor = _ConstructorForTypedArray(O); - - // Steps 8-9. - var C = SpeciesConstructor(O, defaultConstructor); - - // Step 10. + // Step 6. var kept = new List(); - // Step 12. + // Step 8. var captured = 0; - // Steps 11, 13 and 13.g. + // Steps 7 and 9.e. for (var k = 0; k < len; k++) { - // Steps 13.b-c. + // Steps 9.a-b. var kValue = O[k]; - // Steps 13.d-e. + + // Step 9.c. var selected = ToBoolean(callContentFunction(callbackfn, T, kValue, k, O)); - // Step 13.f. + + // Step 9.d. if (selected) { - // Steps 13.f.i-ii. + // Steps 9.d.i-ii. kept[captured++] = kValue; } } - // Steps 14-15. - var A = new C(captured); + // Step 10. + var A = TypedArraySpeciesCreateWithLength(O, captured); - // Steps 16 and 17.c. + // Steps 11 and 12.b. for (var n = 0; n < captured; n++) { - // Steps 17.a-b. + // Step 12.a. A[n] = kept[n]; } - // Step 18. + // Step 13. return A; } @@ -585,12 +675,13 @@ function TypedArrayLastIndexOf(searchElement, fromIndex = undefined) { return -1; } -// ES6 draft rev32 (2015-02-02) 22.2.3.18 %TypedArray%.prototype.map(callbackfn [, thisArg]). +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.3.19 %TypedArray%.prototype.map ( callbackfn [ , thisArg ] ) function TypedArrayMap(callbackfn/*, thisArg*/) { // Step 1. var O = this; - // Steps 2-3. + // Step 2. // This function is not generic. // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); @@ -598,40 +689,35 @@ function TypedArrayMap(callbackfn/*, thisArg*/) { // If we got here, `this` is either a typed array or a cross-compartment // wrapper for one. - // Step 4. + // Step 3. var len; if (isTypedArray) len = TypedArrayLength(O); else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - // Step 5. + // Step 4. if (arguments.length === 0) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, '%TypedArray%.prototype.map'); if (!IsCallable(callbackfn)) ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); - // Step 6. + // Step 5. var T = arguments.length > 1 ? arguments[1] : void 0; - // Step 7. - var defaultConstructor = _ConstructorForTypedArray(O); + // Step 6. + var A = TypedArraySpeciesCreateWithLength(O, len); - // Steps 8-9. - var C = SpeciesConstructor(O, defaultConstructor); - - // Steps 10-11. - var A = new C(len); - - // Steps 12, 13.a (implicit) and 13.h. + // Steps 7, 8.a (implicit) and 8.e. for (var k = 0; k < len; k++) { - // Steps 13.d-e. + // Steps 8.b-c. var mappedValue = callContentFunction(callbackfn, T, O[k], k, O); - // Steps 13.f-g. + + // Steps 8.d. A[k] = mappedValue; } - // Step 14. + // Step 9. return A; } @@ -886,64 +972,56 @@ function TypedArraySet(overloaded, offset = 0) { return SetFromNonTypedArray(target, overloaded, targetOffset, targetLength, targetBuffer); } -// ES6 draft rev32 (2015-02-02) 22.2.3.23 %TypedArray%.prototype.slice(start, end). +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.3.24 %TypedArray%.prototype.slice ( start, end ) function TypedArraySlice(start, end) { - // Step 1. var O = this; - // Step 2-3. + // Step 2. if (!IsObject(O) || !IsTypedArray(O)) { return callFunction(CallTypedArrayMethodIfWrapped, O, start, end, "TypedArraySlice"); } GetAttachedArrayBuffer(O); - // Step 4. + // Step 3. var len = TypedArrayLength(O); - // Steps 5-6. + // Step 4. var relativeStart = ToInteger(start); - // Step 7. + // Step 5. var k = relativeStart < 0 ? std_Math_max(len + relativeStart, 0) : std_Math_min(relativeStart, len); - // Steps 8-9. + // Step 6. var relativeEnd = end === undefined ? len : ToInteger(end); - // Step 10. + // Step 7. var final = relativeEnd < 0 ? std_Math_max(len + relativeEnd, 0) : std_Math_min(relativeEnd, len); - // Step 11. + // Step 8. var count = std_Math_max(final - k, 0); - // Step 12. - var defaultConstructor = _ConstructorForTypedArray(O); + // Step 9. + var A = TypedArraySpeciesCreateWithLength(O, count); - // Steps 13-14. - var C = SpeciesConstructor(O, defaultConstructor); - - // Steps 15-16. - var A = new C(count); - - // Step 17. + // Step 14.a. var n = 0; - // Step 18. + // Step 14.b. while (k < final) { - // Steps 18.a-e. - A[n] = O[k]; - // Step 18f. - k++; - // Step 18g. - n++; + // Steps 14.b.i-v. + A[n++] = O[k++]; } - // Step 19. + // FIXME: Implement step 15 (bug 1140152). + + // Step 16. return A; } @@ -1167,7 +1245,8 @@ function TypedArrayToLocaleString(locales = undefined, options = undefined) { return R; } -// ES6 draft 20150304 %TypedArray%.prototype.subarray +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.3.27 %TypedArray%.prototype.subarray( begin, end ) function TypedArraySubarray(begin, end) { // Step 1. var obj = this; @@ -1183,34 +1262,30 @@ function TypedArraySubarray(begin, end) { var buffer = TypedArrayBuffer(obj); var srcLength = TypedArrayLength(obj); - // Steps 7-9. + // Steps 7-8. var relativeBegin = ToInteger(begin); var beginIndex = relativeBegin < 0 ? std_Math_max(srcLength + relativeBegin, 0) : std_Math_min(relativeBegin, srcLength); - // Steps 10-12. + // Steps 9-10. var relativeEnd = end === undefined ? srcLength : ToInteger(end); var endIndex = relativeEnd < 0 ? std_Math_max(srcLength + relativeEnd, 0) : std_Math_min(relativeEnd, srcLength); - // Step 13. + // Step 11. var newLength = std_Math_max(endIndex - beginIndex, 0); - // Steps 14-15, altered to use a shift instead of a size for performance. + // Steps 12-13, altered to use a shift instead of a size for performance. var elementShift = TypedArrayElementShift(obj); - // Step 16. + // Step 14. var srcByteOffset = TypedArrayByteOffset(obj); - // Step 17. + // Step 15. var beginByteOffset = srcByteOffset + (beginIndex << elementShift); - // Steps 18-20. - var defaultConstructor = _ConstructorForTypedArray(obj); - var constructor = SpeciesConstructor(obj, defaultConstructor); - - // Steps 21-22. - return new constructor(buffer, beginByteOffset, newLength); + // Steps 16-17. + return TypedArraySpeciesCreateWithBuffer(obj, buffer, beginByteOffset, newLength); } // ES6 draft rev30 (2014/12/24) 22.2.3.30 %TypedArray%.prototype.values() @@ -1278,135 +1353,118 @@ function TypedArrayIncludes(searchElement, fromIndex = 0) { return false; } -// ES6 draft rev30 (2014/12/24) 22.2.2.1 %TypedArray%.from(source[, mapfn[, thisArg]]). +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.2.1 %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] ) function TypedArrayStaticFrom(source, mapfn = undefined, thisArg = undefined) { // Step 1. var C = this; // Step 2. if (!IsConstructor(C)) - ThrowTypeError(JSMSG_NOT_CONSTRUCTOR, DecompileArg(1, C)); + ThrowTypeError(JSMSG_NOT_CONSTRUCTOR, typeof C); // Step 3. - var f = mapfn; + var mapping; + if (mapfn !== undefined) { + // Step 3.a. + if (!IsCallable(mapfn)) + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn)); - // Step 4. - if (f !== undefined && !IsCallable(f)) - ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, f)); - - // Steps 5-6. - return TypedArrayFrom(C, undefined, source, f, thisArg); -} - -// ES6 draft rev30 (2014/12/24) 22.2.2.1.1 TypedArrayFrom(). -function TypedArrayFrom(constructor, target, items, mapfn, thisArg) { - // Step 1. - var C = constructor; - - // Step 2. - assert(C === undefined || target === undefined, - "Neither of 'constructor' and 'target' is undefined"); - - // Step 3. - assert(IsConstructor(C) || C === undefined, - "'constructor' is neither an constructor nor undefined"); - - // Step 4. - assert(target === undefined || IsTypedArray(target), - "'target' is neither a typed array nor undefined"); + // Step 3.b. + mapping = true; + } else { + // Step 4. + mapping = false; + } // Step 5. - assert(IsCallable(mapfn) || mapfn === undefined, - "'target' is neither a function nor undefined"); - - // Steps 6-7. - var mapping = mapfn !== undefined; var T = thisArg; - // Steps 8-9. - var usingIterator = GetMethod(items, std_iterator); + // Step 6. + var usingIterator = GetMethod(source, std_iterator); - // Step 10. + // Step 7. if (usingIterator !== undefined) { - // Steps 10.a-b. - var iterator = GetIterator(items, usingIterator); + // Step 7.a. + // Inlined: 22.2.2.1.1 Runtime Semantics: IterableToList( items, method ). - // Step 10.c. + // 22.2.2.1.1 IterableToList, step 1. + var iterator = GetIterator(source, usingIterator); + + // 22.2.2.1.1 IterableToList, step 2. var values = new List(); - // Steps 10.d-e. + // 22.2.2.1.1 IterableToList, steps 3-4. var i = 0; while (true) { - // Steps 10.e.i-ii. + // 22.2.2.1.1 IterableToList, step 4.a. var next = callContentFunction(iterator.next, iterator); if (!IsObject(next)) ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE); - // Steps 10.e.iii-vi. + // 22.2.2.1.1 IterableToList, step 4.b. if (next.done) break; values[i++] = next.value; } - // Step 10.f. + // Step 7.b. var len = i; - // Steps 10.g-h. - // There is no need to implement the 22.2.2.1.2 - TypedArrayAllocOrInit() method, - // since `%TypedArray%(object)` currently doesn't call this self-hosted TypedArrayFrom(). - var targetObj = new C(len); + // Step 7.c. + var targetObj = TypedArrayCreateWithLength(C, len); - // Steps 10.i-j. + // Steps 7.d-e. for (var k = 0; k < len; k++) { - // Steps 10.j.i-ii. + // Step 7.e.ii. var kValue = values[k]; - // Steps 10.j.iii-iv. + // Steps 7.e.iii-iv. var mappedValue = mapping ? callContentFunction(mapfn, T, kValue, k) : kValue; - // Steps 10.j.v-vi. + // Step 7.e.v. targetObj[k] = mappedValue; } - // Step 10.k. - // asserting that `values` is empty here would require removing them one by one from + // Step 7.f. + // Asserting that `values` is empty here would require removing them one by one from // the list's start in the loop above. That would introduce unacceptable overhead. // Additionally, the loop's logic is simple enough not to require the assert. - // Step 10.l. + // Step 7.g. return targetObj; } - // Step 11 is an assertion: items is not an Iterator. Testing this is + // Step 8 is an assertion: items is not an Iterator. Testing this is // literally the very last thing we did, so we don't assert here. - // Steps 12-13. - var arrayLike = ToObject(items); + // Step 9. + var arrayLike = ToObject(source); - // Steps 14-16. + // Step 10. var len = ToLength(arrayLike.length); - // Steps 17-18. - // See comment for steps 10.g-h. - var targetObj = new C(len); + // Step 11. + var targetObj = TypedArrayCreateWithLength(C, len); - // Steps 19-20. + // Steps 12-13. for (var k = 0; k < len; k++) { - // Steps 20.a-c. + // Steps 13.a-b. var kValue = arrayLike[k]; - // Steps 20.d-e. + // Steps 13.c-d. var mappedValue = mapping ? callContentFunction(mapfn, T, kValue, k) : kValue; - // Steps 20.f-g. + // Step 13.e. targetObj[k] = mappedValue; } - // Step 21. + // Step 14. return targetObj; } -// ES6 draft rev30 (2014/12/24) 22.2.2.2 %TypedArray%.of(...items). +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 22.2.2.2 %TypedArray%.of ( ...items ) function TypedArrayStaticOf(/*...items*/) { // Step 1. var len = arguments.length; @@ -1417,11 +1475,12 @@ function TypedArrayStaticOf(/*...items*/) { // Step 3. var C = this; - // Steps 4-5. + // Step 4. if (!IsConstructor(C)) ThrowTypeError(JSMSG_NOT_CONSTRUCTOR, typeof C); - var newObj = new C(len); + // Step 5. + var newObj = TypedArrayCreateWithLength(C, len); // Steps 6-7. for (var k = 0; k < len; k++) diff --git a/js/src/js.msg b/js/src/js.msg index 57eba82f6524..829c5912e130 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -516,6 +516,8 @@ MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED, 0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer") MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS, 0, JSEXN_RANGEERR, "attempting to construct out-of-bounds TypedArray on ArrayBuffer") MSG_DEF(JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT, 1, JSEXN_TYPEERR, "cannot directly {0} builtin %TypedArray%") +MSG_DEF(JSMSG_NON_TYPED_ARRAY_RETURNED, 0, JSEXN_TYPEERR, "constructor didn't return TypedArray object") +MSG_DEF(JSMSG_SHORT_TYPED_ARRAY_RETURNED, 2, JSEXN_TYPEERR, "expected TypedArray of at least length {0}, but constructor returned TypedArray of length {1}") // Shared array buffer MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH, 0, JSEXN_RANGEERR, "length argument out of range") diff --git a/js/src/tests/ecma_6/TypedArray/from_constructor.js b/js/src/tests/ecma_6/TypedArray/from_constructor.js index ca4ee29b94c0..0b0773b85c60 100644 --- a/js/src/tests/ecma_6/TypedArray/from_constructor.js +++ b/js/src/tests/ecma_6/TypedArray/from_constructor.js @@ -1,41 +1,9 @@ for (var constructor of anyTypedArrayConstructors) { - // %TypedArray%.from can be applied to any constructor. - // For example, the Date builtin constructor. - // Unlike Array.from, %TypedArray%.from doesn't set the 'length' property at the end. - var d = constructor.from.call(Date, ["A", "B"]); - assertEq(d instanceof constructor, false); - assertEq(Object.prototype.toString.call(d), "[object Date]"); - assertEq(Object.getPrototypeOf(d), Date.prototype); - assertEq(d.length, undefined); - assertEq(d[0], "A"); - assertEq(d[1], "B"); - - // Or RegExp. - var obj = constructor.from.call(RegExp, [1]); - assertEq(obj instanceof constructor, false); - assertEq(Object.getPrototypeOf(obj), RegExp.prototype); - assertEq(Object.getOwnPropertyNames(obj).join(","), - "0,lastIndex"); - assertEq(obj.length, undefined); - - // Or any JS function. - function C(arg) { - this.args = arguments; - } - var c = constructor.from.call(C, {length: 1, 0: "zero"}); - assertEq(c instanceof C, true); - assertEq(c.args.length, 1); - assertEq(c.args[0], 1); - assertEq(c.length, undefined); - assertEq(c[0], "zero"); - // Note %TypedArray%.from(iterable) calls 'this' with an argument whose value is // `[...iterable].length`, while Array.from(iterable) doesn't pass any argument. - assertEq(constructor.from.call(Object, []) instanceof Number, true); - assertDeepEq(constructor.from.call(Object, []), new Number(0)); - assertEq(constructor.from.call(Number,[1, , "a"]) + 1, 4); constructor.from.call(function(len){ assertEq(len, 3); + return new constructor(len); }, Array(3)); // If the 'this' value passed to %TypedArray.from is not a constructor, @@ -53,13 +21,14 @@ for (var constructor of anyTypedArrayConstructors) { // %TypedArray%.from does not get confused if global constructors for typed arrays // are replaced with another constructor. - function NotArray() { + function NotArray(...rest) { + return new constructor(...rest); } var RealArray = constructor; NotArray.from = constructor.from; this[constructor.name] = NotArray; assertEq(RealArray.from([1]) instanceof RealArray, true); - assertEq(NotArray.from([1]) instanceof NotArray, true); + assertEq(NotArray.from([1]) instanceof RealArray, true); this[constructor.name] = RealArray; } diff --git a/js/src/tests/ecma_6/TypedArray/from_errors.js b/js/src/tests/ecma_6/TypedArray/from_errors.js index d929985ee983..281b1b512922 100644 --- a/js/src/tests/ecma_6/TypedArray/from_errors.js +++ b/js/src/tests/ecma_6/TypedArray/from_errors.js @@ -4,41 +4,18 @@ for (var constructor of anyTypedArrayConstructors) { assertThrowsInstanceOf(() => constructor.from(undefined), TypeError); assertThrowsInstanceOf(() => constructor.from(null), TypeError); - // %TypedArray%.from throws if an element can't be defined on the new object. - function ObjectWithReadOnlyElement() { - Object.defineProperty(this, "0", {value: null}); - this.length = 0; - } - ObjectWithReadOnlyElement.from = constructor.from; - assertDeepEq(ObjectWithReadOnlyElement.from([]), new ObjectWithReadOnlyElement); - assertThrowsInstanceOf(() => ObjectWithReadOnlyElement.from([1]), TypeError); - - // The same, but via preventExtensions. - function InextensibleObject() { - Object.preventExtensions(this); - } - InextensibleObject.from = constructor.from; - assertThrowsInstanceOf(() => InextensibleObject.from([1]), TypeError); - - // The same, but via a readonly property on its __proto__. - function ObjectWithReadOnlyElementOnProto() { - return Object.create({ - get 0(){} - }); - } - ObjectWithReadOnlyElementOnProto.from = constructor.from; - assertThrowsInstanceOf(() => ObjectWithReadOnlyElementOnProto.from([1]), TypeError); - // Unlike Array.from, %TypedArray%.from doesn't get or set the length property. - function ObjectWithThrowingLengthGetterSetter() { - Object.defineProperty(this, "length", { + function ObjectWithThrowingLengthGetterSetter(...rest) { + var ta = new constructor(...rest); + Object.defineProperty(ta, "length", { configurable: true, get() { throw new RangeError("getter!"); }, set() { throw new RangeError("setter!"); } }); + return ta; } ObjectWithThrowingLengthGetterSetter.from = constructor.from; - assertEq(ObjectWithThrowingLengthGetterSetter.from(["foo"])[0], "foo"); + assertEq(ObjectWithThrowingLengthGetterSetter.from([123])[0], 123); // %TypedArray%.from throws if mapfn is neither callable nor undefined. assertThrowsInstanceOf(() => constructor.from([3, 4, 5], {}), TypeError); @@ -51,9 +28,11 @@ for (var constructor of anyTypedArrayConstructors) { // If mapfn is not undefined and not callable, the error happens before anything else. // Before calling the constructor, before touching the arrayLike. var log = ""; - function C() { + var obj; + function C(...rest) { log += "C"; - obj = this; + obj = new constructor(...rest); + return obj; } var p = new Proxy({}, { has: function () { log += "1"; }, @@ -72,7 +51,7 @@ for (var constructor of anyTypedArrayConstructors) { var exc = {surprise: "ponies"}; assertThrowsValue(() => constructor.from.call(C, arrayish, () => { throw exc; }), exc); assertEq(log, "lC0"); - assertEq(obj instanceof C, true); + assertEq(obj instanceof constructor, true); // It's a TypeError if the @@iterator property is a primitive (except null and undefined). for (var primitive of ["foo", 17, Symbol(), true]) { diff --git a/js/src/tests/ecma_6/TypedArray/from_mapping.js b/js/src/tests/ecma_6/TypedArray/from_mapping.js index 9976f147802d..1c24815418d8 100644 --- a/js/src/tests/ecma_6/TypedArray/from_mapping.js +++ b/js/src/tests/ecma_6/TypedArray/from_mapping.js @@ -21,9 +21,11 @@ for (var constructor of anyTypedArrayConstructors) { // If the object to be copied is iterable and the constructor is not Array, // mapfn is still called with two arguments. log = []; - function C() {} + function C(...rest) { + return new constructor(...rest); + } C.from = constructor.from; - var c = new C; + var c = new C(2); c[0] = 1; c[1] = 2; assertDeepEq(C.from(["zero", "one"], f), c); diff --git a/js/src/tests/ecma_6/TypedArray/from_proxy.js b/js/src/tests/ecma_6/TypedArray/from_proxy.js deleted file mode 100644 index 411cc3d2e094..000000000000 --- a/js/src/tests/ecma_6/TypedArray/from_proxy.js +++ /dev/null @@ -1,58 +0,0 @@ -for (var constructor of anyTypedArrayConstructors) { - // Two tests involving %TypedArray%.from and a Proxy. - var log = []; - function LoggingProxy(target) { - log.push("target", target); - var h = { - defineProperty: function (t, id) { - log.push("define", id); - return true; - }, - has: function (t, id) { - log.push("has", id); - return id in t; - }, - get: function (t, id) { - log.push("get", id); - return t[id]; - }, - set: function (t, id, v) { - log.push("set", id); - t[id] = v; - return true; - } - }; - return new Proxy(Object(target), h); - } - - // Unlike Array.from, %TypedArray%.from uses [[Put]] instead of [[DefineOwnProperty]]. - // Hence, it calls handler.set to create new elements rather than handler.defineProperty. - // Additionally, it doesn't set the length property at the end. - LoggingProxy.from = constructor.from; - LoggingProxy.from([3, 4, 5]); - assertDeepEq(log, ["target", 3, "set", "0", "set", "1", "set", "2"]); - - // When the argument passed to %TypedArray%.from is a Proxy, %TypedArray%.from - // calls handler.get on it. - log = []; - assertDeepEq(constructor.from(new LoggingProxy([3, 4, 5])), new constructor([3, 4, 5])); - assertDeepEq(log, ["target", [3, 4, 5], - "get", Symbol.iterator, - "get", "length", "get", "0", - "get", "length", "get", "1", - "get", "length", "get", "2", - "get", "length"]); - - // Array-like iteration only gets the length once. - log = []; - var arr = [5, 6, 7]; - arr[Symbol.iterator] = undefined; - assertDeepEq(constructor.from(new LoggingProxy(arr)), new constructor([5, 6, 7])); - assertDeepEq(log, ["target", [5, 6, 7], - "get", Symbol.iterator, - "get", "length", - "get", "0", "get", "1", "get", "2"]); -} - -if (typeof reportCompare === "function") - reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/from_string.js b/js/src/tests/ecma_6/TypedArray/from_string.js index cef9d1e51367..09a2c367104b 100644 --- a/js/src/tests/ecma_6/TypedArray/from_string.js +++ b/js/src/tests/ecma_6/TypedArray/from_string.js @@ -1,23 +1,24 @@ // %TypedArray%.from called on Array should also handle strings correctly. -var from = Int8Array.from.bind(Array); +var from = Int8Array.from.bind(Uint32Array); +var toCodePoint = s => s.codePointAt(0); // %TypedArray%.from on a string iterates over the string. -assertDeepEq(from("test string"), - ['t', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g']); +assertEqArray(from("test string", toCodePoint), + ['t', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g'].map(toCodePoint)); // %TypedArray%.from on a string handles surrogate pairs correctly. var gclef = "\uD834\uDD1E"; // U+1D11E MUSICAL SYMBOL G CLEF -assertDeepEq(from(gclef), [gclef]); -assertDeepEq(from(gclef + " G"), [gclef, " ", "G"]); +assertEqArray(from(gclef, toCodePoint), [gclef].map(toCodePoint)); +assertEqArray(from(gclef + " G", toCodePoint), [gclef, " ", "G"].map(toCodePoint)); // %TypedArray%.from on a string calls the @@iterator method. String.prototype[Symbol.iterator] = function* () { yield 1; yield 2; }; -assertDeepEq(from("anything"), [1, 2]); +assertEqArray(from("anything"), [1, 2]); // If the iterator method is deleted, Strings are still arraylike. delete String.prototype[Symbol.iterator]; -assertDeepEq(from("works"), ['w', 'o', 'r', 'k', 's']); -assertDeepEq(from(gclef), ['\uD834', '\uDD1E']); +assertEqArray(from("works", toCodePoint), ['w', 'o', 'r', 'k', 's'].map(toCodePoint)); +assertEqArray(from(gclef, toCodePoint), ['\uD834', '\uDD1E'].map(toCodePoint)); if (typeof reportCompare === "function") reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/of.js b/js/src/tests/ecma_6/TypedArray/of.js index d2311ed53f30..fffc06325430 100644 --- a/js/src/tests/ecma_6/TypedArray/of.js +++ b/js/src/tests/ecma_6/TypedArray/of.js @@ -15,20 +15,17 @@ for (var constructor of anyTypedArrayConstructors) { assertDeepEq(constructor.of(1, 2, 3), new constructor([1, 2, 3])); assertDeepEq(constructor.of("1", "2", "3"), new constructor([1, 2, 3])); - // This method can be transplanted to other constructors. - assertDeepEq(constructor.of.call(Array, 1, 2, 3), [1, 2, 3]); + // This method can't be transplanted to other constructors. + assertThrows(() => constructor.of.call(Array), TypeError); + assertThrows(() => constructor.of.call(Array, 1, 2, 3), TypeError); var hits = 0; assertDeepEq(constructor.of.call(function(len) { assertEq(arguments.length, 1); assertEq(len, 3); hits++; - return {}; - }, "a", "b", "c"), { - 0: "a", - 1: "b", - 2: "c" - }); + return new constructor(len); + }, 10, 20, 30), new constructor([10, 20, 30])); assertEq(hits, 1); // Behavior across compartments. diff --git a/js/src/tests/ecma_7/TypedArray/browser.js b/js/src/tests/ecma_7/TypedArray/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_7/TypedArray/filter-validation.js b/js/src/tests/ecma_7/TypedArray/filter-validation.js new file mode 100644 index 000000000000..450b4fa2cf83 --- /dev/null +++ b/js/src/tests/ecma_7/TypedArray/filter-validation.js @@ -0,0 +1,185 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.prototype.filter. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayLengths = [0, 1, 1024]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.prototype.filter, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.prototype.filter, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].prototype.filter, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => true), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => false), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + + // Passes when the length is zero. + if (length === 0) { + let result = method.call(ta, () => true); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(ta, () => true), error); + } + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, () => false); + assertEq(result.length, 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, () => true); + assertEq(result.length, length + extraLength); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, () => false); + assertEq(result.length, extraLength); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => true), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => false), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/TypedArray/from-iterable-validation.js b/js/src/tests/ecma_7/TypedArray/from-iterable-validation.js new file mode 100644 index 000000000000..50d46bb81ff9 --- /dev/null +++ b/js/src/tests/ecma_7/TypedArray/from-iterable-validation.js @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.from. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayArgs = [[], [123], [123, ...Array(1023).fill(0)]]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.from, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.from, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].from, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, args), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + // Passes when the length is zero. + if (args.length === 0) { + let result = method.call(species, args); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(species, args), error); + } + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + let result = method.call(species, args); + assertEq(result.length, args.length + extraLength); + assertEq(result[0], (args.length === 0 ? 0 : 123)); + assertEq(result[args.length + extraLength - 1], 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, args), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/TypedArray/from-non-iterable-validation.js b/js/src/tests/ecma_7/TypedArray/from-non-iterable-validation.js new file mode 100644 index 000000000000..3276426b2866 --- /dev/null +++ b/js/src/tests/ecma_7/TypedArray/from-non-iterable-validation.js @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.from. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayArgs = [{length: 0}, {length: 1, 0: 123}, {length: 1024, 0: 123}]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.from, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.from, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].from, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, args), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + // Passes when the length is zero. + if (args.length === 0) { + let result = method.call(species, args); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(species, args), error); + } + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + let result = method.call(species, args); + assertEq(result.length, args.length + extraLength); + assertEq(result[0], (args.length === 0 ? 0 : 123)); + assertEq(result[args.length + extraLength - 1], 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, args), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/TypedArray/map-validation.js b/js/src/tests/ecma_7/TypedArray/map-validation.js new file mode 100644 index 000000000000..1694ef50cb53 --- /dev/null +++ b/js/src/tests/ecma_7/TypedArray/map-validation.js @@ -0,0 +1,149 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.prototype.map. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayLengths = [0, 1, 1024]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.prototype.map, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.prototype.map, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].prototype.map, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => 123), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + + // Passes when the length is zero. + if (length === 0) { + let result = method.call(ta, () => 123); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(ta, () => 123), error); + } + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, () => 123); + assertEq(result.length, length + extraLength); + assertEq(result[0], (length === 0 ? 0 : 123)); + assertEq(result[length + extraLength - 1], 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, () => 123), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/TypedArray/of-validation.js b/js/src/tests/ecma_7/TypedArray/of-validation.js new file mode 100644 index 000000000000..e7ab90519549 --- /dev/null +++ b/js/src/tests/ecma_7/TypedArray/of-validation.js @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.of. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayArgs = [[], [123], [123, ...Array(1023).fill(0)]]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.of, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.of, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].of, + error: otherGlobal.TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, ...args), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + // Passes when the length is zero. + if (args.length === 0) { + let result = method.call(species, ...args); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(species, ...args), error); + } + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + let result = method.call(species, ...args); + assertEq(result.length, args.length + extraLength); + assertEq(result[0], (args.length === 0 ? 0 : 123)); + assertEq(result[args.length + extraLength - 1], 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let args of typedArrayArgs) { + assertThrowsInstanceOf(() => method.call(species, ...args), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/TypedArray/shell.js b/js/src/tests/ecma_7/TypedArray/shell.js new file mode 100644 index 000000000000..3d53f31ee64c --- /dev/null +++ b/js/src/tests/ecma_7/TypedArray/shell.js @@ -0,0 +1,113 @@ +(function(global) { + "use strict"; + + const { + Float32Array, Float64Array, Object, Reflect, SharedArrayBuffer, WeakMap, + assertEq + } = global; + const { + apply: Reflect_apply, + construct: Reflect_construct, + } = Reflect; + const { + get: WeakMap_prototype_get, + has: WeakMap_prototype_has, + } = WeakMap.prototype; + + const sharedConstructors = new WeakMap(); + + // Synthesize a constructor for a shared memory array from the constructor + // for unshared memory. This has "good enough" fidelity for many uses. In + // cases where it's not good enough, call isSharedConstructor for local + // workarounds. + function sharedConstructor(baseConstructor) { + // Create SharedTypedArray as a subclass of %TypedArray%, following the + // built-in %TypedArray% subclasses. + class SharedTypedArray extends Object.getPrototypeOf(baseConstructor) { + constructor(...args) { + var array = Reflect_construct(baseConstructor, args); + var {buffer, byteOffset, length} = array; + var sharedBuffer = new SharedArrayBuffer(buffer.byteLength); + var sharedArray = Reflect_construct(baseConstructor, + [sharedBuffer, byteOffset, length], + new.target); + for (var i = 0; i < length; i++) + sharedArray[i] = array[i]; + assertEq(sharedArray.buffer, sharedBuffer); + return sharedArray; + } + } + + // 22.2.5.1 TypedArray.BYTES_PER_ELEMENT + Object.defineProperty(SharedTypedArray, "BYTES_PER_ELEMENT", + {__proto__: null, value: baseConstructor.BYTES_PER_ELEMENT}); + + // 22.2.6.1 TypedArray.prototype.BYTES_PER_ELEMENT + Object.defineProperty(SharedTypedArray.prototype, "BYTES_PER_ELEMENT", + {__proto__: null, value: baseConstructor.BYTES_PER_ELEMENT}); + + // Share the same name with the base constructor to avoid calling + // isSharedConstructor() in multiple places. + Object.defineProperty(SharedTypedArray, "name", + {__proto__: null, value: baseConstructor.name}); + + sharedConstructors.set(SharedTypedArray, baseConstructor); + + return SharedTypedArray; + } + + /** + * All TypedArray constructors for unshared memory. + */ + const typedArrayConstructors = Object.freeze([ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + ]); + + /** + * All TypedArray constructors for shared memory. + */ + const sharedTypedArrayConstructors = Object.freeze( + typedArrayConstructors.map(sharedConstructor) + ); + + /** + * All TypedArray constructors for unshared and shared memory. + */ + const anyTypedArrayConstructors = Object.freeze([ + ...typedArrayConstructors, + ...(typeof SharedArrayBuffer === "function" ? sharedTypedArrayConstructors : []), + ]); + + /** + * Returns `true` if `constructor` is a TypedArray constructor for shared + * memory. + */ + function isSharedConstructor(constructor) { + return Reflect_apply(WeakMap_prototype_has, sharedConstructors, [constructor]); + } + + /** + * Returns `true` if `constructor` is a TypedArray constructor for shared + * or unshared memory, with an underlying element type of either Float32 or + * Float64. + */ + function isFloatConstructor(constructor) { + if (isSharedConstructor(constructor)) + constructor = Reflect_apply(WeakMap_prototype_get, sharedConstructors, [constructor]); + return constructor == Float32Array || constructor == Float64Array; + } + + global.typedArrayConstructors = typedArrayConstructors; + global.sharedTypedArrayConstructors = sharedTypedArrayConstructors; + global.anyTypedArrayConstructors = anyTypedArrayConstructors; + global.isSharedConstructor = isSharedConstructor; + global.isFloatConstructor = isFloatConstructor; +})(this); diff --git a/js/src/tests/ecma_7/TypedArray/slice-validation.js b/js/src/tests/ecma_7/TypedArray/slice-validation.js new file mode 100644 index 000000000000..95954aafcf72 --- /dev/null +++ b/js/src/tests/ecma_7/TypedArray/slice-validation.js @@ -0,0 +1,187 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.prototype.slice. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayLengths = [0, 1, 1024]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.prototype.slice, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.prototype.slice, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].prototype.slice, + // Note: slice uses CallTypedArrayMethodIfWrapped, which results + // in throwing a TypeError from the wrong Realm. + error: TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is too small. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function TooSmallConstructor(length) { + let a = new TAConstructor(Math.max(length - 1, 0)); + callCount += 1; + return a; + } + function TooSmallConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](Math.max(length - 1, 0)); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooSmallConstructor, TooSmallConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + + // Passes when the length is zero. + if (length === 0) { + let result = method.call(ta, 0); + assertEq(result.length, 0); + } else { + assertThrowsInstanceOf(() => method.call(ta, 0), error); + } + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, 0, 0); + assertEq(result.length, 0); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// No exception when array is larger than requested. +for (const TAConstructor of anyTypedArrayConstructors) { + const extraLength = 1; + + let callCount = 0, expectedCallCount = 0; + function TooLargeConstructor(length) { + let a = new TAConstructor(length + extraLength); + callCount += 1; + return a; + } + function TooLargeConstructorCrossRealm(length) { + let a = new otherGlobal[TAConstructor.name](length + extraLength); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, TooLargeConstructor, TooLargeConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, 0); + assertEq(result.length, length + extraLength); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + let result = method.call(ta, 0, 0); + assertEq(result.length, extraLength); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + otherGlobal.detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/TypedArray/subarray-validation.js b/js/src/tests/ecma_7/TypedArray/subarray-validation.js new file mode 100644 index 000000000000..e81f194fb023 --- /dev/null +++ b/js/src/tests/ecma_7/TypedArray/subarray-validation.js @@ -0,0 +1,110 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Summary: Ensure typed array validation is called for TypedArray.prototype.subarray. + +const otherGlobal = typeof newGlobal === "function" ? newGlobal() : undefined; +const typedArrayLengths = [0, 1, 1024]; + +function createTestCases(TAConstructor, constructor, constructorCrossRealm) { + let testCases = []; + testCases.push({ + species: constructor, + method: TAConstructor.prototype.subarray, + error: TypeError, + }); + if (otherGlobal) { + testCases.push({ + species: constructorCrossRealm, + method: TAConstructor.prototype.subarray, + error: TypeError, + }); + testCases.push({ + species: constructor, + method: otherGlobal[TAConstructor.name].prototype.subarray, + // Note: subarray uses CallTypedArrayMethodIfWrapped, which results + // in throwing a TypeError from the wrong Realm. + error: TypeError, + }); + } + return testCases; +} + +// Throws TypeError when the returned value is not a typed array. +for (const TAConstructor of anyTypedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function NoTypedArrayConstructor(...args) { + let a = []; + callCount += 1; + return a; + } + function NoTypedArrayConstructorCrossRealm(...args) { + let a = new otherGlobal.Array(); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, NoTypedArrayConstructor, NoTypedArrayConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } +} + +// Throws TypeError exception when returned array is detached. +if (typeof detachArrayBuffer === "function") { + for (const TAConstructor of typedArrayConstructors) { + let callCount = 0, expectedCallCount = 0; + function DetachConstructor(...args) { + let a = new TAConstructor(...args); + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + function DetachConstructorCrossRealm(...args) { + let a = new otherGlobal[TAConstructor.name](...args); + // Note: TypedArray |a| is (currently) created in this global, not + // |otherGlobal|, because a typed array and its buffer must + // use the same compartment. + detachArrayBuffer(a.buffer); + callCount += 1; + return a; + } + let testCases = createTestCases(TAConstructor, DetachConstructor, DetachConstructorCrossRealm); + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + + for (let {species, method, error} of testCases) { + for (let length of typedArrayLengths) { + let ta = new TAConstructor(length); + ta.constructor = {[Symbol.species]: species}; + assertThrowsInstanceOf(() => method.call(ta, 0, 0), error); + assertEq(callCount, ++expectedCallCount); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 688fcdf14e5a..87aeedc05095 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1165,7 +1165,6 @@ intrinsic_PossiblyWrappedTypedArrayLength(JSContext* cx, unsigned argc, Value* v MOZ_ASSERT(args[0].isObject()); JSObject* obj = CheckedUnwrap(&args[0].toObject()); - if (!obj) { JS_ReportErrorASCII(cx, "Permission denied to access object"); return false; @@ -1177,6 +1176,25 @@ intrinsic_PossiblyWrappedTypedArrayLength(JSContext* cx, unsigned argc, Value* v return true; } +static bool +intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + JSObject* obj = CheckedUnwrap(&args[0].toObject()); + if (!obj) { + JS_ReportErrorASCII(cx, "Permission denied to access object"); + return false; + } + + MOZ_ASSERT(obj->is()); + bool detached = obj->as().hasDetachedBuffer(); + args.rval().setBoolean(detached); + return true; +} + static bool intrinsic_MoveTypedArrayElements(JSContext* cx, unsigned argc, Value* vp) { @@ -2427,6 +2445,8 @@ static const JSFunctionSpec intrinsic_functions[] = { IntrinsicTypedArrayLength), JS_INLINABLE_FN("PossiblyWrappedTypedArrayLength", intrinsic_PossiblyWrappedTypedArrayLength, 1, 0, IntrinsicPossiblyWrappedTypedArrayLength), + JS_FN("PossiblyWrappedTypedArrayHasDetachedBuffer", + intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer, 1, 0), JS_FN("MoveTypedArrayElements", intrinsic_MoveTypedArrayElements, 4,0), JS_FN("SetFromTypedArrayApproach",intrinsic_SetFromTypedArrayApproach, 4, 0), From 9e839646c7eb69df6265f74b5f430aa95fd2571d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Mon, 10 Oct 2016 13:13:28 -0700 Subject: [PATCH 21/80] Bug 1243717 - Part 0: Set correct function length when parameter expressions, but no defaults are present. r=arai --HG-- extra : rebase_source : b027e738a1741c20757fbc6b958ebf5d2c2c30ed --- js/src/frontend/Parser.cpp | 12 ++++--- ...-destructuring-and-parameter-expression.js | 31 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 js/src/tests/ecma_6/Function/length-with-destructuring-and-parameter-expression.js diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index c4980bfcc46b..b413ff4d3b14 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2608,6 +2608,7 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn } if (hasArguments) { bool hasRest = false; + bool hasDefault = false; bool duplicatedParam = false; bool disallowDuplicateParams = kind == Arrow || kind == Method || kind == ClassConstructor; AtomVector& positionalFormals = pc->positionalFormalParameterNames(); @@ -2724,13 +2725,15 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); return false; } - if (!funbox->hasParameterExprs) { - funbox->hasParameterExprs = true; + + if (!hasDefault) { + hasDefault = true; // The Function.length property is the number of formals // before the first default argument. funbox->length = positionalFormals.length() - 1; } + funbox->hasParameterExprs = true; Node def_expr = assignExprWithoutYield(yieldHandling, JSMSG_YIELD_IN_DEFAULT); if (!def_expr) @@ -2773,9 +2776,10 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn } } - if (!funbox->hasParameterExprs) + if (!hasDefault) funbox->length = positionalFormals.length() - hasRest; - else if (funbox->hasDirectEval()) + + if (funbox->hasParameterExprs && funbox->hasDirectEval()) funbox->hasDirectEvalInParameterExpr = true; funbox->function()->setArgCount(positionalFormals.length()); diff --git a/js/src/tests/ecma_6/Function/length-with-destructuring-and-parameter-expression.js b/js/src/tests/ecma_6/Function/length-with-destructuring-and-parameter-expression.js new file mode 100644 index 000000000000..a3f00ac1f280 --- /dev/null +++ b/js/src/tests/ecma_6/Function/length-with-destructuring-and-parameter-expression.js @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +assertEq(function([a = 0]){}.length, 1); +assertEq(function({p: a = 0}){}.length, 1); +assertEq(function({a = 0}){}.length, 1); +assertEq(function({[0]: a}){}.length, 1); + +assertEq(function([a = 0], [b = 0]){}.length, 2); +assertEq(function({p: a = 0}, [b = 0]){}.length, 2); +assertEq(function({a = 0}, [b = 0]){}.length, 2); +assertEq(function({[0]: a}, [b = 0]){}.length, 2); + +assertEq(function(x, [a = 0]){}.length, 2); +assertEq(function(x, {p: a = 0}){}.length, 2); +assertEq(function(x, {a = 0}){}.length, 2); +assertEq(function(x, {[0]: a}){}.length, 2); + +assertEq(function(x = 0, [a = 0]){}.length, 0); +assertEq(function(x = 0, {p: a = 0}){}.length, 0); +assertEq(function(x = 0, {a = 0}){}.length, 0); +assertEq(function(x = 0, {[0]: a}){}.length, 0); + +assertEq(function([a = 0], ...r){}.length, 1); +assertEq(function({p: a = 0}, ...r){}.length, 1); +assertEq(function({a = 0}, ...r){}.length, 1); +assertEq(function({[0]: a}, ...r){}.length, 1); + +if (typeof reportCompare === "function") + reportCompare(0, 0); From 6d5af6682201343813d7d2f5ea6d97f698816f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Mon, 10 Oct 2016 13:13:58 -0700 Subject: [PATCH 22/80] Bug 1243717 - Part 1: Allow destructuring for rest parameter (ES2016). r=arai --HG-- extra : rebase_source : 157701e5de82412122186cdf78b74a98c435e9e3 --- js/src/builtin/ReflectParse.cpp | 1 - js/src/frontend/BytecodeEmitter.cpp | 5 +- js/src/frontend/NameAnalysisTypes.h | 2 + js/src/frontend/Parser.cpp | 93 +++++++++------- js/src/frontend/Parser.h | 1 + .../rest-parameter-aray-iterator.js | 40 +++++++ .../Destructuring/rest-parameter-arguments.js | 101 ++++++++++++++++++ .../rest-parameter-function-length.js | 41 +++++++ ...rest-parameter-spread-call-optimization.js | 29 +++++ .../Destructuring/rest-parameter-syntax.js | 87 +++++++++++++++ .../ecma_7/Destructuring/rest-parameter.js | 54 ++++++++++ js/src/tests/ecma_7/extensions/browser.js | 0 .../parse-rest-destructuring-parameter.js | 27 +++++ js/src/tests/ecma_7/extensions/shell.js | 0 14 files changed, 441 insertions(+), 40 deletions(-) create mode 100644 js/src/tests/ecma_7/Destructuring/rest-parameter-aray-iterator.js create mode 100644 js/src/tests/ecma_7/Destructuring/rest-parameter-arguments.js create mode 100644 js/src/tests/ecma_7/Destructuring/rest-parameter-function-length.js create mode 100644 js/src/tests/ecma_7/Destructuring/rest-parameter-spread-call-optimization.js create mode 100644 js/src/tests/ecma_7/Destructuring/rest-parameter-syntax.js create mode 100644 js/src/tests/ecma_7/Destructuring/rest-parameter.js create mode 100644 js/src/tests/ecma_7/extensions/browser.js create mode 100644 js/src/tests/ecma_7/extensions/parse-rest-destructuring-parameter.js create mode 100644 js/src/tests/ecma_7/extensions/shell.js diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 11d8abff3e90..6692783718cc 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -3496,7 +3496,6 @@ ASTSerializer::functionArgs(ParseNode* pn, ParseNode* pnargs, if (!pattern(pat, &node)) return false; if (rest.isUndefined() && arg->pn_next == pnargs->last()) { - MOZ_ASSERT(arg->isKind(PNK_NAME)); rest.setObject(node.toObject()); } else { if (!args.append(node)) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 6ae4f49fd5f2..33ff9144fae9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -7858,7 +7858,10 @@ BytecodeEmitter::isRestParameter(ParseNode* pn, bool* result) if (paramLoc && lookupName(name) == *paramLoc) { FunctionScope::Data* bindings = funbox->functionScopeBindings(); if (bindings->nonPositionalFormalStart > 0) { - *result = name == bindings->names[bindings->nonPositionalFormalStart - 1].name(); + // |paramName| can be nullptr when the rest destructuring syntax is + // used: `function f(...[]) {}`. + JSAtom* paramName = bindings->names[bindings->nonPositionalFormalStart - 1].name(); + *result = paramName && name == paramName; return true; } } diff --git a/js/src/frontend/NameAnalysisTypes.h b/js/src/frontend/NameAnalysisTypes.h index dbb28f18abd5..d39e177fba1a 100644 --- a/js/src/frontend/NameAnalysisTypes.h +++ b/js/src/frontend/NameAnalysisTypes.h @@ -71,6 +71,7 @@ enum class DeclarationKind : uint8_t { PositionalFormalParameter, FormalParameter, + CoverArrowParameter, Var, ForOfVar, Let, @@ -89,6 +90,7 @@ DeclarationKindToBindingKind(DeclarationKind kind) switch (kind) { case DeclarationKind::PositionalFormalParameter: case DeclarationKind::FormalParameter: + case DeclarationKind::CoverArrowParameter: return BindingKind::FormalParameter; case DeclarationKind::Var: diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index b413ff4d3b14..0433f63bbb84 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -93,6 +93,8 @@ DeclarationKindString(DeclarationKind kind) case DeclarationKind::PositionalFormalParameter: case DeclarationKind::FormalParameter: return "formal parameter"; + case DeclarationKind::CoverArrowParameter: + return "cover arrow parameter"; case DeclarationKind::Var: return "var"; case DeclarationKind::Let: @@ -1300,6 +1302,10 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind break; } + case DeclarationKind::CoverArrowParameter: + // CoverArrowParameter is only used as a placeholder declaration kind. + break; + case DeclarationKind::PositionalFormalParameter: MOZ_CRASH("Positional formal parameter names should use " "notePositionalFormalParameter"); @@ -2628,12 +2634,39 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn if (!tokenStream.getToken(&tt, TokenStream::Operand)) return false; MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME || tt == TOK_YIELD); + + if (tt == TOK_TRIPLEDOT) { + if (IsSetterKind(kind)) { + report(ParseError, false, null(), + JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); + return false; + } + + disallowDuplicateParams = true; + if (duplicatedParam) { + // Has duplicated args before the rest parameter. + report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + return false; + } + + hasRest = true; + funbox->function()->setHasRest(); + + if (!tokenStream.getToken(&tt)) + return false; + + if (tt != TOK_NAME && tt != TOK_YIELD && tt != TOK_LB && tt != TOK_LC) { + report(ParseError, false, null(), JSMSG_NO_REST_NAME); + return false; + } + } + switch (tt) { case TOK_LB: case TOK_LC: { - /* See comment below in the TOK_NAME case. */ disallowDuplicateParams = true; if (duplicatedParam) { + // Has duplicated args before the destructuring parameter. report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); return false; } @@ -2653,31 +2686,6 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn break; } - case TOK_TRIPLEDOT: - if (IsSetterKind(kind)) { - report(ParseError, false, null(), - JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); - return false; - } - - hasRest = true; - funbox->function()->setHasRest(); - - disallowDuplicateParams = true; - if (duplicatedParam) { - // Has duplicated args before the rest parameter. - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); - return false; - } - - if (!tokenStream.getToken(&tt)) - return false; - if (tt != TOK_NAME && tt != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_NO_REST_NAME); - return false; - } - MOZ_FALLTHROUGH; - case TOK_NAME: case TOK_YIELD: { if (parenFreeArrow) @@ -3941,9 +3949,8 @@ Parser::checkDestructuringArray(ParseNode* arrayPattern, * pc->inDestructuringDecl clear, so the lvalue expressions in the * pattern are parsed normally. primaryExpr links variable references * into the appropriate use chains; creates placeholder definitions; - * and so on. checkDestructuringPattern is called with |data| nullptr - * (since we won't be binding any new names), and we specialize lvalues - * as appropriate. + * and so on. checkDestructuringPattern won't bind any new names and + * we specialize lvalues as appropriate. * * In declaration-like contexts, the normal variable reference * processing would just be an obstruction, because we're going to @@ -3953,8 +3960,7 @@ Parser::checkDestructuringArray(ParseNode* arrayPattern, * whatever name nodes it creates unconnected. Then, here in * checkDestructuringPattern, we require the pattern's property value * positions to be simple names, and define them as appropriate to the - * context. For these calls, |data| points to the right sort of - * BindData. + * context. */ template <> bool @@ -9085,13 +9091,24 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling if (!tokenStream.getToken(&next)) return null(); - // This doesn't check that the provided name is allowed, e.g. if the - // enclosing code is strict mode code, any of "arguments", "let", or - // "yield" should be prohibited. Argument-parsing code handles that. - if (next != TOK_NAME && next != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "rest argument name", TokenKindToDesc(next)); - return null(); + if (next == TOK_LB || next == TOK_LC) { + // Validate, but don't store the pattern right now. The whole arrow + // function is reparsed in functionFormalParametersAndBody(). + if (!destructuringDeclaration(DeclarationKind::CoverArrowParameter, yieldHandling, + next)) + { + return null(); + } + } else { + // This doesn't check that the provided name is allowed, e.g. if + // the enclosing code is strict mode code, any of "let", "yield", + // or "arguments" should be prohibited. Argument-parsing code + // handles that. + if (next != TOK_NAME && next != TOK_YIELD) { + report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, + "rest argument name", TokenKindToDesc(next)); + return null(); + } } if (!tokenStream.getToken(&next)) diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 7523453e625f..cd420f4bd079 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -16,6 +16,7 @@ #include "frontend/BytecodeCompiler.h" #include "frontend/FullParseHandler.h" +#include "frontend/NameAnalysisTypes.h" #include "frontend/NameCollections.h" #include "frontend/SharedContext.h" #include "frontend/SyntaxParseHandler.h" diff --git a/js/src/tests/ecma_7/Destructuring/rest-parameter-aray-iterator.js b/js/src/tests/ecma_7/Destructuring/rest-parameter-aray-iterator.js new file mode 100644 index 000000000000..bf9643fd780c --- /dev/null +++ b/js/src/tests/ecma_7/Destructuring/rest-parameter-aray-iterator.js @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Destructuring rest arrays call the array iterator. This behaviour is +// observable when Array.prototype[Symbol.iterator] is overridden. + +const oldArrayIterator = Array.prototype[Symbol.iterator]; +try { + let callCount = 0; + Array.prototype[Symbol.iterator] = function() { + callCount += 1; + return oldArrayIterator.call(this); + }; + + // Array iterator called exactly once. + function arrayIterCalledOnce(...[]) { } + assertEq(callCount, 0); + arrayIterCalledOnce(); + assertEq(callCount, 1); + + // Array iterator not called before rest parameter. + callCount = 0; + function arrayIterNotCalledBeforeRest(t = assertEq(callCount, 0), ...[]) { } + assertEq(callCount, 0); + arrayIterNotCalledBeforeRest(); + assertEq(callCount, 1); + + // Array iterator called when rest parameter is processed. + callCount = 0; + function arrayIterCalledWhenDestructuring(...[t = assertEq(callCount, 1)]) { } + assertEq(callCount, 0); + arrayIterCalledWhenDestructuring(); + assertEq(callCount, 1); +} finally { + Array.prototype[Symbol.iterator] = oldArrayIterator; +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/Destructuring/rest-parameter-arguments.js b/js/src/tests/ecma_7/Destructuring/rest-parameter-arguments.js new file mode 100644 index 000000000000..e87cdff33964 --- /dev/null +++ b/js/src/tests/ecma_7/Destructuring/rest-parameter-arguments.js @@ -0,0 +1,101 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Ensure the |arguments| object works as expected when a destructuring rest +// parameter is present. + +// |arguments.length| with destructuring rest array. +function argsLengthEmptyRestArray(...[]) { + return arguments.length; +} +assertEq(argsLengthEmptyRestArray(), 0); +assertEq(argsLengthEmptyRestArray(10), 1); +assertEq(argsLengthEmptyRestArray(10, 20), 2); + +function argsLengthRestArray(...[a]) { + return arguments.length; +} +assertEq(argsLengthRestArray(), 0); +assertEq(argsLengthRestArray(10), 1); +assertEq(argsLengthRestArray(10, 20), 2); + +function argsLengthRestArrayWithDefault(...[a = 0]) { + return arguments.length; +} +assertEq(argsLengthRestArrayWithDefault(), 0); +assertEq(argsLengthRestArrayWithDefault(10), 1); +assertEq(argsLengthRestArrayWithDefault(10, 20), 2); + + +// |arguments.length| with destructuring rest object. +function argsLengthEmptyRestObject(...{}) { + return arguments.length; +} +assertEq(argsLengthEmptyRestObject(), 0); +assertEq(argsLengthEmptyRestObject(10), 1); +assertEq(argsLengthEmptyRestObject(10, 20), 2); + +function argsLengthRestObject(...{a}) { + return arguments.length; +} +assertEq(argsLengthRestObject(), 0); +assertEq(argsLengthRestObject(10), 1); +assertEq(argsLengthRestObject(10, 20), 2); + +function argsLengthRestObjectWithDefault(...{a = 0}) { + return arguments.length; +} +assertEq(argsLengthRestObjectWithDefault(), 0); +assertEq(argsLengthRestObjectWithDefault(10), 1); +assertEq(argsLengthRestObjectWithDefault(10, 20), 2); + + +// |arguments| access with destructuring rest array. +function argsAccessEmptyRestArray(...[]) { + return arguments[0]; +} +assertEq(argsAccessEmptyRestArray(), undefined); +assertEq(argsAccessEmptyRestArray(10), 10); +assertEq(argsAccessEmptyRestArray(10, 20), 10); + +function argsAccessRestArray(...[a]) { + return arguments[0]; +} +assertEq(argsAccessRestArray(), undefined); +assertEq(argsAccessRestArray(10), 10); +assertEq(argsAccessRestArray(10, 20), 10); + +function argsAccessRestArrayWithDefault(...[a = 0]) { + return arguments[0]; +} +assertEq(argsAccessRestArrayWithDefault(), undefined); +assertEq(argsAccessRestArrayWithDefault(10), 10); +assertEq(argsAccessRestArrayWithDefault(10, 20), 10); + + +// |arguments| access with destructuring rest object. +function argsAccessEmptyRestObject(...{}) { + return arguments[0]; +} +assertEq(argsAccessEmptyRestObject(), undefined); +assertEq(argsAccessEmptyRestObject(10), 10); +assertEq(argsAccessEmptyRestObject(10, 20), 10); + +function argsAccessRestObject(...{a}) { + return arguments[0]; +} +assertEq(argsAccessRestObject(), undefined); +assertEq(argsAccessRestObject(10), 10); +assertEq(argsAccessRestObject(10, 20), 10); + +function argsAccessRestObjectWithDefault(...{a = 0}) { + return arguments[0]; +} +assertEq(argsAccessRestObjectWithDefault(), undefined); +assertEq(argsAccessRestObjectWithDefault(10), 10); +assertEq(argsAccessRestObjectWithDefault(10, 20), 10); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/Destructuring/rest-parameter-function-length.js b/js/src/tests/ecma_7/Destructuring/rest-parameter-function-length.js new file mode 100644 index 000000000000..5924e799ad44 --- /dev/null +++ b/js/src/tests/ecma_7/Destructuring/rest-parameter-function-length.js @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Ensure function length is set correctly when a destructuring rest parameter +// is present. + +assertEq(function(...[]) {}.length, 0); +assertEq(function(...[a]) {}.length, 0); +assertEq(function(...[a = 0]) {}.length, 0); +assertEq(function(...{}) {}.length, 0); +assertEq(function(...{p: a}) {}.length, 0); +assertEq(function(...{p: a = 0}) {}.length, 0); +assertEq(function(...{a = 0}) {}.length, 0); + +assertEq(function(x, ...[]) {}.length, 1); +assertEq(function(x, ...[a]) {}.length, 1); +assertEq(function(x, ...[a = 0]) {}.length, 1); +assertEq(function(x, ...{}) {}.length, 1); +assertEq(function(x, ...{p: a}) {}.length, 1); +assertEq(function(x, ...{p: a = 0}) {}.length, 1); +assertEq(function(x, ...{a = 0}) {}.length, 1); + +assertEq(function(x, y, ...[]) {}.length, 2); +assertEq(function(x, y, ...[a]) {}.length, 2); +assertEq(function(x, y, ...[a = 0]) {}.length, 2); +assertEq(function(x, y, ...{}) {}.length, 2); +assertEq(function(x, y, ...{p: a}) {}.length, 2); +assertEq(function(x, y, ...{p: a = 0}) {}.length, 2); +assertEq(function(x, y, ...{a = 0}) {}.length, 2); + +assertEq(function(x, y = 0, ...[]) {}.length, 1); +assertEq(function(x, y = 0, ...[a]) {}.length, 1); +assertEq(function(x, y = 0, ...[a = 0]) {}.length, 1); +assertEq(function(x, y = 0, ...{}) {}.length, 1); +assertEq(function(x, y = 0, ...{p: a}) {}.length, 1); +assertEq(function(x, y = 0, ...{p: a = 0}) {}.length, 1); +assertEq(function(x, y = 0, ...{a = 0}) {}.length, 1); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/Destructuring/rest-parameter-spread-call-optimization.js b/js/src/tests/ecma_7/Destructuring/rest-parameter-spread-call-optimization.js new file mode 100644 index 000000000000..20f6a529dca2 --- /dev/null +++ b/js/src/tests/ecma_7/Destructuring/rest-parameter-spread-call-optimization.js @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Ensure the spread-call optimization doesn't break when a destructuring rest +// parameter is used. + +function spreadTarget() { return arguments.length; } + +function spreadOpt(...[r]){ return spreadTarget(...r); } +assertEq(spreadOpt([]), 0); +assertEq(spreadOpt([10]), 1); +assertEq(spreadOpt([10, 20]), 2); +assertEq(spreadOpt([10, 20, 30]), 3); + +function spreadOpt2(...[...r]){ return spreadTarget(...r); } +assertEq(spreadOpt2(), 0); +assertEq(spreadOpt2(10), 1); +assertEq(spreadOpt2(10, 20), 2); +assertEq(spreadOpt2(10, 20, 30), 3); + +function spreadOpt3(r, ...[]){ return spreadTarget(...r); } +assertEq(spreadOpt3([]), 0); +assertEq(spreadOpt3([10]), 1); +assertEq(spreadOpt3([10, 20]), 2); +assertEq(spreadOpt3([10, 20, 30]), 3); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/Destructuring/rest-parameter-syntax.js b/js/src/tests/ecma_7/Destructuring/rest-parameter-syntax.js new file mode 100644 index 000000000000..a145d1a2dff8 --- /dev/null +++ b/js/src/tests/ecma_7/Destructuring/rest-parameter-syntax.js @@ -0,0 +1,87 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +const bindingPatterns = [ + "[]", + "[a]", + "[a, b]", + "[a, ...b]", + "[...a]", + "[...[]]", + + "{}", + "{p: a}", + "{p: a = 0}", + "{p: {}}", + "{p: a, q: b}", + "{a}", + "{a, b}", + "{a = 0}", +]; + +const functions = [ + p => `function f(${p}) {}`, + p => `function* g(${p}) {}`, + p => `({m(${p}) {}});`, + p => `(class {m(${p}) {}});`, + p => `(${p}) => {};`, +]; + +for (let pattern of bindingPatterns) { + for (let fn of functions) { + // No leading parameters. + eval(fn(`...${pattern}`)); + + // Leading normal parameters. + eval(fn(`x, ...${pattern}`)); + eval(fn(`x, y, ...${pattern}`)); + + // Leading parameters with defaults. + eval(fn(`x = 0, ...${pattern}`)); + eval(fn(`x = 0, y = 0, ...${pattern}`)); + + // Leading array destructuring parameters. + eval(fn(`[], ...${pattern}`)); + eval(fn(`[x], ...${pattern}`)); + eval(fn(`[x = 0], ...${pattern}`)); + eval(fn(`[...x], ...${pattern}`)); + + // Leading object destructuring parameters. + eval(fn(`{}, ...${pattern}`)); + eval(fn(`{p: x}, ...${pattern}`)); + eval(fn(`{x}, ...${pattern}`)); + eval(fn(`{x = 0}, ...${pattern}`)); + + // Trailing parameters after rest parameter. + assertThrowsInstanceOf(() => eval(fn(`...${pattern},`)), SyntaxError); + assertThrowsInstanceOf(() => eval(fn(`...${pattern}, x`)), SyntaxError); + assertThrowsInstanceOf(() => eval(fn(`...${pattern}, x = 0`)), SyntaxError); + assertThrowsInstanceOf(() => eval(fn(`...${pattern}, ...x`)), SyntaxError); + assertThrowsInstanceOf(() => eval(fn(`...${pattern}, []`)), SyntaxError); + assertThrowsInstanceOf(() => eval(fn(`...${pattern}, {}`)), SyntaxError); + + // Rest parameter with defaults. + assertThrowsInstanceOf(() => eval(fn(`...${pattern} = 0`)), SyntaxError); + } +} + +for (let fn of functions) { + // Missing name, incomplete patterns. + assertThrowsInstanceOf(() => eval(fn(`...`)), SyntaxError); + assertThrowsInstanceOf(() => eval(fn(`...[`)), SyntaxError); + assertThrowsInstanceOf(() => eval(fn(`...{`)), SyntaxError); + + // Invalid binding name. + assertThrowsInstanceOf(() => eval(fn(`...[0]`)), SyntaxError); + assertThrowsInstanceOf(() => eval(fn(`...[p.q]`)), SyntaxError); +} + +// Rest parameters aren't valid in getter/setter methods. +assertThrowsInstanceOf(() => eval(`({get p(...[]) {}})`), SyntaxError); +assertThrowsInstanceOf(() => eval(`({set p(...[]) {}})`), SyntaxError); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/Destructuring/rest-parameter.js b/js/src/tests/ecma_7/Destructuring/rest-parameter.js new file mode 100644 index 000000000000..50d77f3cc519 --- /dev/null +++ b/js/src/tests/ecma_7/Destructuring/rest-parameter.js @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Simple functional test for destructuring rest parameters. + +function arrayRest(...[a, b]) { + return a + b; +} +assertEq(arrayRest(3, 7), 10); + + +function arrayRestWithDefault(...[a, b = 1]) { + return a + b; +} +assertEq(arrayRestWithDefault(3, 7), 10); +assertEq(arrayRestWithDefault(4), 5); +assertEq(arrayRestWithDefault(4, undefined), 5); + + +function objectRest(...{length: len}) { + return len; +} +assertEq(objectRest(), 0); +assertEq(objectRest(10), 1); +assertEq(objectRest(10, 20), 2); + + +function objectRestWithDefault(...{0: a, 1: b = 1}) { + return a + b; +} +assertEq(objectRestWithDefault(3, 7), 10); +assertEq(objectRestWithDefault(4), 5); +assertEq(objectRestWithDefault(4, undefined), 5); + + +function arrayRestWithNestedRest(...[...r]) { + return r.length; +} +assertEq(arrayRestWithNestedRest(), 0); +assertEq(arrayRestWithNestedRest(10), 1); +assertEq(arrayRestWithNestedRest(10, 20), 2); + + +function arrayRestTDZ(...[a = a]) { } +assertThrowsInstanceOf(() => arrayRestTDZ(), ReferenceError); + + +function objectRestTDZ(...{a = a}) { } +assertThrowsInstanceOf(() => objectRestTDZ(), ReferenceError); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/extensions/browser.js b/js/src/tests/ecma_7/extensions/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_7/extensions/parse-rest-destructuring-parameter.js b/js/src/tests/ecma_7/extensions/parse-rest-destructuring-parameter.js new file mode 100644 index 000000000000..dda67c95f3ac --- /dev/null +++ b/js/src/tests/ecma_7/extensions/parse-rest-destructuring-parameter.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!xulRuntime.shell) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function funArgs(params) { + return Reflect.parse(`function f(${params}) {}`).body[0].rest; +} + +var arrayRest = funArgs("...[]"); +assertEq(arrayRest.type, "ArrayPattern"); +assertEq(arrayRest.elements.length, 0); + +arrayRest = funArgs("...[a]"); +assertEq(arrayRest.type, "ArrayPattern"); +assertEq(arrayRest.elements.length, 1); + +var objectRest = funArgs("...{}"); +assertEq(objectRest.type, "ObjectPattern"); +assertEq(objectRest.properties.length, 0); + +objectRest = funArgs("...{p: a}"); +assertEq(objectRest.type, "ObjectPattern"); +assertEq(objectRest.properties.length, 1); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_7/extensions/shell.js b/js/src/tests/ecma_7/extensions/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 From f54639ddc39e012c82714ab02c9ee43f9c78e4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Mon, 17 Oct 2016 09:19:50 -0700 Subject: [PATCH 23/80] Bug 1303795 - Self-host Reflect.apply and Reflect.construct. r=till --- js/src/builtin/Function.js | 131 +++++++++---------- js/src/builtin/Reflect.cpp | 98 +------------- js/src/builtin/Reflect.js | 103 ++++++++++++++- js/src/builtin/RegExp.js | 11 +- js/src/builtin/SelfHostingDefines.h | 21 +++ js/src/tests/ecma_6/Reflect/argumentsList.js | 2 + js/src/vm/ArgumentsObject.h | 1 + 7 files changed, 195 insertions(+), 172 deletions(-) diff --git a/js/src/builtin/Function.js b/js/src/builtin/Function.js index a0458939b805..628ee79c59d1 100644 --- a/js/src/builtin/Function.js +++ b/js/src/builtin/Function.js @@ -85,40 +85,39 @@ function FunctionBind(thisArg, ...boundArgs) { */ function bind_bindFunction0(fun, thisArg, boundArgs) { return function bound() { - var a = arguments; var newTarget; if (_IsConstructing()) { newTarget = new.target; if (newTarget === bound) newTarget = fun; - switch (a.length) { + switch (arguments.length) { case 0: return constructContentFunction(fun, newTarget); case 1: - return constructContentFunction(fun, newTarget, a[0]); + return constructContentFunction(fun, newTarget, SPREAD(arguments, 1)); case 2: - return constructContentFunction(fun, newTarget, a[0], a[1]); + return constructContentFunction(fun, newTarget, SPREAD(arguments, 2)); case 3: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2]); + return constructContentFunction(fun, newTarget, SPREAD(arguments, 3)); case 4: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3]); + return constructContentFunction(fun, newTarget, SPREAD(arguments, 4)); case 5: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3], a[4]); + return constructContentFunction(fun, newTarget, SPREAD(arguments, 5)); } } else { - switch (a.length) { + switch (arguments.length) { case 0: return callContentFunction(fun, thisArg); case 1: - return callContentFunction(fun, thisArg, a[0]); + return callContentFunction(fun, thisArg, SPREAD(arguments, 1)); case 2: - return callContentFunction(fun, thisArg, a[0], a[1]); + return callContentFunction(fun, thisArg, SPREAD(arguments, 2)); case 3: - return callContentFunction(fun, thisArg, a[0], a[1], a[2]); + return callContentFunction(fun, thisArg, SPREAD(arguments, 3)); case 4: - return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3]); + return callContentFunction(fun, thisArg, SPREAD(arguments, 4)); case 5: - return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4]); + return callContentFunction(fun, thisArg, SPREAD(arguments, 5)); } } var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); @@ -129,40 +128,39 @@ function bind_bindFunction0(fun, thisArg, boundArgs) { function bind_bindFunction1(fun, thisArg, boundArgs) { var bound1 = boundArgs[0]; return function bound() { - var a = arguments; var newTarget; if (_IsConstructing()) { newTarget = new.target; if (newTarget === bound) newTarget = fun; - switch (a.length) { + switch (arguments.length) { case 0: return constructContentFunction(fun, newTarget, bound1); case 1: - return constructContentFunction(fun, newTarget, bound1, a[0]); + return constructContentFunction(fun, newTarget, bound1, SPREAD(arguments, 1)); case 2: - return constructContentFunction(fun, newTarget, bound1, a[0], a[1]); + return constructContentFunction(fun, newTarget, bound1, SPREAD(arguments, 2)); case 3: - return constructContentFunction(fun, newTarget, bound1, a[0], a[1], a[2]); + return constructContentFunction(fun, newTarget, bound1, SPREAD(arguments, 3)); case 4: - return constructContentFunction(fun, newTarget, bound1, a[0], a[1], a[2], a[3]); + return constructContentFunction(fun, newTarget, bound1, SPREAD(arguments, 4)); case 5: - return constructContentFunction(fun, newTarget, bound1, a[0], a[1], a[2], a[3], a[4]); + return constructContentFunction(fun, newTarget, bound1, SPREAD(arguments, 5)); } } else { - switch (a.length) { + switch (arguments.length) { case 0: return callContentFunction(fun, thisArg, bound1); case 1: - return callContentFunction(fun, thisArg, bound1, a[0]); + return callContentFunction(fun, thisArg, bound1, SPREAD(arguments, 1)); case 2: - return callContentFunction(fun, thisArg, bound1, a[0], a[1]); + return callContentFunction(fun, thisArg, bound1, SPREAD(arguments, 2)); case 3: - return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2]); + return callContentFunction(fun, thisArg, bound1, SPREAD(arguments, 3)); case 4: - return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2], a[3]); + return callContentFunction(fun, thisArg, bound1, SPREAD(arguments, 4)); case 5: - return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2], a[3], a[4]); + return callContentFunction(fun, thisArg, bound1, SPREAD(arguments, 5)); } } var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); @@ -174,40 +172,39 @@ function bind_bindFunction2(fun, thisArg, boundArgs) { var bound1 = boundArgs[0]; var bound2 = boundArgs[1]; return function bound() { - var a = arguments; var newTarget; if (_IsConstructing()) { newTarget = new.target; if (newTarget === bound) newTarget = fun; - switch (a.length) { + switch (arguments.length) { case 0: return constructContentFunction(fun, newTarget, bound1, bound2); case 1: - return constructContentFunction(fun, newTarget, bound1, bound2, a[0]); + return constructContentFunction(fun, newTarget, bound1, bound2, SPREAD(arguments, 1)); case 2: - return constructContentFunction(fun, newTarget, bound1, bound2, a[0], a[1]); + return constructContentFunction(fun, newTarget, bound1, bound2, SPREAD(arguments, 2)); case 3: - return constructContentFunction(fun, newTarget, bound1, bound2, a[0], a[1], a[2]); + return constructContentFunction(fun, newTarget, bound1, bound2, SPREAD(arguments, 3)); case 4: - return constructContentFunction(fun, newTarget, bound1, bound2, a[0], a[1], a[2], a[3]); + return constructContentFunction(fun, newTarget, bound1, bound2, SPREAD(arguments, 4)); case 5: - return constructContentFunction(fun, newTarget, bound1, bound2, a[0], a[1], a[2], a[3], a[4]); + return constructContentFunction(fun, newTarget, bound1, bound2, SPREAD(arguments, 5)); } } else { - switch (a.length) { + switch (arguments.length) { case 0: return callContentFunction(fun, thisArg, bound1, bound2); case 1: - return callContentFunction(fun, thisArg, bound1, bound2, a[0]); + return callContentFunction(fun, thisArg, bound1, bound2, SPREAD(arguments, 1)); case 2: - return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1]); + return callContentFunction(fun, thisArg, bound1, bound2, SPREAD(arguments, 2)); case 3: - return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2]); + return callContentFunction(fun, thisArg, bound1, bound2, SPREAD(arguments, 3)); case 4: - return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2], a[3]); + return callContentFunction(fun, thisArg, bound1, bound2, SPREAD(arguments, 4)); case 5: - return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2], a[3], a[4]); + return callContentFunction(fun, thisArg, bound1, bound2, SPREAD(arguments, 5)); } } var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); @@ -256,62 +253,62 @@ function bind_invokeFunctionN(fun, thisArg, newTarget, boundArgs, callArgs) { return bind_applyFunctionN(fun, thisArg, args); } -function bind_applyFunctionN(fun, thisArg, a) { - switch (a.length) { +function bind_applyFunctionN(fun, thisArg, args) { + switch (args.length) { case 0: return callContentFunction(fun, thisArg); case 1: - return callContentFunction(fun, thisArg, a[0]); + return callContentFunction(fun, thisArg, SPREAD(args, 1)); case 2: - return callContentFunction(fun, thisArg, a[0], a[1]); + return callContentFunction(fun, thisArg, SPREAD(args, 2)); case 3: - return callContentFunction(fun, thisArg, a[0], a[1], a[2]); + return callContentFunction(fun, thisArg, SPREAD(args, 3)); case 4: - return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3]); + return callContentFunction(fun, thisArg, SPREAD(args, 4)); case 5: - return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4]); + return callContentFunction(fun, thisArg, SPREAD(args, 5)); case 6: - return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5]); + return callContentFunction(fun, thisArg, SPREAD(args, 6)); case 7: - return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6]); + return callContentFunction(fun, thisArg, SPREAD(args, 7)); case 8: - return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); + return callContentFunction(fun, thisArg, SPREAD(args, 8)); case 9: - return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); + return callContentFunction(fun, thisArg, SPREAD(args, 9)); default: - return FUN_APPLY(fun, thisArg, a); + return FUN_APPLY(fun, thisArg, args); } } -function bind_constructFunctionN(fun, newTarget, a) { - switch (a.length) { +function bind_constructFunctionN(fun, newTarget, args) { + switch (args.length) { case 1: - return constructContentFunction(fun, newTarget, a[0]); + return constructContentFunction(fun, newTarget, SPREAD(args, 1)); case 2: - return constructContentFunction(fun, newTarget, a[0], a[1]); + return constructContentFunction(fun, newTarget, SPREAD(args, 2)); case 3: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2]); + return constructContentFunction(fun, newTarget, SPREAD(args, 3)); case 4: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3]); + return constructContentFunction(fun, newTarget, SPREAD(args, 4)); case 5: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3], a[4]); + return constructContentFunction(fun, newTarget, SPREAD(args, 5)); case 6: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3], a[4], a[5]); + return constructContentFunction(fun, newTarget, SPREAD(args, 6)); case 7: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3], a[4], a[5], a[6]); + return constructContentFunction(fun, newTarget, SPREAD(args, 7)); case 8: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); + return constructContentFunction(fun, newTarget, SPREAD(args, 8)); case 9: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); + return constructContentFunction(fun, newTarget, SPREAD(args, 9)); case 10: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]); + return constructContentFunction(fun, newTarget, SPREAD(args, 10)); case 11: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10]); + return constructContentFunction(fun, newTarget, SPREAD(args, 11)); case 12: - return constructContentFunction(fun, newTarget, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]); + return constructContentFunction(fun, newTarget, SPREAD(args, 12)); default: - assert(a.length !== 0, + assert(args.length !== 0, "bound function construction without args should be handled by caller"); - return _ConstructFunction(fun, newTarget, a); + return _ConstructFunction(fun, newTarget, args); } } diff --git a/js/src/builtin/Reflect.cpp b/js/src/builtin/Reflect.cpp index 7f47bfb4ae30..2f509a226827 100644 --- a/js/src/builtin/Reflect.cpp +++ b/js/src/builtin/Reflect.cpp @@ -19,100 +19,6 @@ using namespace js; /*** Reflect methods *****************************************************************************/ -/* - * ES draft rev 32 (2015 Feb 2) 7.3.17 CreateListFromArrayLike. - * The elementTypes argument is not supported. The result list is - * pushed to *args. - */ -template -static bool -InitArgsFromArrayLike(JSContext* cx, HandleValue v, InvokeArgs* args) -{ - // Step 3. - RootedObject obj(cx, NonNullObject(cx, v)); - if (!obj) - return false; - - // Steps 4-5. - uint32_t len; - if (!GetLengthProperty(cx, obj, &len)) - return false; - - // Allocate space for the arguments. - if (!args->init(cx, len)) - return false; - - // Steps 6-8. - for (uint32_t index = 0; index < len; index++) { - if (!GetElement(cx, obj, obj, index, (*args)[index])) - return false; - } - - // Step 9. - return true; -} - -/* ES6 26.1.1 Reflect.apply(target, thisArgument, argumentsList) */ -static bool -Reflect_apply(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1. - if (!IsCallable(args.get(0))) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, - "Reflect.apply argument"); - return false; - } - - // Steps 2-3. - FastCallGuard fig(cx, args.get(0)); - InvokeArgs& invokeArgs = fig.args(); - if (!InitArgsFromArrayLike(cx, args.get(2), &invokeArgs)) - return false; - - // Steps 4-5. This is specified to be a tail call, but isn't. - return fig.call(cx, args.get(0), args.get(1), args.rval()); -} - -/* ES6 26.1.2 Reflect.construct(target, argumentsList [, newTarget]) */ -static bool -Reflect_construct(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1. - if (!IsConstructor(args.get(0))) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR, - "Reflect.construct argument"); - return false; - } - - // Steps 2-3. - RootedValue newTarget(cx, args.get(0)); - if (argc > 2) { - newTarget = args[2]; - if (!IsConstructor(newTarget)) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR, - "Reflect.construct argument 3"); - return false; - } - } - - // Step 4-5. - ConstructArgs constructArgs(cx); - if (!InitArgsFromArrayLike(cx, args.get(1), &constructArgs)) - return false; - - // Step 6. - RootedObject obj(cx); - if (!Construct(cx, args.get(0), constructArgs, newTarget, &obj)) - return false; - - args.rval().setObject(*obj); - return true; -} - /* ES6 26.1.3 Reflect.defineProperty(target, propertyKey, attributes) */ static bool Reflect_defineProperty(JSContext* cx, unsigned argc, Value* vp) @@ -340,8 +246,8 @@ Reflect_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) } static const JSFunctionSpec methods[] = { - JS_FN("apply", Reflect_apply, 3, 0), - JS_FN("construct", Reflect_construct, 2, 0), + JS_SELF_HOSTED_FN("apply", "Reflect_apply", 3, 0), + JS_SELF_HOSTED_FN("construct", "Reflect_construct", 2, 0), JS_FN("defineProperty", Reflect_defineProperty, 3, 0), JS_FN("deleteProperty", Reflect_deleteProperty, 2, 0), JS_FN("get", Reflect_get, 2, 0), diff --git a/js/src/builtin/Reflect.js b/js/src/builtin/Reflect.js index e31f11e9ac8b..e3a343452d3a 100644 --- a/js/src/builtin/Reflect.js +++ b/js/src/builtin/Reflect.js @@ -2,12 +2,111 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -// ES6 draft rev 32 (2015 Feb 2) 26.1.9 +// ES2017 draft rev a785b0832b071f505a694e1946182adeab84c972 +// 7.3.17 CreateListFromArrayLike (obj [ , elementTypes ] ) +function CreateListFromArrayLikeForArgs(obj) { + // Step 1 (not applicable). + + // Step 2. + assert(IsObject(obj), "object must be passed to CreateListFromArrayLikeForArgs"); + + // Step 3. + var len = ToLength(obj.length); + + // This version of CreateListFromArrayLike is only used for argument lists. + if (len > MAX_ARGS_LENGTH) + ThrowRangeError(JSMSG_TOO_MANY_ARGUMENTS); + + // Steps 4-6. + var list = std_Array(len); + for (var i = 0; i < len; i++) + _DefineDataProperty(list, i, obj[i]); + + // Step 7. + return list; +} + +// ES2017 draft rev a785b0832b071f505a694e1946182adeab84c972 +// 26.1.1 Reflect.apply ( target, thisArgument, argumentsList ) +function Reflect_apply(target, thisArgument, argumentsList) { + // Step 1. + if (!IsCallable(target)) + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, target)); + + // Step 2. + if (!IsObject(argumentsList)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(2, argumentsList)); + + // Steps 2-4. + return callFunction(std_Function_apply, target, thisArgument, argumentsList); +} + +// ES2017 draft rev a785b0832b071f505a694e1946182adeab84c972 +// 26.1.2 Reflect.construct ( target, argumentsList [ , newTarget ] ) +function Reflect_construct(target, argumentsList/*, newTarget*/) { + // Step 1. + if (!IsConstructor(target)) + ThrowTypeError(JSMSG_NOT_CONSTRUCTOR, DecompileArg(0, target)); + + // Steps 2-3. + var newTarget; + if (arguments.length > 2) { + newTarget = arguments[2]; + if (!IsConstructor(newTarget)) + ThrowTypeError(JSMSG_NOT_CONSTRUCTOR, DecompileArg(2, newTarget)); + } else { + newTarget = target; + } + + // Step 4. + if (!IsObject(argumentsList)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(1, argumentsList)); + + // Fast path when we can avoid calling CreateListFromArrayLikeForArgs(). + var args = (IsPackedArray(argumentsList) && argumentsList.length <= MAX_ARGS_LENGTH) + ? argumentsList + : CreateListFromArrayLikeForArgs(argumentsList); + + // Step 5. + switch (args.length) { + case 0: + return constructContentFunction(target, newTarget); + case 1: + return constructContentFunction(target, newTarget, SPREAD(args, 1)); + case 2: + return constructContentFunction(target, newTarget, SPREAD(args, 2)); + case 3: + return constructContentFunction(target, newTarget, SPREAD(args, 3)); + case 4: + return constructContentFunction(target, newTarget, SPREAD(args, 4)); + case 5: + return constructContentFunction(target, newTarget, SPREAD(args, 5)); + case 6: + return constructContentFunction(target, newTarget, SPREAD(args, 6)); + case 7: + return constructContentFunction(target, newTarget, SPREAD(args, 7)); + case 8: + return constructContentFunction(target, newTarget, SPREAD(args, 8)); + case 9: + return constructContentFunction(target, newTarget, SPREAD(args, 9)); + case 10: + return constructContentFunction(target, newTarget, SPREAD(args, 10)); + case 11: + return constructContentFunction(target, newTarget, SPREAD(args, 11)); + case 12: + return constructContentFunction(target, newTarget, SPREAD(args, 12)); + default: + return _ConstructFunction(target, newTarget, args); + } +} + +// ES2017 draft rev a785b0832b071f505a694e1946182adeab84c972 +// 26.1.8 Reflect.has ( target, propertyKey ) function Reflect_has(target, propertyKey) { // Step 1. if (!IsObject(target)) ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, target)); - // Steps 2-4 are identical to the runtime semantics of the "in" operator. + // Steps 2-3 are identical to the runtime semantics of the "in" operator. return propertyKey in target; } diff --git a/js/src/builtin/RegExp.js b/js/src/builtin/RegExp.js index 1833593a3299..b4d3fd0277d6 100644 --- a/js/src/builtin/RegExp.js +++ b/js/src/builtin/RegExp.js @@ -380,16 +380,13 @@ function RegExpGetComplexReplacement(result, matched, S, position, case 0: return ToString(replaceValue(matched, position, S)); case 1: - return ToString(replaceValue(matched, captures[0], position, S)); + return ToString(replaceValue(matched, SPREAD(captures, 1), position, S)); case 2: - return ToString(replaceValue(matched, captures[0], captures[1], - position, S)); + return ToString(replaceValue(matched, SPREAD(captures, 2), position, S)); case 3: - return ToString(replaceValue(matched, captures[0], captures[1], - captures[2], position, S)); + return ToString(replaceValue(matched, SPREAD(captures, 3), position, S)); case 4: - return ToString(replaceValue(matched, captures[0], captures[1], - captures[2], captures[3], position, S)); + return ToString(replaceValue(matched, SPREAD(captures, 4), position, S)); default: // Steps 14.j.ii-v. _DefineDataProperty(captures, capturesLength++, position); diff --git a/js/src/builtin/SelfHostingDefines.h b/js/src/builtin/SelfHostingDefines.h index cbc43fb652e6..51ad6a1fc4ec 100644 --- a/js/src/builtin/SelfHostingDefines.h +++ b/js/src/builtin/SelfHostingDefines.h @@ -19,6 +19,27 @@ #define FUN_APPLY(FUN, RECEIVER, ARGS) \ callFunction(std_Function_apply, FUN, RECEIVER, ARGS) +// NB: keep this in sync with the copy in vm/ArgumentsObject.h. +#define MAX_ARGS_LENGTH (500 * 1000) + +// Spread non-empty argument list of up to 15 elements. +#define SPREAD(v, n) SPREAD_##n(v) +#define SPREAD_1(v) v[0] +#define SPREAD_2(v) SPREAD_1(v), v[1] +#define SPREAD_3(v) SPREAD_2(v), v[2] +#define SPREAD_4(v) SPREAD_3(v), v[3] +#define SPREAD_5(v) SPREAD_4(v), v[4] +#define SPREAD_6(v) SPREAD_5(v), v[5] +#define SPREAD_7(v) SPREAD_6(v), v[6] +#define SPREAD_8(v) SPREAD_7(v), v[7] +#define SPREAD_9(v) SPREAD_8(v), v[8] +#define SPREAD_10(v) SPREAD_9(v), v[9] +#define SPREAD_11(v) SPREAD_10(v), v[10] +#define SPREAD_12(v) SPREAD_11(v), v[11] +#define SPREAD_13(v) SPREAD_12(v), v[12] +#define SPREAD_14(v) SPREAD_13(v), v[13] +#define SPREAD_15(v) SPREAD_14(v), v[14] + // Property descriptor attributes. #define ATTR_ENUMERABLE 0x01 #define ATTR_CONFIGURABLE 0x02 diff --git a/js/src/tests/ecma_6/Reflect/argumentsList.js b/js/src/tests/ecma_6/Reflect/argumentsList.js index cea2bb30ec85..621843519b28 100644 --- a/js/src/tests/ecma_6/Reflect/argumentsList.js +++ b/js/src/tests/ecma_6/Reflect/argumentsList.js @@ -159,5 +159,7 @@ function testMany(...args) { } assertEq(Reflect.apply(testMany, "pass", args), "pass"); assertEq(Reflect.construct(testMany, args) instanceof testMany, true); +assertEq(Reflect.apply(new Proxy(testMany, {}), "pass", args), "pass"); +assertEq(Reflect.construct(new Proxy(testMany, {}), args) instanceof testMany, true); reportCompare(0, 0); diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index 3a34e304e8aa..247c7cd94346 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -96,6 +96,7 @@ struct ArgumentsData // number of arguments that can be supplied to Function.prototype.apply. // This value also bounds the number of elements parsed in an array // initializer. +// NB: keep this in sync with the copy in builtin/SelfHostingDefines.h. static const unsigned ARGS_LENGTH_MAX = 500 * 1000; /* From 9e49f3a3ffb87a0709fca50436b4fa6856e9603a Mon Sep 17 00:00:00 2001 From: Stone Shih Date: Fri, 14 Oct 2016 09:38:49 +0800 Subject: [PATCH 24/80] Bug 1307388 - Fix intermittent poster-7.html reftest failed. r=smaug. MozReview-Commit-ID: 21tXbmbekRL --- layout/reftests/ogg-video/poster-4.html | 5 ++++- layout/reftests/ogg-video/poster-7.html | 5 ++++- layout/reftests/webm-video/poster-4.html | 5 ++++- layout/reftests/webm-video/poster-7.html | 5 ++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/layout/reftests/ogg-video/poster-4.html b/layout/reftests/ogg-video/poster-4.html index ac23726435e5..85453638c808 100644 --- a/layout/reftests/ogg-video/poster-4.html +++ b/layout/reftests/ogg-video/poster-4.html @@ -1,11 +1,14 @@ + onload="setTimeout(function(){document.documentElement.className = '';}, 0);"> + diff --git a/layout/reftests/ogg-video/poster-7.html b/layout/reftests/ogg-video/poster-7.html index 5008cf72481a..4c3e002d37c9 100644 --- a/layout/reftests/ogg-video/poster-7.html +++ b/layout/reftests/ogg-video/poster-7.html @@ -1,11 +1,14 @@ + onload="setTimeout(function(){document.documentElement.className = '';}, 0);"> + diff --git a/layout/reftests/webm-video/poster-4.html b/layout/reftests/webm-video/poster-4.html index 5ea6db0114f3..91ad6ea3bcaa 100644 --- a/layout/reftests/webm-video/poster-4.html +++ b/layout/reftests/webm-video/poster-4.html @@ -1,11 +1,14 @@ + onload="setTimeout(function(){document.documentElement.className = '';}, 0);"> + diff --git a/layout/reftests/webm-video/poster-7.html b/layout/reftests/webm-video/poster-7.html index eb4db0a1b819..769858cd6000 100644 --- a/layout/reftests/webm-video/poster-7.html +++ b/layout/reftests/webm-video/poster-7.html @@ -1,11 +1,14 @@ + onload="setTimeout(function(){document.documentElement.className = '';}, 0);"> + From 2151007a865b0634fac70c601e319162c6b935f0 Mon Sep 17 00:00:00 2001 From: Mark Smith Date: Mon, 17 Oct 2016 01:01:00 +0200 Subject: [PATCH 25/80] Bug 1308275 - make unix domain socket support spaces; r=bagder MozReview-Commit-ID: 7tE6VztAYp2 --- netwerk/socket/nsSOCKSIOLayer.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/netwerk/socket/nsSOCKSIOLayer.cpp b/netwerk/socket/nsSOCKSIOLayer.cpp index 859c9992cd9e..f339b67c26e6 100644 --- a/netwerk/socket/nsSOCKSIOLayer.cpp +++ b/netwerk/socket/nsSOCKSIOLayer.cpp @@ -20,6 +20,8 @@ #include "nsICancelable.h" #include "nsThreadUtils.h" #include "nsIURL.h" +#include "nsIFile.h" +#include "nsNetUtil.h" #include "mozilla/Logging.h" #include "mozilla/net/DNS.h" #include "mozilla/Unused.h" @@ -131,17 +133,15 @@ private: nsresult rv; MOZ_ASSERT(aProxyAddr); - nsCOMPtr url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv); + nsCOMPtr socketFile; + rv = NS_GetFileFromURLSpec(aDomainSocketPath, + getter_AddRefs(socketFile)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - if (NS_WARN_IF(NS_FAILED(rv = url->SetSpec(aDomainSocketPath)))) { - return rv; - } - nsAutoCString path; - if (NS_WARN_IF(NS_FAILED(rv = url->GetPath(path)))) { + if (NS_WARN_IF(NS_FAILED(rv = socketFile->GetNativePath(path)))) { return rv; } From 13e0da5f08e093f3498ca215da128f978313594c Mon Sep 17 00:00:00 2001 From: Adam Velebil Date: Mon, 10 Oct 2016 23:15:45 +0200 Subject: [PATCH 26/80] Bug 1308600 - Remove unused ServiceReady method from Geolocation. r=jdm --- dom/geolocation/nsGeolocation.cpp | 34 ++++--------------------------- dom/geolocation/nsGeolocation.h | 7 ------- 2 files changed, 4 insertions(+), 37 deletions(-) diff --git a/dom/geolocation/nsGeolocation.cpp b/dom/geolocation/nsGeolocation.cpp index ce0b55e468d9..30f4d51597e1 100644 --- a/dom/geolocation/nsGeolocation.cpp +++ b/dom/geolocation/nsGeolocation.cpp @@ -1247,14 +1247,8 @@ Geolocation::GetCurrentPosition(GeoPositionCallback callback, return NS_ERROR_FAILURE; } - return GetCurrentPositionReady(request); -} - -nsresult -Geolocation::GetCurrentPositionReady(nsGeolocationRequest* aRequest) -{ if (mOwner) { - if (!RegisterRequestWithPrompt(aRequest)) { + if (!RegisterRequestWithPrompt(request)) { return NS_ERROR_NOT_AVAILABLE; } @@ -1265,7 +1259,7 @@ Geolocation::GetCurrentPositionReady(nsGeolocationRequest* aRequest) return NS_ERROR_FAILURE; } - nsCOMPtr ev = new RequestAllowEvent(true, aRequest); + nsCOMPtr ev = new RequestAllowEvent(true, request); NS_DispatchToMainThread(ev); return NS_OK; @@ -1334,14 +1328,8 @@ Geolocation::WatchPosition(GeoPositionCallback aCallback, return NS_ERROR_FAILURE; } - return WatchPositionReady(request); -} - -nsresult -Geolocation::WatchPositionReady(nsGeolocationRequest* aRequest) -{ if (mOwner) { - if (!RegisterRequestWithPrompt(aRequest)) + if (!RegisterRequestWithPrompt(request)) return NS_ERROR_NOT_AVAILABLE; return NS_OK; @@ -1351,7 +1339,7 @@ Geolocation::WatchPositionReady(nsGeolocationRequest* aRequest) return NS_ERROR_FAILURE; } - aRequest->Allow(JS::UndefinedHandleValue); + request->Allow(JS::UndefinedHandleValue); return NS_OK; } @@ -1390,20 +1378,6 @@ Geolocation::ClearWatch(int32_t aWatchId) return NS_OK; } -void -Geolocation::ServiceReady() -{ - for (uint32_t length = mPendingRequests.Length(); length > 0; --length) { - if (mPendingRequests[0]->IsWatch()) { - WatchPositionReady(mPendingRequests[0]); - } else { - GetCurrentPositionReady(mPendingRequests[0]); - } - - mPendingRequests.RemoveElementAt(0); - } -} - bool Geolocation::WindowOwnerStillExists() { diff --git a/dom/geolocation/nsGeolocation.h b/dom/geolocation/nsGeolocation.h index f82c10cb3afc..259445910bf6 100644 --- a/dom/geolocation/nsGeolocation.h +++ b/dom/geolocation/nsGeolocation.h @@ -171,9 +171,6 @@ public: // Check to see if any active request requires high accuracy bool HighAccuracyRequested(); - // Notification from the service: - void ServiceReady(); - private: ~Geolocation(); @@ -187,10 +184,6 @@ private: bool RegisterRequestWithPrompt(nsGeolocationRequest* request); - // Methods for the service when it's ready to process requests: - nsresult GetCurrentPositionReady(nsGeolocationRequest* aRequest); - nsresult WatchPositionReady(nsGeolocationRequest* aRequest); - // Check if clearWatch is already called bool IsAlreadyCleared(nsGeolocationRequest* aRequest); From 4a53a5bca9e24db4c6ed048eac2c616a9d381447 Mon Sep 17 00:00:00 2001 From: djmdev Date: Thu, 13 Oct 2016 10:32:55 +0530 Subject: [PATCH 27/80] Bug 1305365 - disabled eyedropper for non-HTML documents and added a tooltip;r=jdescottes MozReview-Commit-ID: DMexSsQfjQf --- .../test/browser_inspector_highlighter-eyedropper-xul.js | 6 +++++- devtools/client/locales/en-US/inspector.properties | 4 ++++ devtools/client/shared/widgets/tooltip/HTMLTooltip.js | 1 + .../shared/widgets/tooltip/SwatchColorPickerTooltip.js | 9 ++++++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js b/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js index c0bc68938169..b5a207343038 100644 --- a/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js +++ b/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js @@ -30,9 +30,13 @@ add_task(function* () { yield onColorPickerReady; button = cPicker.tooltip.doc.querySelector("#eyedropper-button"); - ok(!isVisible(button), "The button is hidden in the color picker"); + ok(isDisabled(button), "The button is disabled in the color picker"); }); function isVisible(button) { return button.getBoxQuads().length !== 0; } + +function isDisabled(button) { + return button.disabled; +} diff --git a/devtools/client/locales/en-US/inspector.properties b/devtools/client/locales/en-US/inspector.properties index c9dde23f9f5e..4f48296787d7 100644 --- a/devtools/client/locales/en-US/inspector.properties +++ b/devtools/client/locales/en-US/inspector.properties @@ -41,6 +41,10 @@ markupView.whitespaceOnly=Whitespace-only text node: %S #LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded previewTooltip.image.brokenImage=Could not load the image +# LOCALIZATION NOTE: Used in color picker tooltip when the eyedropper is disabled for +# non-HTML documents +eyedropper.disabled.title=Unavailable in non-HTML documents + #LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded eventsTooltip.openInDebugger=Open in Debugger diff --git a/devtools/client/shared/widgets/tooltip/HTMLTooltip.js b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js index 0560179aee32..4ab790877f4c 100644 --- a/devtools/client/shared/widgets/tooltip/HTMLTooltip.js +++ b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js @@ -574,6 +574,7 @@ HTMLTooltip.prototype = { panel.setAttribute("noautofocus", true); panel.setAttribute("noautohide", true); panel.setAttribute("ignorekeys", true); + panel.setAttribute("tooltip", "aHTMLTooltip"); // Use type="arrow" to prevent side effects (see Bug 1285206) panel.setAttribute("type", "arrow"); diff --git a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js index 06821158063c..815e6c6c392d 100644 --- a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js +++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js @@ -8,6 +8,8 @@ const {Task} = require("devtools/shared/task"); const {colorUtils} = require("devtools/shared/css/color"); const {Spectrum} = require("devtools/client/shared/widgets/Spectrum"); const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip"); +const {LocalizationHelper} = require("devtools/shared/l10n"); +const L10N = new LocalizationHelper("devtools/locale/inspector.properties"); const Heritage = require("sdk/core/heritage"); @@ -56,6 +58,10 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr let eyedropper = doc.createElementNS(XHTML_NS, "button"); eyedropper.id = "eyedropper-button"; eyedropper.className = "devtools-button"; + /* pointerEvents for eyedropper has to be set auto to display tooltip when + * eyedropper is disabled in non-HTML documents. + */ + eyedropper.style.pointerEvents = "auto"; container.appendChild(eyedropper); this.tooltip.setContent(container, { width: 218, height: 224 }); @@ -96,7 +102,8 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr if (value && this.inspector.selection.nodeFront.isInHTMLDocument) { eyeButton.addEventListener("click", this._openEyeDropper); } else { - eyeButton.style.display = "none"; + eyeButton.disabled = true; + eyeButton.title = L10N.getStr("eyedropper.disabled.title"); } this.emit("ready"); }, e => console.error(e)); From fbd9b124bbec3084ba6442c1addd99b0f8a47a6d Mon Sep 17 00:00:00 2001 From: Sander Mathijs van Veen Date: Mon, 17 Oct 2016 02:49:00 +0200 Subject: [PATCH 28/80] Bug 1308647 - Mark testcase of bug1296249 as slow. r=h4writer --- js/src/jit-test/tests/basic/bug1296249.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/jit-test/tests/basic/bug1296249.js b/js/src/jit-test/tests/basic/bug1296249.js index 65e3674a7fc2..b9994fd4290e 100644 --- a/js/src/jit-test/tests/basic/bug1296249.js +++ b/js/src/jit-test/tests/basic/bug1296249.js @@ -1,3 +1,4 @@ +// |jit-test| slow if (!('oomTest' in this)) quit(); From 95701b6cbf24b2918efeb3a371fe24ddcf14999b Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 17 Oct 2016 20:33:00 +0200 Subject: [PATCH 29/80] Bug 1309829 - Remove redundant IdentifyTextureHost call. r=dvander --- widget/nsBaseWidget.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index ce177b965a6b..16bc1dee26a3 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -352,9 +352,6 @@ nsBaseWidget::OnRenderingDeviceReset() // Update the texture factory identifier. clm->UpdateTextureFactoryIdentifier(identifier); - if (ShadowLayerForwarder* lf = clm->AsShadowForwarder()) { - lf->IdentifyTextureHost(identifier); - } ImageBridgeChild::IdentifyCompositorTextureHost(identifier); gfx::VRManagerChild::IdentifyTextureHost(identifier); } From 4e97bb7842880cae230df0fab2216af543f2afa8 Mon Sep 17 00:00:00 2001 From: Jan Odvarko Date: Mon, 17 Oct 2016 15:58:49 +0200 Subject: [PATCH 30/80] Bug 1308742 - Preserve line breaks in String rep; r=linclark --- devtools/client/shared/components/reps/string.js | 7 ++++--- .../shared/components/test/mochitest/test_reps_string.html | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/devtools/client/shared/components/reps/string.js b/devtools/client/shared/components/reps/string.js index beb15b127367..175c1cafa905 100644 --- a/devtools/client/shared/components/reps/string.js +++ b/devtools/client/shared/components/reps/string.js @@ -10,7 +10,7 @@ define(function (require, exports, module) { // Dependencies const React = require("devtools/client/shared/vendor/react"); - const { cropMultipleLines } = require("./rep-utils"); + const { cropString } = require("./rep-utils"); // Shortcuts const { span } = React.DOM; @@ -43,13 +43,14 @@ define(function (require, exports, module) { } let croppedString = this.props.cropLimit ? - cropMultipleLines(text, this.props.cropLimit) : cropMultipleLines(text); + cropString(text, this.props.cropLimit) : cropString(text); let formattedString = this.props.useQuotes ? "\"" + croppedString + "\"" : croppedString; return ( - span({className: "objectBox objectBox-string"}, formattedString + span({className: "objectBox objectBox-string"}, + formattedString ) ); }, diff --git a/devtools/client/shared/components/test/mochitest/test_reps_string.html b/devtools/client/shared/components/test/mochitest/test_reps_string.html index 4560c83aa438..19430f9eabd6 100644 --- a/devtools/client/shared/components/test/mochitest/test_reps_string.html +++ b/devtools/client/shared/components/test/mochitest/test_reps_string.html @@ -36,12 +36,12 @@ window.onload = Task.async(function* () { function testMultiline() { const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testMultiline") }); - is(renderedComponent.textContent, "\"aaaaaaaaaaaaaaaaaaaaa\\nbbbbbbbbbbbbbbbbbbb\\ncccccccccccccccc\\n\"", "String rep has expected text content for multiline string"); + is(renderedComponent.textContent, "\"aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n\"", "String rep has expected text content for multiline string"); } function testMultilineLimit() { const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testMultiline"), cropLimit: 20 }); - is(renderedComponent.textContent, "\"aaaaaaaaaa…cccccccc\\n\"", "String rep has expected text content for multiline string with specified number of characters"); + is(renderedComponent.textContent, "\"aaaaaaaaaa…cccccccc\n\"", "String rep has expected text content for multiline string with specified number of characters"); } function testMultilineOpen() { From 7df44c80a08ae0da51537ca216e3c9b91a25feea Mon Sep 17 00:00:00 2001 From: Jan Odvarko Date: Mon, 17 Oct 2016 18:11:40 +0200 Subject: [PATCH 31/80] Bug 1309243 - Use unique key property; r=nchevobbe --- .../net/components/net-info-params.js | 8 +++--- .../net/test/mochitest/browser_net_params.js | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/devtools/client/webconsole/net/components/net-info-params.js b/devtools/client/webconsole/net/components/net-info-params.js index de5e992a81ad..573257b281ee 100644 --- a/devtools/client/webconsole/net/components/net-info-params.js +++ b/devtools/client/webconsole/net/components/net-info-params.js @@ -14,6 +14,8 @@ const PropTypes = React.PropTypes; * It's essentially a list of name + value pairs. */ var NetInfoParams = React.createClass({ + displayName: "NetInfoParams", + propTypes: { params: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string.isRequired, @@ -21,8 +23,6 @@ var NetInfoParams = React.createClass({ })).isRequired, }, - displayName: "NetInfoParams", - render() { let params = this.props.params || []; @@ -31,9 +31,9 @@ var NetInfoParams = React.createClass({ }); let rows = []; - params.forEach(param => { + params.forEach((param, index) => { rows.push( - DOM.tr({key: param.name}, + DOM.tr({key: index}, DOM.td({className: "netInfoParamName"}, DOM.span({title: param.name}, param.name) ), diff --git a/devtools/client/webconsole/net/test/mochitest/browser_net_params.js b/devtools/client/webconsole/net/test/mochitest/browser_net_params.js index d0a0d6b865d1..d8b0e2c84bf1 100644 --- a/devtools/client/webconsole/net/test/mochitest/browser_net_params.js +++ b/devtools/client/webconsole/net/test/mochitest/browser_net_params.js @@ -40,3 +40,30 @@ add_task(function* () { is(paramValue.textContent, "bar", "The param value must have proper value"); }); + +/** + * Test URL parameters with the same name. + */ +add_task(function* () { + info("Test XHR Spy params started"); + + let {hud} = yield addTestTab(TEST_PAGE_URL); + + let netInfoBody = yield executeAndInspectXhr(hud, { + method: "GET", + url: JSON_XHR_URL, + queryString: "?box[]=123&box[]=456" + }); + + // Check headers + let tabBody = yield selectNetInfoTab(hud, netInfoBody, "params"); + + let params = tabBody.querySelectorAll( + ".netInfoParamName > span[title='box[]']"); + is(params.length, 2, "Two URI parameters must exist"); + + let values = tabBody.querySelectorAll( + ".netInfoParamValue > code"); + is(values[0].textContent, 123, "First value must match"); + is(values[1].textContent, 456, "Second value must match"); +}); From ac643f0834ead1c429157a38fca2f81d41cda6fc Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:45:57 +0900 Subject: [PATCH 32/80] Bug 1290337 - Part 21: Use const reference in BarrierMethods::exposeToJS. r=sfink --- js/public/Value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/public/Value.h b/js/public/Value.h index cc4879110887..0b2ef2221ea2 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -1739,7 +1739,7 @@ struct BarrierMethods static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) { JS::HeapValuePostBarrier(v, prev, next); } - static void exposeToJS(JS::Value v) { + static void exposeToJS(const JS::Value& v) { JS::ExposeValueToActiveJS(v); } }; From 19d7f7bb03f3cc7290a2680dce89792de523091b Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Sun, 11 Sep 2016 18:23:11 +0900 Subject: [PATCH 33/80] Bug 1290337 - Part 22: Add moz_non_param annotation. r=ehsan,jwalden --- build/clang-plugin/clang-plugin.cpp | 66 +++++++ .../tests/NonParameterTestCases.h | 61 ++++++ .../tests/TestNonParameterChecker.cpp | 179 ++++++++++++++++++ build/clang-plugin/tests/moz.build | 1 + mfbt/Attributes.h | 5 + 5 files changed, 312 insertions(+) create mode 100644 build/clang-plugin/tests/NonParameterTestCases.h create mode 100644 build/clang-plugin/tests/TestNonParameterChecker.cpp diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp index 279f827b9eea..85b1c65ad243 100644 --- a/build/clang-plugin/clang-plugin.cpp +++ b/build/clang-plugin/clang-plugin.cpp @@ -199,6 +199,11 @@ private: virtual void run(const MatchFinder::MatchResult &Result); }; + class NonParamInsideFunctionDeclChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + ScopeChecker Scope; ArithmeticArgChecker ArithmeticArg; TrivialCtorDtorChecker TrivialCtorDtor; @@ -219,6 +224,7 @@ private: SprintfLiteralChecker SprintfLiteral; OverrideBaseCallChecker OverrideBaseCall; OverrideBaseCallUsageChecker OverrideBaseCallUsage; + NonParamInsideFunctionDeclChecker NonParamInsideFunctionDecl; MatchFinder AstMatcher; }; @@ -496,6 +502,8 @@ static CustomTypeAnnotation NonTemporaryClass = CustomTypeAnnotation("moz_non_temporary_class", "non-temporary"); static CustomTypeAnnotation MustUse = CustomTypeAnnotation("moz_must_use_type", "must-use"); +static CustomTypeAnnotation NonParam = + CustomTypeAnnotation("moz_non_param", "non-param"); class MemMoveAnnotation final : public CustomTypeAnnotation { public: @@ -1329,6 +1337,17 @@ DiagnosticsMatcher::DiagnosticsMatcher() { AstMatcher.addMatcher( cxxMethodDecl(isNonVirtual(), isRequiredBaseMethod()).bind("method"), &OverrideBaseCallUsage); + + AstMatcher.addMatcher( + functionDecl(anyOf(allOf(isDefinition(), + hasAncestor(classTemplateSpecializationDecl() + .bind("spec"))), + isDefinition())) + .bind("func"), + &NonParamInsideFunctionDecl); + AstMatcher.addMatcher( + lambdaExpr().bind("lambda"), + &NonParamInsideFunctionDecl); } // These enum variants determine whether an allocation has occured in the code. @@ -2133,6 +2152,53 @@ void DiagnosticsMatcher::OverrideBaseCallUsageChecker::run( Diag.Report(Method->getLocation(), ErrorID); } +void DiagnosticsMatcher::NonParamInsideFunctionDeclChecker::run( + const MatchFinder::MatchResult &Result) { + static DenseSet CheckedFunctionDecls; + + const FunctionDecl *func = Result.Nodes.getNodeAs("func"); + if (!func) { + const LambdaExpr *lambda = Result.Nodes.getNodeAs("lambda"); + if (lambda) { + func = lambda->getCallOperator(); + } + } + + if (!func) { + return; + } + + // Don't report errors on the same declarations more than once. + if (CheckedFunctionDecls.count(func)) { + return; + } + CheckedFunctionDecls.insert(func); + + const ClassTemplateSpecializationDecl *Spec = + Result.Nodes.getNodeAs("spec"); + + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Type %0 must not be used as parameter"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "Please consider passing a const reference instead"); + unsigned SpecNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "The bad argument was passed to %0 here"); + + for (ParmVarDecl *p : func->parameters()) { + QualType T = p->getType().withoutLocalFastQualifiers(); + if (NonParam.hasEffectiveAnnotation(T)) { + Diag.Report(p->getLocation(), ErrorID) << T; + Diag.Report(p->getLocation(), NoteID); + + if (Spec) { + Diag.Report(Spec->getPointOfInstantiation(), SpecNoteID) + << Spec->getSpecializedTemplate(); + } + } + } +} + class MozCheckAction : public PluginASTAction { public: ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, diff --git a/build/clang-plugin/tests/NonParameterTestCases.h b/build/clang-plugin/tests/NonParameterTestCases.h new file mode 100644 index 000000000000..d38a14d9447c --- /dev/null +++ b/build/clang-plugin/tests/NonParameterTestCases.h @@ -0,0 +1,61 @@ +MAYBE_STATIC void raw(Param x) {} + +MAYBE_STATIC void raw(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamUnion x) {} //expected-error {{Type 'NonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamClass x) {} //expected-error {{Type 'NonParamClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamEnum x) {} //expected-error {{Type 'NonParamEnum' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamEnumClass x) {} //expected-error {{Type 'NonParamEnumClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +MAYBE_STATIC void const_(const NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamUnion x) {} //expected-error {{Type 'NonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamClass x) {} //expected-error {{Type 'NonParamClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamEnum x) {} //expected-error {{Type 'NonParamEnum' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamEnumClass x) {} //expected-error {{Type 'NonParamEnumClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +MAYBE_STATIC void array(NonParam x[]) {} +MAYBE_STATIC void array(NonParamUnion x[]) {} +MAYBE_STATIC void array(NonParamClass x[]) {} +MAYBE_STATIC void array(NonParamEnum x[]) {} +MAYBE_STATIC void array(NonParamEnumClass x[]) {} +MAYBE_STATIC void array(HasNonParamStruct x[]) {} +MAYBE_STATIC void array(HasNonParamUnion x[]) {} +MAYBE_STATIC void array(HasNonParamStructUnion x[]) {} + +MAYBE_STATIC void ptr(NonParam* x) {} +MAYBE_STATIC void ptr(NonParamUnion* x) {} +MAYBE_STATIC void ptr(NonParamClass* x) {} +MAYBE_STATIC void ptr(NonParamEnum* x) {} +MAYBE_STATIC void ptr(NonParamEnumClass* x) {} +MAYBE_STATIC void ptr(HasNonParamStruct* x) {} +MAYBE_STATIC void ptr(HasNonParamUnion* x) {} +MAYBE_STATIC void ptr(HasNonParamStructUnion* x) {} + +MAYBE_STATIC void ref(NonParam& x) {} +MAYBE_STATIC void ref(NonParamUnion& x) {} +MAYBE_STATIC void ref(NonParamClass& x) {} +MAYBE_STATIC void ref(NonParamEnum& x) {} +MAYBE_STATIC void ref(NonParamEnumClass& x) {} +MAYBE_STATIC void ref(HasNonParamStruct& x) {} +MAYBE_STATIC void ref(HasNonParamUnion& x) {} +MAYBE_STATIC void ref(HasNonParamStructUnion& x) {} + +MAYBE_STATIC void constRef(const NonParam& x) {} +MAYBE_STATIC void constRef(const NonParamUnion& x) {} +MAYBE_STATIC void constRef(const NonParamClass& x) {} +MAYBE_STATIC void constRef(const NonParamEnum& x) {} +MAYBE_STATIC void constRef(const NonParamEnumClass& x) {} +MAYBE_STATIC void constRef(const HasNonParamStruct& x) {} +MAYBE_STATIC void constRef(const HasNonParamUnion& x) {} +MAYBE_STATIC void constRef(const HasNonParamStructUnion& x) {} + +MAYBE_STATIC inline void inlineRaw(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamUnion x) {} //expected-error {{Type 'NonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamClass x) {} //expected-error {{Type 'NonParamClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamEnum x) {} //expected-error {{Type 'NonParamEnum' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamEnumClass x) {} //expected-error {{Type 'NonParamEnumClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} diff --git a/build/clang-plugin/tests/TestNonParameterChecker.cpp b/build/clang-plugin/tests/TestNonParameterChecker.cpp new file mode 100644 index 000000000000..87ff89238731 --- /dev/null +++ b/build/clang-plugin/tests/TestNonParameterChecker.cpp @@ -0,0 +1,179 @@ +#define MOZ_NON_PARAM __attribute__((annotate("moz_non_param"))) + +struct Param {}; +struct MOZ_NON_PARAM NonParam {}; +union MOZ_NON_PARAM NonParamUnion {}; +class MOZ_NON_PARAM NonParamClass {}; +enum MOZ_NON_PARAM NonParamEnum { X, Y, Z }; +enum class MOZ_NON_PARAM NonParamEnumClass { X, Y, Z }; + +struct HasNonParamStruct { NonParam x; int y; }; +union HasNonParamUnion { NonParam x; int y; }; +struct HasNonParamStructUnion { HasNonParamUnion z; }; + +#define MAYBE_STATIC +#include "NonParameterTestCases.h" +#undef MAYBE_STATIC + +// Do not check typedef and using. +typedef void (*funcTypeParam)(Param x); +typedef void (*funcTypeNonParam)(NonParam x); + +using usingFuncTypeParam = void (*)(Param x); +using usingFuncTypeNonParam = void (*)(NonParam x); + +class class_ +{ + explicit class_(Param x) {} + explicit class_(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + explicit class_(HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + explicit class_(HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + explicit class_(HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +#define MAYBE_STATIC +#include "NonParameterTestCases.h" +#undef MAYBE_STATIC +}; + +class classWithStatic +{ +#define MAYBE_STATIC static +#include "NonParameterTestCases.h" +#undef MAYBE_STATIC +}; + +template +class tmplClassForParam +{ +public: + void raw(T x) {} + void rawDefault(T x = T()) {} + void const_(const T x) {} + void ptr(T* x) {} + void ref(T& x) {} + void constRef(const T& x) {} + + void notCalled(T x) {} +}; + +template +class tmplClassForNonParam +{ +public: + void raw(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void rawDefault(T x = T()) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void const_(const T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void ptr(T* x) {} + void ref(T& x) {} + void constRef(const T& x) {} + + void notCalled(T x) {} +}; + +template +class tmplClassForHasNonParamStruct +{ +public: + void raw(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void rawDefault(T x = T()) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void const_(const T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void ptr(T* x) {} + void ref(T& x) {} + void constRef(const T& x) {} + + void notCalled(T x) {} +}; + +void testTemplateClass() +{ + tmplClassForParam paramClass; + Param param; + paramClass.raw(param); + paramClass.rawDefault(); + paramClass.const_(param); + paramClass.ptr(¶m); + paramClass.ref(param); + paramClass.constRef(param); + + tmplClassForNonParam nonParamClass; //expected-note 3 {{The bad argument was passed to 'tmplClassForNonParam' here}} + NonParam nonParam; + nonParamClass.raw(nonParam); + nonParamClass.rawDefault(); + nonParamClass.const_(nonParam); + nonParamClass.ptr(&nonParam); + nonParamClass.ref(nonParam); + nonParamClass.constRef(nonParam); + + tmplClassForHasNonParamStruct hasNonParamStructClass;//expected-note 3 {{The bad argument was passed to 'tmplClassForHasNonParamStruct' here}} + HasNonParamStruct hasNonParamStruct; + hasNonParamStructClass.raw(hasNonParamStruct); + hasNonParamStructClass.rawDefault(); + hasNonParamStructClass.const_(hasNonParamStruct); + hasNonParamStructClass.ptr(&hasNonParamStruct); + hasNonParamStructClass.ref(hasNonParamStruct); + hasNonParamStructClass.constRef(hasNonParamStruct); +} + +template +class NestedTemplateInner +{ +public: + void raw(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +}; + +template +class nestedTemplateOuter +{ +public: + void constRef(const T& x) { + NestedTemplateInner inner; //expected-note {{The bad argument was passed to 'NestedTemplateInner' here}} + inner.raw(x); + } +}; + +void testNestedTemplateClass() +{ + nestedTemplateOuter outer; + NonParam nonParam; + outer.constRef(nonParam); // FIXME: this line needs note "The bad argument was passed to 'constRef' here" +} + +template +void tmplFuncForParam(T x) {} +template +void tmplFuncForNonParam(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +template +void tmplFuncForNonParamImplicit(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +template +void tmplFuncForHasNonParamStruct(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +template +void tmplFuncForHasNonParamStructImplicit(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +void testTemplateFunc() +{ + Param param; + tmplFuncForParam(param); + + NonParam nonParam; + tmplFuncForNonParam(nonParam); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForNonParam' here" + tmplFuncForNonParamImplicit(nonParam); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForNonParamImplicit' here" + + HasNonParamStruct hasNonParamStruct; + tmplFuncForHasNonParamStruct(hasNonParamStruct); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForHasNonParamStruct' here" + tmplFuncForHasNonParamStructImplicit(hasNonParamStruct); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForHasNonParamStructImplicit' here" +} + +void testLambda() +{ + auto paramLambda = [](Param x) -> void {}; + auto nonParamLambda = [](NonParam x) -> void {}; //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + auto nonParamStructLambda = [](HasNonParamStruct x) -> void {}; //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + auto nonParamUnionLambda = [](HasNonParamUnion x) -> void {}; //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + auto nonParamStructUnionLambda = [](HasNonParamStructUnion x) -> void {}; //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + + (void)[](Param x) -> void {}; + (void)[](NonParam x) -> void {}; //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + (void)[](HasNonParamStruct x) -> void {}; //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + (void)[](HasNonParamUnion x) -> void {}; //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + (void)[](HasNonParamStructUnion x) -> void {}; //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +} diff --git a/build/clang-plugin/tests/moz.build b/build/clang-plugin/tests/moz.build index 1746c2fbc517..3f7bdcba18a7 100644 --- a/build/clang-plugin/tests/moz.build +++ b/build/clang-plugin/tests/moz.build @@ -30,6 +30,7 @@ SOURCES += [ 'TestNonHeapClass.cpp', 'TestNonMemMovable.cpp', 'TestNonMemMovableStd.cpp', + 'TestNonParameterChecker.cpp', 'TestNonTemporaryClass.cpp', 'TestNoRefcountedInsideLambdas.cpp', 'TestOverrideBaseCall.cpp', diff --git a/mfbt/Attributes.h b/mfbt/Attributes.h index 23e77e5e0375..f7f8a0c39291 100644 --- a/mfbt/Attributes.h +++ b/mfbt/Attributes.h @@ -464,6 +464,8 @@ * tool aware that the marked function is part of the initialization process * and to include the marked function in the scan mechanism that determines witch * member variables still remain uninitialized. + * MOZ_NON_PARAM: Applies to types. Makes it compile time error to use the type + * in parameter without pointer or reference. * MOZ_NON_AUTOABLE: Applies to class declarations. Makes it a compile time error to * use `auto` in place of this type in variable declarations. This is intended to * be used with types which are intended to be implicitly constructed into other @@ -505,6 +507,8 @@ __attribute__((annotate("moz_ignore_ctor_initialization"))) # define MOZ_IS_CLASS_INIT \ __attribute__((annotate("moz_is_class_init"))) +# define MOZ_NON_PARAM \ + __attribute__((annotate("moz_non_param"))) # define MOZ_REQUIRED_BASE_METHOD \ __attribute__((annotate("moz_required_base_method"))) /* @@ -540,6 +544,7 @@ # define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS /* nothing */ # define MOZ_INIT_OUTSIDE_CTOR /* nothing */ # define MOZ_IS_CLASS_INIT /* nothing */ +# define MOZ_NON_PARAM /* nothing */ # define MOZ_NON_AUTOABLE /* nothing */ # define MOZ_REQUIRED_BASE_METHOD /* nothing */ #endif /* MOZ_CLANG_PLUGIN */ From 6a21d20cc65122e90c56e9ee146daaec46da53d8 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:45:58 +0900 Subject: [PATCH 34/80] Bug 1290337 - Part 22.1: Do not check deleted function parameter. r=ehsan --- build/clang-plugin/clang-plugin.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp index 85b1c65ad243..b3ec1ece1509 100644 --- a/build/clang-plugin/clang-plugin.cpp +++ b/build/clang-plugin/clang-plugin.cpp @@ -2168,6 +2168,10 @@ void DiagnosticsMatcher::NonParamInsideFunctionDeclChecker::run( return; } + if (func->isDeleted()) { + return; + } + // Don't report errors on the same declarations more than once. if (CheckedFunctionDecls.count(func)) { return; From 7ee22eddfba0d061ed1f5a5f9274ffe155ae525a Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Sun, 11 Sep 2016 18:23:11 +0900 Subject: [PATCH 35/80] Bug 1290337 - Part 23: Use alignas and MOZ_NON_PARAM attribute for jsval_layout. r=jwalden --- js/public/Value.h | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 0b2ef2221ea2..76c8c54244e5 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -29,24 +29,6 @@ namespace JS { class Value; } #define JSVAL_INT_MIN ((int32_t)0x80000000) #define JSVAL_INT_MAX ((int32_t)0x7fffffff) -/* - * Try to get jsvals 64-bit aligned. We could almost assert that all values are - * aligned, but MSVC and GCC occasionally break alignment. - */ -#if defined(__GNUC__) || defined(__xlc__) || defined(__xlC__) -# define JSVAL_ALIGNMENT __attribute__((aligned (8))) -#elif defined(_MSC_VER) - /* - * Structs can be aligned with MSVC, but not if they are used as parameters, - * so we just don't try to align. - */ -# define JSVAL_ALIGNMENT -#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) -# define JSVAL_ALIGNMENT -#elif defined(__HP_cc) || defined(__HP_aCC) -# define JSVAL_ALIGNMENT -#endif - #if defined(JS_PUNBOX64) # define JSVAL_TAG_SHIFT 47 #endif @@ -239,7 +221,7 @@ typedef enum JSWhyMagic #if MOZ_LITTLE_ENDIAN # if defined(JS_NUNBOX32) -typedef union jsval_layout +union MOZ_NON_PARAM alignas(8) jsval_layout { uint64_t asBits; struct { @@ -260,9 +242,9 @@ typedef union jsval_layout } s; double asDouble; void* asPtr; -} JSVAL_ALIGNMENT jsval_layout; +}; # elif defined(JS_PUNBOX64) -typedef union jsval_layout +union MOZ_NON_PARAM alignas(8) jsval_layout { uint64_t asBits; #if !defined(_WIN64) @@ -283,11 +265,11 @@ typedef union jsval_layout void* asPtr; size_t asWord; uintptr_t asUIntPtr; -} JSVAL_ALIGNMENT jsval_layout; +}; # endif /* JS_PUNBOX64 */ #else /* MOZ_LITTLE_ENDIAN */ # if defined(JS_NUNBOX32) -typedef union jsval_layout +union MOZ_NON_PARAM alignas(8) jsval_layout { uint64_t asBits; struct { @@ -308,9 +290,9 @@ typedef union jsval_layout } s; double asDouble; void* asPtr; -} JSVAL_ALIGNMENT jsval_layout; +}; # elif defined(JS_PUNBOX64) -typedef union jsval_layout +union MOZ_NON_PARAM alignas(8) jsval_layout { uint64_t asBits; struct { @@ -329,7 +311,7 @@ typedef union jsval_layout void* asPtr; size_t asWord; uintptr_t asUIntPtr; -} JSVAL_ALIGNMENT jsval_layout; +}; # endif /* JS_PUNBOX64 */ #endif /* MOZ_LITTLE_ENDIAN */ From f2e075bfa244ff321fbc93d1494e4391c67a049d Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Sun, 11 Sep 2016 18:23:11 +0900 Subject: [PATCH 36/80] Bug 1290337 - Part 24: Replace JS_ALIGNMENT_OF with alignof. r=jwalden --- js/src/jsscript.cpp | 55 ++++++++++++++++++++++++++------------------- js/src/jsutil.h | 11 --------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index d542033737dc..eb0982fdc1b1 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -2327,16 +2327,25 @@ js::FreeScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock) * The following static assertions check JSScript::data's alignment properties. */ -#define KEEPS_JSVAL_ALIGNMENT(T) \ - (JS_ALIGNMENT_OF(JS::Value) % JS_ALIGNMENT_OF(T) == 0 && \ - sizeof(T) % sizeof(JS::Value) == 0) +template +constexpr bool +KeepsValueAlignment() { + return alignof(JS::Value) % alignof(T) == 0 && + sizeof(T) % sizeof(JS::Value) == 0; +} -#define HAS_JSVAL_ALIGNMENT(T) \ - (JS_ALIGNMENT_OF(JS::Value) == JS_ALIGNMENT_OF(T) && \ - sizeof(T) == sizeof(JS::Value)) +template +constexpr bool +HasValueAlignment() { + return alignof(JS::Value) == alignof(T) && + sizeof(T) == sizeof(JS::Value); +} -#define NO_PADDING_BETWEEN_ENTRIES(T1, T2) \ - (JS_ALIGNMENT_OF(T1) % JS_ALIGNMENT_OF(T2) == 0) +template +constexpr bool +NoPaddingBetweenEntries() { + return alignof(T1) % alignof(T2) == 0; +} /* * These assertions ensure that there is no padding between the array headers, @@ -2344,24 +2353,24 @@ js::FreeScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock) * Value-aligned. (There is an assumption that |data| itself is Value-aligned; * we check this below). */ -JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ConstArray)); -JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ObjectArray)); /* there are two of these */ -JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(TryNoteArray)); -JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ScopeNoteArray)); +JS_STATIC_ASSERT(KeepsValueAlignment()); +JS_STATIC_ASSERT(KeepsValueAlignment()); /* there are two of these */ +JS_STATIC_ASSERT(KeepsValueAlignment()); +JS_STATIC_ASSERT(KeepsValueAlignment()); /* These assertions ensure there is no padding required between array elements. */ -JS_STATIC_ASSERT(HAS_JSVAL_ALIGNMENT(GCPtrValue)); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(GCPtrValue, GCPtrObject)); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(GCPtrObject, GCPtrObject)); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(GCPtrObject, JSTryNote)); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, uint32_t)); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, uint32_t)); +JS_STATIC_ASSERT(HasValueAlignment()); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(GCPtrValue, ScopeNote)); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(ScopeNote, ScopeNote)); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, ScopeNote)); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(GCPtrObject, ScopeNote)); -JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(ScopeNote, uint32_t)); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); +JS_STATIC_ASSERT((NoPaddingBetweenEntries())); static inline size_t ScriptDataSize(uint32_t nscopes, uint32_t nconsts, uint32_t nobjects, diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 28fcb51c90fd..7d846f506aa2 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -47,17 +47,6 @@ js_memcpy(void* dst_, const void* src_, size_t len) namespace js { -template -struct AlignmentTestStruct -{ - char c; - T t; -}; - -/* This macro determines the alignment requirements of a type. */ -#define JS_ALIGNMENT_OF(t_) \ - (sizeof(js::AlignmentTestStruct) - sizeof(t_)) - template class AlignedPtrAndFlag { From a60c24ed6c0be68ef7d0edad537ab8ba9bad0905 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:45:59 +0900 Subject: [PATCH 37/80] Bug 1290337 - Part 25: Use HandleValue in ToNumber and ToNumberSlow. r=shu --- js/public/Conversions.h | 2 +- js/src/jsnum.cpp | 4 ++-- js/src/jsnum.h | 8 ++++---- js/src/vm/TypedArrayCommon.h | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/js/public/Conversions.h b/js/public/Conversions.h index cceceae1c26d..1cee31c561ae 100644 --- a/js/public/Conversions.h +++ b/js/public/Conversions.h @@ -30,7 +30,7 @@ ToBooleanSlow(JS::HandleValue v); /* DO NOT CALL THIS. Use JS::ToNumber. */ extern JS_PUBLIC_API(bool) -ToNumberSlow(JSContext* cx, const JS::Value& v, double* dp); +ToNumberSlow(JSContext* cx, JS::HandleValue v, double* dp); /* DO NOT CALL THIS. Use JS::ToInt8. */ extern JS_PUBLIC_API(bool) diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 23d2cfa7e3b7..f2dbda350be9 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -1574,7 +1574,7 @@ js::StringToNumber(ExclusiveContext* cx, JSString* str, double* result) } bool -js::ToNumberSlow(ExclusiveContext* cx, const Value& v_, double* out) +js::ToNumberSlow(ExclusiveContext* cx, HandleValue v_, double* out) { RootedValue v(cx, v_); MOZ_ASSERT(!v.isNumber()); @@ -1624,7 +1624,7 @@ js::ToNumberSlow(ExclusiveContext* cx, const Value& v_, double* out) } JS_PUBLIC_API(bool) -js::ToNumberSlow(JSContext* cx, const Value& v, double* out) +js::ToNumberSlow(JSContext* cx, HandleValue v, double* out) { return ToNumberSlow(static_cast(cx), v, out); } diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 5bc4edea986d..62b3d617f141 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -173,7 +173,7 @@ ToNumber(JSContext* cx, JS::MutableHandleValue vp) if (vp.isNumber()) return true; double d; - extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, const Value& v, double* dp); + extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, HandleValue v, double* dp); if (!ToNumberSlow(cx, vp, &d)) return false; @@ -258,7 +258,7 @@ ToInteger(JSContext* cx, HandleValue v, double* dp) if (v.isDouble()) { *dp = v.toDouble(); } else { - extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, const Value& v, double* dp); + extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, HandleValue v, double* dp); if (!ToNumberSlow(cx, v, dp)) return false; } @@ -335,12 +335,12 @@ SafeMul(int32_t one, int32_t two, int32_t* res) } extern MOZ_MUST_USE bool -ToNumberSlow(ExclusiveContext* cx, const Value& v, double* dp); +ToNumberSlow(ExclusiveContext* cx, HandleValue v, double* dp); // Variant of ToNumber which takes an ExclusiveContext instead of a JSContext. // ToNumber is part of the API and can't use ExclusiveContext directly. MOZ_ALWAYS_INLINE MOZ_MUST_USE bool -ToNumber(ExclusiveContext* cx, const Value& v, double* out) +ToNumber(ExclusiveContext* cx, HandleValue v, double* out) { if (v.isNumber()) { *out = v.toNumber(); diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index b4e595c200e7..56afbed5277d 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -540,7 +540,7 @@ class ElementSpecific } static bool - valueToNative(JSContext* cx, const Value& v, T* result) + valueToNative(JSContext* cx, HandleValue v, T* result) { MOZ_ASSERT(!v.isMagic()); From 20e4f390f1ff3817a2235c75157267085f1e999e Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:45:59 +0900 Subject: [PATCH 38/80] Bug 1290337 - Part 26: Use HandleValue in ToPropertyKey. r=shu --- js/src/jsobjinlines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 1aae724bad34..6be4d0d288a2 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -620,7 +620,7 @@ HasObjectValueOf(JSObject* obj, JSContext* cx) /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */ inline bool -ToPropertyKey(JSContext* cx, const Value& argument, MutableHandleId result) +ToPropertyKey(JSContext* cx, HandleValue argument, MutableHandleId result) { // Steps 1-2. RootedValue key(cx, argument); From f1477f767173b9d2a34d99e0c896e64ceae2c8a4 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:45:59 +0900 Subject: [PATCH 39/80] Bug 1290337 - Part 27: Pass CallArgs& to GetScriptAndPCArgs. r=terrence --- js/src/shell/js.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 08baa0906908..b53af7be407e 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -2305,13 +2305,13 @@ GetTopScript(JSContext* cx) } static bool -GetScriptAndPCArgs(JSContext* cx, unsigned argc, Value* argv, MutableHandleScript scriptp, +GetScriptAndPCArgs(JSContext* cx, CallArgs& args, MutableHandleScript scriptp, int32_t* ip) { RootedScript script(cx, GetTopScript(cx)); *ip = 0; - if (argc != 0) { - RootedValue v(cx, argv[0]); + if (!args.get(0).isUndefined()) { + HandleValue v = args[0]; unsigned intarg = 0; if (v.isObject() && JS_GetClass(&v.toObject()) == Jsvalify(&JSFunction::class_)) { @@ -2320,8 +2320,8 @@ GetScriptAndPCArgs(JSContext* cx, unsigned argc, Value* argv, MutableHandleScrip return false; intarg++; } - if (argc > intarg) { - if (!JS::ToInt32(cx, HandleValue::fromMarkedLocation(&argv[intarg]), ip)) + if (!args.get(intarg).isUndefined()) { + if (!JS::ToInt32(cx, args[intarg], ip)) return false; if ((uint32_t)*ip >= script->length()) { JS_ReportErrorASCII(cx, "Invalid PC"); @@ -2373,7 +2373,7 @@ PCToLine(JSContext* cx, unsigned argc, Value* vp) int32_t i; unsigned lineno; - if (!GetScriptAndPCArgs(cx, args.length(), args.array(), &script, &i)) + if (!GetScriptAndPCArgs(cx, args, &script, &i)) return false; lineno = PCToLineNumber(script, script->offsetToPC(i)); if (!lineno) From cf85a109177252aad12647619a056e344dda2173 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:45:59 +0900 Subject: [PATCH 40/80] Bug 1304191 - Part 1: Change BUILD_JSVAL to JS::Value::fromRawBits and JS::Value::fromTagAndPayload. r=jwalden --- js/public/Value.h | 94 ++++++++++++++++++++++----------------------- js/src/vm/Value.cpp | 8 ++-- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 76c8c54244e5..e291ebe618e3 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -347,15 +347,9 @@ JS_STATIC_ASSERT(sizeof(jsval_layout) == 8); #endif #if defined(JS_VALUE_IS_CONSTEXPR) -# define JS_RETURN_LAYOUT_FROM_BITS(BITS) \ - return (jsval_layout) { .asBits = (BITS) } # define JS_VALUE_CONSTEXPR constexpr # define JS_VALUE_CONSTEXPR_VAR constexpr #else -# define JS_RETURN_LAYOUT_FROM_BITS(BITS) \ - jsval_layout l; \ - l.asBits = (BITS); \ - return l; # define JS_VALUE_CONSTEXPR # define JS_VALUE_CONSTEXPR_VAR const #endif @@ -368,12 +362,6 @@ JS_STATIC_ASSERT(sizeof(jsval_layout) == 8); * Thus, all comparisons should explicitly cast operands to uint32_t. */ -static inline JS_VALUE_CONSTEXPR jsval_layout -BUILD_JSVAL(JSValueTag tag, uint32_t payload) -{ - JS_RETURN_LAYOUT_FROM_BITS((((uint64_t)(uint32_t)tag) << 32) | payload); -} - static inline bool JSVAL_IS_DOUBLE_IMPL(const jsval_layout& l) { @@ -401,19 +389,6 @@ JSVAL_TO_INT32_IMPL(const jsval_layout& l) return l.s.payload.i32; } -static inline JS_VALUE_CONSTEXPR jsval_layout -INT32_TO_JSVAL_IMPL(int32_t i) -{ -#if defined(JS_VALUE_IS_CONSTEXPR) - return BUILD_JSVAL(JSVAL_TAG_INT32, i); -#else - jsval_layout l; - l.s.tag = JSVAL_TAG_INT32; - l.s.payload.i32 = i; - return l; -#endif -} - static inline bool JSVAL_IS_NUMBER_IMPL(const jsval_layout& l) { @@ -654,12 +629,6 @@ JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) #elif defined(JS_PUNBOX64) -static inline JS_VALUE_CONSTEXPR jsval_layout -BUILD_JSVAL(JSValueTag tag, uint64_t payload) -{ - JS_RETURN_LAYOUT_FROM_BITS((((uint64_t)(uint32_t)tag) << JSVAL_TAG_SHIFT) | payload); -} - static inline bool JSVAL_IS_DOUBLE_IMPL(const jsval_layout& l) { @@ -687,12 +656,6 @@ JSVAL_TO_INT32_IMPL(const jsval_layout& l) return (int32_t)l.asBits; } -static inline JS_VALUE_CONSTEXPR jsval_layout -INT32_TO_JSVAL_IMPL(int32_t i32) -{ - JS_RETURN_LAYOUT_FROM_BITS(((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32); -} - static inline bool JSVAL_IS_NUMBER_IMPL(const jsval_layout& l) { @@ -1011,6 +974,12 @@ CanonicalizeNaN(double d) class Value { public: +#if defined(JS_NUNBOX32) + using PayloadType = uint32_t; +#elif defined(JS_PUNBOX64) + using PayloadType = uint64_t; +#endif + /* * N.B. the default constructor leaves Value unitialized. Adding a default * constructor prevents Value from being stored in a union. @@ -1032,15 +1001,15 @@ class Value /*** Mutators ***/ void setNull() { - data.asBits = BUILD_JSVAL(JSVAL_TAG_NULL, 0).asBits; + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_NULL, 0); } void setUndefined() { - data.asBits = BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0).asBits; + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0); } void setInt32(int32_t i) { - data = INT32_TO_JSVAL_IMPL(i); + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i)); } int32_t& getInt32Ref() { @@ -1374,6 +1343,7 @@ class Value private: #if defined(JS_VALUE_IS_CONSTEXPR) + explicit JS_VALUE_CONSTEXPR Value(uint64_t asBits) : data({ .asBits = asBits }) {} MOZ_IMPLICIT JS_VALUE_CONSTEXPR Value(const jsval_layout& layout) : data(layout) {} #endif @@ -1387,6 +1357,39 @@ class Value friend jsval_layout (::JSVAL_TO_IMPL)(const Value&); friend Value JS_VALUE_CONSTEXPR (::IMPL_TO_JSVAL)(const jsval_layout& l); friend Value JS_VALUE_CONSTEXPR (JS::UndefinedValue)(); + + public: + static JS_VALUE_CONSTEXPR uint64_t + bitsFromTagAndPayload(JSValueTag tag, PayloadType payload) + { +#if defined(JS_NUNBOX32) + return (uint64_t(uint32_t(tag)) << 32) | payload; +#elif defined(JS_PUNBOX64) + return (uint64_t(uint32_t(tag)) << JSVAL_TAG_SHIFT) | payload; +#endif + } + + static JS_VALUE_CONSTEXPR Value + fromTagAndPayload(JSValueTag tag, PayloadType payload) + { + return fromRawBits(bitsFromTagAndPayload(tag, payload)); + } + + static JS_VALUE_CONSTEXPR Value + fromRawBits(uint64_t asBits) { +#if defined(JS_VALUE_IS_CONSTEXPR) + return Value(asBits); +#else + Value v; + v.data.asBits = asBits; + return v; +#endif + } + + static JS_VALUE_CONSTEXPR Value + fromInt32(int32_t i) { + return fromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i)); + } } JS_HAZ_GC_POINTER; inline bool @@ -1419,19 +1422,13 @@ NullValue() static inline JS_VALUE_CONSTEXPR Value UndefinedValue() { -#if defined(JS_VALUE_IS_CONSTEXPR) - return Value(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); -#else - JS::Value v; - v.setUndefined(); - return v; -#endif + return Value::fromTagAndPayload(JSVAL_TAG_UNDEFINED, 0); } static inline JS_VALUE_CONSTEXPR Value Int32Value(int32_t i32) { - return IMPL_TO_JSVAL(INT32_TO_JSVAL_IMPL(i32)); + return Value::fromInt32(i32); } static inline Value @@ -1964,6 +1961,5 @@ extern JS_PUBLIC_DATA(const HandleValue) FalseHandleValue; } // namespace JS #undef JS_VALUE_IS_CONSTEXPR -#undef JS_RETURN_LAYOUT_FROM_BITS #endif /* js_Value_h */ diff --git a/js/src/vm/Value.cpp b/js/src/vm/Value.cpp index 9bc22aec60d7..bdd5b56b516d 100644 --- a/js/src/vm/Value.cpp +++ b/js/src/vm/Value.cpp @@ -6,10 +6,10 @@ #include "js/Value.h" -static const JS::Value JSVAL_NULL = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0)); -static const JS::Value JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, false)); -static const JS::Value JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, true)); -static const JS::Value JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); +static const JS::Value JSVAL_NULL = JS::Value::fromTagAndPayload(JSVAL_TAG_NULL, 0); +static const JS::Value JSVAL_FALSE = JS::Value::fromTagAndPayload(JSVAL_TAG_BOOLEAN, false); +static const JS::Value JSVAL_TRUE = JS::Value::fromTagAndPayload(JSVAL_TAG_BOOLEAN, true); +static const JS::Value JSVAL_VOID = JS::Value::fromTagAndPayload(JSVAL_TAG_UNDEFINED, 0); namespace JS { From 95cdd0842ad3c5429bcaca508d0fd214efa24194 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:45:59 +0900 Subject: [PATCH 41/80] Bug 1304191 - Part 2: Move JSVAL_IS_*_IMPL into JS::Value methods. r=jwalden --- js/public/Value.h | 271 ++++++++++++---------------------------------- 1 file changed, 72 insertions(+), 199 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index e291ebe618e3..1881f8c1fd97 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -377,38 +377,12 @@ DOUBLE_TO_JSVAL_IMPL(double d) return l; } -static inline bool -JSVAL_IS_INT32_IMPL(const jsval_layout& l) -{ - return l.s.tag == JSVAL_TAG_INT32; -} - static inline int32_t JSVAL_TO_INT32_IMPL(const jsval_layout& l) { return l.s.payload.i32; } -static inline bool -JSVAL_IS_NUMBER_IMPL(const jsval_layout& l) -{ - JSValueTag tag = l.s.tag; - MOZ_ASSERT(tag != JSVAL_TAG_CLEAR); - return (uint32_t)tag <= (uint32_t)JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET; -} - -static inline bool -JSVAL_IS_UNDEFINED_IMPL(const jsval_layout& l) -{ - return l.s.tag == JSVAL_TAG_UNDEFINED; -} - -static inline bool -JSVAL_IS_STRING_IMPL(const jsval_layout& l) -{ - return l.s.tag == JSVAL_TAG_STRING; -} - static inline jsval_layout STRING_TO_JSVAL_IMPL(JSString* str) { @@ -425,12 +399,6 @@ JSVAL_TO_STRING_IMPL(const jsval_layout& l) return l.s.payload.str; } -static inline bool -JSVAL_IS_SYMBOL_IMPL(const jsval_layout& l) -{ - return l.s.tag == JSVAL_TAG_SYMBOL; -} - static inline jsval_layout SYMBOL_TO_JSVAL_IMPL(JS::Symbol* sym) { @@ -447,12 +415,6 @@ JSVAL_TO_SYMBOL_IMPL(const jsval_layout& l) return l.s.payload.sym; } -static inline bool -JSVAL_IS_BOOLEAN_IMPL(const jsval_layout& l) -{ - return l.s.tag == JSVAL_TAG_BOOLEAN; -} - static inline bool JSVAL_TO_BOOLEAN_IMPL(const jsval_layout& l) { @@ -468,31 +430,6 @@ BOOLEAN_TO_JSVAL_IMPL(bool b) return l; } -static inline bool -JSVAL_IS_MAGIC_IMPL(const jsval_layout& l) -{ - return l.s.tag == JSVAL_TAG_MAGIC; -} - -static inline bool -JSVAL_IS_OBJECT_IMPL(const jsval_layout& l) -{ - return l.s.tag == JSVAL_TAG_OBJECT; -} - -static inline bool -JSVAL_IS_PRIMITIVE_IMPL(const jsval_layout& l) -{ - return (uint32_t)l.s.tag < (uint32_t)JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET; -} - -static inline bool -JSVAL_IS_OBJECT_OR_NULL_IMPL(const jsval_layout& l) -{ - MOZ_ASSERT((uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_OBJECT); - return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET; -} - static inline JSObject* JSVAL_TO_OBJECT_IMPL(const jsval_layout& l) { @@ -509,12 +446,6 @@ OBJECT_TO_JSVAL_IMPL(JSObject* obj) return l; } -static inline bool -JSVAL_IS_NULL_IMPL(const jsval_layout& l) -{ - return l.s.tag == JSVAL_TAG_NULL; -} - static inline jsval_layout PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr) { @@ -555,13 +486,6 @@ JSVAL_IS_PRIVATE_GCTHING_IMPL(const jsval_layout& l) return l.s.tag == JSVAL_TAG_PRIVATE_GCTHING; } -static inline bool -JSVAL_IS_GCTHING_IMPL(const jsval_layout& l) -{ - /* gcc sometimes generates signed < without explicit casts. */ - return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET; -} - static inline js::gc::Cell* JSVAL_TO_GCTHING_IMPL(const jsval_layout& l) { @@ -582,18 +506,6 @@ JSVAL_TRACE_KIND_IMPL(const jsval_layout& l) return l.s.tag & 0x03; } -static inline bool -JSVAL_IS_SPECIFIC_INT32_IMPL(const jsval_layout& l, int32_t i32) -{ - return l.s.tag == JSVAL_TAG_INT32 && l.s.payload.i32 == i32; -} - -static inline bool -JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(const jsval_layout& l, bool b) -{ - return (l.s.tag == JSVAL_TAG_BOOLEAN) && (l.s.payload.boo == uint32_t(b)); -} - static inline jsval_layout MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) { @@ -644,36 +556,12 @@ DOUBLE_TO_JSVAL_IMPL(double d) return l; } -static inline bool -JSVAL_IS_INT32_IMPL(const jsval_layout& l) -{ - return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_INT32; -} - static inline int32_t JSVAL_TO_INT32_IMPL(const jsval_layout& l) { return (int32_t)l.asBits; } -static inline bool -JSVAL_IS_NUMBER_IMPL(const jsval_layout& l) -{ - return l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET; -} - -static inline bool -JSVAL_IS_UNDEFINED_IMPL(const jsval_layout& l) -{ - return l.asBits == JSVAL_SHIFTED_TAG_UNDEFINED; -} - -static inline bool -JSVAL_IS_STRING_IMPL(const jsval_layout& l) -{ - return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_STRING; -} - static inline jsval_layout STRING_TO_JSVAL_IMPL(JSString* str) { @@ -691,12 +579,6 @@ JSVAL_TO_STRING_IMPL(const jsval_layout& l) return (JSString*)(l.asBits & JSVAL_PAYLOAD_MASK); } -static inline bool -JSVAL_IS_SYMBOL_IMPL(const jsval_layout& l) -{ - return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_SYMBOL; -} - static inline jsval_layout SYMBOL_TO_JSVAL_IMPL(JS::Symbol* sym) { @@ -714,12 +596,6 @@ JSVAL_TO_SYMBOL_IMPL(const jsval_layout& l) return (JS::Symbol*)(l.asBits & JSVAL_PAYLOAD_MASK); } -static inline bool -JSVAL_IS_BOOLEAN_IMPL(const jsval_layout& l) -{ - return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_BOOLEAN; -} - static inline bool JSVAL_TO_BOOLEAN_IMPL(const jsval_layout& l) { @@ -734,32 +610,6 @@ BOOLEAN_TO_JSVAL_IMPL(bool b) return l; } -static inline bool -JSVAL_IS_MAGIC_IMPL(const jsval_layout& l) -{ - return (l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_MAGIC; -} - -static inline bool -JSVAL_IS_PRIMITIVE_IMPL(const jsval_layout& l) -{ - return l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET; -} - -static inline bool -JSVAL_IS_OBJECT_IMPL(const jsval_layout& l) -{ - MOZ_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT); - return l.asBits >= JSVAL_SHIFTED_TAG_OBJECT; -} - -static inline bool -JSVAL_IS_OBJECT_OR_NULL_IMPL(const jsval_layout& l) -{ - MOZ_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT); - return l.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET; -} - static inline JSObject* JSVAL_TO_OBJECT_IMPL(const jsval_layout& l) { @@ -779,24 +629,12 @@ OBJECT_TO_JSVAL_IMPL(JSObject* obj) return l; } -static inline bool -JSVAL_IS_NULL_IMPL(const jsval_layout& l) -{ - return l.asBits == JSVAL_SHIFTED_TAG_NULL; -} - static inline bool JSVAL_IS_PRIVATE_GCTHING_IMPL(const jsval_layout& l) { return (l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_PRIVATE_GCTHING; } -static inline bool -JSVAL_IS_GCTHING_IMPL(const jsval_layout& l) -{ - return l.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET; -} - static inline js::gc::Cell* JSVAL_TO_GCTHING_IMPL(const jsval_layout& l) { @@ -855,18 +693,6 @@ PRIVATE_GCTHING_TO_JSVAL_IMPL(js::gc::Cell* cell) return l; } -static inline bool -JSVAL_IS_SPECIFIC_INT32_IMPL(const jsval_layout& l, int32_t i32) -{ - return l.asBits == (((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32); -} - -static inline bool -JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(const jsval_layout& l, bool b) -{ - return l.asBits == (((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN); -} - static inline jsval_layout MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) { @@ -900,12 +726,6 @@ JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) #endif /* JS_PUNBOX64 */ -static inline bool -JSVAL_IS_TRACEABLE_IMPL(const jsval_layout& l) -{ - return JSVAL_IS_GCTHING_IMPL(l) && !JSVAL_IS_NULL_IMPL(l); -} - static inline jsval_layout JSVAL_TO_IMPL(const JS::Value& v); static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(const jsval_layout& l); @@ -1088,14 +908,39 @@ class Value data.asBits = tmp; } + private: + JSValueTag toTag() const { +#if defined(JS_NUNBOX32) + return data.s.tag; +#elif defined(JS_PUNBOX64) + return JSValueTag(data.asBits >> JSVAL_TAG_SHIFT); +#endif + } + + public: /*** Value type queries ***/ + /* + * N.B. GCC, in some but not all cases, chooses to emit signed comparison + * of JSValueTag even though its underlying type has been forced to be + * uint32_t. Thus, all comparisons should explicitly cast operands to + * uint32_t. + */ + bool isUndefined() const { - return JSVAL_IS_UNDEFINED_IMPL(data); +#if defined(JS_NUNBOX32) + return toTag() == JSVAL_TAG_UNDEFINED; +#elif defined(JS_PUNBOX64) + return data.asBits == JSVAL_SHIFTED_TAG_UNDEFINED; +#endif } bool isNull() const { - return JSVAL_IS_NULL_IMPL(data); +#if defined(JS_NUNBOX32) + return toTag() == JSVAL_TAG_NULL; +#elif defined(JS_PUNBOX64) + return data.asBits == JSVAL_SHIFTED_TAG_NULL; +#endif } bool isNullOrUndefined() const { @@ -1103,68 +948,96 @@ class Value } bool isInt32() const { - return JSVAL_IS_INT32_IMPL(data); + return toTag() == JSVAL_TAG_INT32; } bool isInt32(int32_t i32) const { - return JSVAL_IS_SPECIFIC_INT32_IMPL(data, i32); + return data.asBits == bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i32)); } bool isDouble() const { - return JSVAL_IS_DOUBLE_IMPL(data); +#if defined(JS_NUNBOX32) + return uint32_t(toTag()) <= uint32_t(JSVAL_TAG_CLEAR); +#elif defined(JS_PUNBOX64) + return (data.asBits | mozilla::DoubleTypeTraits::kSignBit) <= JSVAL_SHIFTED_TAG_MAX_DOUBLE; +#endif } bool isNumber() const { - return JSVAL_IS_NUMBER_IMPL(data); +#if defined(JS_NUNBOX32) + MOZ_ASSERT(toTag() != JSVAL_TAG_CLEAR); + return uint32_t(toTag()) <= uint32_t(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET); +#elif defined(JS_PUNBOX64) + return data.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET; +#endif } bool isString() const { - return JSVAL_IS_STRING_IMPL(data); + return toTag() == JSVAL_TAG_STRING; } bool isSymbol() const { - return JSVAL_IS_SYMBOL_IMPL(data); + return toTag() == JSVAL_TAG_SYMBOL; } bool isObject() const { - return JSVAL_IS_OBJECT_IMPL(data); +#if defined(JS_NUNBOX32) + return toTag() == JSVAL_TAG_OBJECT; +#elif defined(JS_PUNBOX64) + MOZ_ASSERT((data.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT); + return data.asBits >= JSVAL_SHIFTED_TAG_OBJECT; +#endif } bool isPrimitive() const { - return JSVAL_IS_PRIMITIVE_IMPL(data); +#if defined(JS_NUNBOX32) + return uint32_t(toTag()) < uint32_t(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET); +#elif defined(JS_PUNBOX64) + return data.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET; +#endif } bool isObjectOrNull() const { - return JSVAL_IS_OBJECT_OR_NULL_IMPL(data); + MOZ_ASSERT(uint32_t(toTag()) <= uint32_t(JSVAL_TAG_OBJECT)); +#if defined(JS_NUNBOX32) + return uint32_t(toTag()) >= uint32_t(JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET); +#elif defined(JS_PUNBOX64) + return data.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET; +#endif } bool isGCThing() const { - return JSVAL_IS_GCTHING_IMPL(data); +#if defined(JS_NUNBOX32) + /* gcc sometimes generates signed < without explicit casts. */ + return uint32_t(toTag()) >= uint32_t(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET); +#elif defined(JS_PUNBOX64) + return data.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET; +#endif } bool isBoolean() const { - return JSVAL_IS_BOOLEAN_IMPL(data); + return toTag() == JSVAL_TAG_BOOLEAN; } bool isTrue() const { - return JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(data, true); + return data.asBits == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(true)); } bool isFalse() const { - return JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(data, false); + return data.asBits == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(false)); } bool isMagic() const { - return JSVAL_IS_MAGIC_IMPL(data); + return toTag() == JSVAL_TAG_MAGIC; } bool isMagic(JSWhyMagic why) const { MOZ_ASSERT_IF(isMagic(), data.s.payload.why == why); - return JSVAL_IS_MAGIC_IMPL(data); + return isMagic(); } bool isMarkable() const { - return JSVAL_IS_TRACEABLE_IMPL(data); + return isGCThing() && !isNull(); } JS::TraceKind traceKind() const { @@ -1299,7 +1172,7 @@ class Value } bool isPrivateGCThing() const { - return JSVAL_IS_PRIVATE_GCTHING_IMPL(data); + return toTag() == JSVAL_TAG_PRIVATE_GCTHING; } /* From 0cf3292966e49e5ff03953d459242e6b2bb922fd Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:45:59 +0900 Subject: [PATCH 42/80] Bug 1304191 - Part 3: Move *_TO_JSVAL_IMPL into JS::Value methods. r=jwalden --- js/public/Value.h | 259 +++++++++++----------------------------------- js/src/jsutil.h | 8 +- 2 files changed, 64 insertions(+), 203 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 1881f8c1fd97..9a6dd96daafb 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -368,47 +368,18 @@ JSVAL_IS_DOUBLE_IMPL(const jsval_layout& l) return (uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_CLEAR; } -static inline jsval_layout -DOUBLE_TO_JSVAL_IMPL(double d) -{ - jsval_layout l; - l.asDouble = d; - MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); - return l; -} - static inline int32_t JSVAL_TO_INT32_IMPL(const jsval_layout& l) { return l.s.payload.i32; } -static inline jsval_layout -STRING_TO_JSVAL_IMPL(JSString* str) -{ - jsval_layout l; - MOZ_ASSERT(uintptr_t(str) > 0x1000); - l.s.tag = JSVAL_TAG_STRING; - l.s.payload.str = str; - return l; -} - static inline JSString* JSVAL_TO_STRING_IMPL(const jsval_layout& l) { return l.s.payload.str; } -static inline jsval_layout -SYMBOL_TO_JSVAL_IMPL(JS::Symbol* sym) -{ - jsval_layout l; - MOZ_ASSERT(uintptr_t(sym) > 0x1000); - l.s.tag = JSVAL_TAG_SYMBOL; - l.s.payload.sym = sym; - return l; -} - static inline JS::Symbol* JSVAL_TO_SYMBOL_IMPL(const jsval_layout& l) { @@ -421,65 +392,18 @@ JSVAL_TO_BOOLEAN_IMPL(const jsval_layout& l) return bool(l.s.payload.boo); } -static inline jsval_layout -BOOLEAN_TO_JSVAL_IMPL(bool b) -{ - jsval_layout l; - l.s.tag = JSVAL_TAG_BOOLEAN; - l.s.payload.boo = uint32_t(b); - return l; -} - static inline JSObject* JSVAL_TO_OBJECT_IMPL(const jsval_layout& l) { return l.s.payload.obj; } -static inline jsval_layout -OBJECT_TO_JSVAL_IMPL(JSObject* obj) -{ - jsval_layout l; - MOZ_ASSERT(uintptr_t(obj) > 0x1000 || uintptr_t(obj) == 0x48); - l.s.tag = JSVAL_TAG_OBJECT; - l.s.payload.obj = obj; - return l; -} - -static inline jsval_layout -PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr) -{ - jsval_layout l; - MOZ_ASSERT((uintptr_t(ptr) & 1) == 0); - l.s.tag = (JSValueTag)0; - l.s.payload.ptr = ptr; - MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); - return l; -} - static inline void* JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l) { return l.s.payload.ptr; } -static inline jsval_layout -PRIVATE_GCTHING_TO_JSVAL_IMPL(js::gc::Cell* cell) -{ - MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String, - "Private GC thing Values must not be strings. Make a StringValue instead."); - MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol, - "Private GC thing Values must not be symbols. Make a SymbolValue instead."); - MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object, - "Private GC thing Values must not be objects. Make an ObjectValue instead."); - - jsval_layout l; - MOZ_ASSERT(uintptr_t(cell) > 0x1000); - l.s.tag = JSVAL_TAG_PRIVATE_GCTHING; - l.s.payload.cell = cell; - return l; -} - static inline bool JSVAL_IS_PRIVATE_GCTHING_IMPL(const jsval_layout& l) { @@ -506,24 +430,6 @@ JSVAL_TRACE_KIND_IMPL(const jsval_layout& l) return l.s.tag & 0x03; } -static inline jsval_layout -MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) -{ - jsval_layout l; - l.s.tag = JSVAL_TAG_MAGIC; - l.s.payload.why = why; - return l; -} - -static inline jsval_layout -MAGIC_UINT32_TO_JSVAL_IMPL(uint32_t payload) -{ - jsval_layout l; - l.s.tag = JSVAL_TAG_MAGIC; - l.s.payload.u32 = payload; - return l; -} - static inline bool JSVAL_SAME_TYPE_IMPL(const jsval_layout& lhs, const jsval_layout& rhs) { @@ -547,49 +453,18 @@ JSVAL_IS_DOUBLE_IMPL(const jsval_layout& l) return (l.asBits | mozilla::DoubleTypeTraits::kSignBit) <= JSVAL_SHIFTED_TAG_MAX_DOUBLE; } -static inline jsval_layout -DOUBLE_TO_JSVAL_IMPL(double d) -{ - jsval_layout l; - l.asDouble = d; - MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); - return l; -} - static inline int32_t JSVAL_TO_INT32_IMPL(const jsval_layout& l) { return (int32_t)l.asBits; } -static inline jsval_layout -STRING_TO_JSVAL_IMPL(JSString* str) -{ - jsval_layout l; - uint64_t strBits = (uint64_t)str; - MOZ_ASSERT(uintptr_t(str) > 0x1000); - MOZ_ASSERT((strBits >> JSVAL_TAG_SHIFT) == 0); - l.asBits = strBits | JSVAL_SHIFTED_TAG_STRING; - return l; -} - static inline JSString* JSVAL_TO_STRING_IMPL(const jsval_layout& l) { return (JSString*)(l.asBits & JSVAL_PAYLOAD_MASK); } -static inline jsval_layout -SYMBOL_TO_JSVAL_IMPL(JS::Symbol* sym) -{ - jsval_layout l; - uint64_t symBits = (uint64_t)sym; - MOZ_ASSERT(uintptr_t(sym) > 0x1000); - MOZ_ASSERT((symBits >> JSVAL_TAG_SHIFT) == 0); - l.asBits = symBits | JSVAL_SHIFTED_TAG_SYMBOL; - return l; -} - static inline JS::Symbol* JSVAL_TO_SYMBOL_IMPL(const jsval_layout& l) { @@ -602,14 +477,6 @@ JSVAL_TO_BOOLEAN_IMPL(const jsval_layout& l) return (bool)(l.asBits & JSVAL_PAYLOAD_MASK); } -static inline jsval_layout -BOOLEAN_TO_JSVAL_IMPL(bool b) -{ - jsval_layout l; - l.asBits = ((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN; - return l; -} - static inline JSObject* JSVAL_TO_OBJECT_IMPL(const jsval_layout& l) { @@ -618,17 +485,6 @@ JSVAL_TO_OBJECT_IMPL(const jsval_layout& l) return (JSObject*)ptrBits; } -static inline jsval_layout -OBJECT_TO_JSVAL_IMPL(JSObject* obj) -{ - jsval_layout l; - uint64_t objBits = (uint64_t)obj; - MOZ_ASSERT(uintptr_t(obj) > 0x1000 || uintptr_t(obj) == 0x48); - MOZ_ASSERT((objBits >> JSVAL_TAG_SHIFT) == 0); - l.asBits = objBits | JSVAL_SHIFTED_TAG_OBJECT; - return l; -} - static inline bool JSVAL_IS_PRIVATE_GCTHING_IMPL(const jsval_layout& l) { @@ -657,17 +513,6 @@ JSVAL_TRACE_KIND_IMPL(const jsval_layout& l) return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) & 0x03; } -static inline jsval_layout -PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr) -{ - jsval_layout l; - uintptr_t ptrBits = uintptr_t(ptr); - MOZ_ASSERT((ptrBits & 1) == 0); - l.asBits = ptrBits >> 1; - MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l)); - return l; -} - static inline void* JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l) { @@ -675,40 +520,6 @@ JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l) return (void*)(l.asBits << 1); } -static inline jsval_layout -PRIVATE_GCTHING_TO_JSVAL_IMPL(js::gc::Cell* cell) -{ - MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String, - "Private GC thing Values must not be strings. Make a StringValue instead."); - MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol, - "Private GC thing Values must not be symbols. Make a SymbolValue instead."); - MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object, - "Private GC thing Values must not be objects. Make an ObjectValue instead."); - - jsval_layout l; - uint64_t cellBits = (uint64_t)cell; - MOZ_ASSERT(uintptr_t(cellBits) > 0x1000); - MOZ_ASSERT((cellBits >> JSVAL_TAG_SHIFT) == 0); - l.asBits = cellBits | JSVAL_SHIFTED_TAG_PRIVATE_GCTHING; - return l; -} - -static inline jsval_layout -MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) -{ - jsval_layout l; - l.asBits = ((uint64_t)(uint32_t)why) | JSVAL_SHIFTED_TAG_MAGIC; - return l; -} - -static inline jsval_layout -MAGIC_UINT32_TO_JSVAL_IMPL(uint32_t payload) -{ - jsval_layout l; - l.asBits = ((uint64_t)payload) | JSVAL_SHIFTED_TAG_MAGIC; - return l; -} - static inline bool JSVAL_SAME_TYPE_IMPL(const jsval_layout& lhs, const jsval_layout& rhs) { @@ -732,6 +543,7 @@ static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(const jsval_layout& l); namespace JS { static inline JS_VALUE_CONSTEXPR JS::Value UndefinedValue(); +static inline JS::Value PoisonedObjectValue(JSObject* obj); /** * Returns a generic quiet NaN value, with all payload bits set to zero. @@ -838,7 +650,8 @@ class Value } void setDouble(double d) { - data = DOUBLE_TO_JSVAL_IMPL(d); + data.asDouble = d; + MOZ_ASSERT(isDouble()); } void setNaN() { @@ -851,27 +664,45 @@ class Value } void setString(JSString* str) { - data = STRING_TO_JSVAL_IMPL(str); + MOZ_ASSERT(uintptr_t(str) > 0x1000); + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str)); } void setSymbol(JS::Symbol* sym) { - data = SYMBOL_TO_JSVAL_IMPL(sym); + MOZ_ASSERT(uintptr_t(sym) > 0x1000); + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym)); } void setObject(JSObject& obj) { - data = OBJECT_TO_JSVAL_IMPL(&obj); + MOZ_ASSERT(uintptr_t(&obj) > 0x1000 || uintptr_t(&obj) == 0x48); +#if defined(JS_PUNBOX64) + // VisualStudio cannot contain parenthesized C++ style cast and shift + // inside decltype in template parameter: + // AssertionConditionType> 1))> + // It throws syntax error. + MOZ_ASSERT((((uintptr_t)&obj) >> JSVAL_TAG_SHIFT) == 0); +#endif + setObjectNoCheck(&obj); } + private: + void setObjectNoCheck(JSObject* obj) { + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_OBJECT, PayloadType(obj)); + } + + friend inline Value PoisonedObjectValue(JSObject* obj); + + public: void setBoolean(bool b) { - data = BOOLEAN_TO_JSVAL_IMPL(b); + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(b)); } void setMagic(JSWhyMagic why) { - data = MAGIC_TO_JSVAL_IMPL(why); + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, uint32_t(why)); } void setMagicUint32(uint32_t payload) { - data = MAGIC_UINT32_TO_JSVAL_IMPL(payload); + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, payload); } bool setNumber(uint32_t ui) { @@ -1141,11 +972,18 @@ class Value */ void setPrivate(void* ptr) { - data = PRIVATE_PTR_TO_JSVAL_IMPL(ptr); + MOZ_ASSERT((uintptr_t(ptr) & 1) == 0); +#if defined(JS_NUNBOX32) + data.s.tag = JSValueTag(0); + data.s.payload.ptr = ptr; +#elif defined(JS_PUNBOX64) + data.asBits = uintptr_t(ptr) >> 1; +#endif + MOZ_ASSERT(isDouble()); } void* toPrivate() const { - MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(data)); + MOZ_ASSERT(isDouble()); return JSVAL_TO_PRIVATE_PTR_IMPL(data); } @@ -1168,7 +1006,22 @@ class Value */ void setPrivateGCThing(js::gc::Cell* cell) { - data = PRIVATE_GCTHING_TO_JSVAL_IMPL(cell); + MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String, + "Private GC thing Values must not be strings. Make a StringValue instead."); + MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol, + "Private GC thing Values must not be symbols. Make a SymbolValue instead."); + MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object, + "Private GC thing Values must not be objects. Make an ObjectValue instead."); + + MOZ_ASSERT(uintptr_t(cell) > 0x1000); +#if defined(JS_PUNBOX64) + // VisualStudio cannot contain parenthesized C++ style cast and shift + // inside decltype in template parameter: + // AssertionConditionType> 1))> + // It throws syntax error. + MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0); +#endif + data.asBits = bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell)); } bool isPrivateGCThing() const { @@ -1553,6 +1406,14 @@ PrivateGCThingValue(js::gc::Cell* cell) return v; } +static inline Value +PoisonedObjectValue(JSObject* obj) +{ + Value v; + v.setObjectNoCheck(obj); + return v; +} + inline bool SameType(const Value& lhs, const Value& rhs) { diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 7d846f506aa2..790b97ddca33 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -346,11 +346,11 @@ Poison(void* ptr, uint8_t value, size_t num) # if defined(JS_PUNBOX64) obj = obj & ((uintptr_t(1) << JSVAL_TAG_SHIFT) - 1); # endif - const jsval_layout layout = OBJECT_TO_JSVAL_IMPL((JSObject*)obj); + JS::Value v = JS::PoisonedObjectValue(reinterpret_cast(obj)); - size_t value_count = num / sizeof(jsval_layout); - size_t byte_count = num % sizeof(jsval_layout); - mozilla::PodSet((jsval_layout*)ptr, layout, value_count); + size_t value_count = num / sizeof(v); + size_t byte_count = num % sizeof(v); + mozilla::PodSet(reinterpret_cast(ptr), v, value_count); if (byte_count) { uint8_t* bytes = static_cast(ptr); uint8_t* end = bytes + num; From 481cbe78ba1828d75f77fc6baffdf420c150cf73 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:00 +0900 Subject: [PATCH 43/80] Bug 1304191 - Part 4: Move JSVAL_SAME_TYPE_IMPL into SameType. r=jwalden --- js/public/Value.h | 46 +++++++++-------------------------- js/src/vm/StructuredClone.cpp | 6 ++--- 2 files changed, 15 insertions(+), 37 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 9a6dd96daafb..f9137c923a09 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -356,18 +356,6 @@ JS_STATIC_ASSERT(sizeof(jsval_layout) == 8); #if defined(JS_NUNBOX32) -/* - * N.B. GCC, in some but not all cases, chooses to emit signed comparison of - * JSValueTag even though its underlying type has been forced to be uint32_t. - * Thus, all comparisons should explicitly cast operands to uint32_t. - */ - -static inline bool -JSVAL_IS_DOUBLE_IMPL(const jsval_layout& l) -{ - return (uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_CLEAR; -} - static inline int32_t JSVAL_TO_INT32_IMPL(const jsval_layout& l) { @@ -430,13 +418,6 @@ JSVAL_TRACE_KIND_IMPL(const jsval_layout& l) return l.s.tag & 0x03; } -static inline bool -JSVAL_SAME_TYPE_IMPL(const jsval_layout& lhs, const jsval_layout& rhs) -{ - JSValueTag ltag = lhs.s.tag, rtag = rhs.s.tag; - return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR); -} - static inline JSValueType JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) { @@ -447,12 +428,6 @@ JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) #elif defined(JS_PUNBOX64) -static inline bool -JSVAL_IS_DOUBLE_IMPL(const jsval_layout& l) -{ - return (l.asBits | mozilla::DoubleTypeTraits::kSignBit) <= JSVAL_SHIFTED_TAG_MAX_DOUBLE; -} - static inline int32_t JSVAL_TO_INT32_IMPL(const jsval_layout& l) { @@ -520,13 +495,6 @@ JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l) return (void*)(l.asBits << 1); } -static inline bool -JSVAL_SAME_TYPE_IMPL(const jsval_layout& lhs, const jsval_layout& rhs) -{ - return (JSVAL_IS_DOUBLE_IMPL(lhs) && JSVAL_IS_DOUBLE_IMPL(rhs)) || - (((lhs.asBits ^ rhs.asBits) & 0xFFFF800000000000LL) == 0); -} - static inline JSValueType JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) { @@ -650,10 +618,14 @@ class Value } void setDouble(double d) { - data.asDouble = d; + setDoubleNoCheck(d); MOZ_ASSERT(isDouble()); } + void setDoubleNoCheck(double d) { + data.asDouble = d; + } + void setNaN() { setDouble(GenericNaN()); } @@ -1417,7 +1389,13 @@ PoisonedObjectValue(JSObject* obj) inline bool SameType(const Value& lhs, const Value& rhs) { - return JSVAL_SAME_TYPE_IMPL(lhs.data, rhs.data); +#if defined(JS_NUNBOX32) + JSValueTag ltag = lhs.toTag(), rtag = rhs.toTag(); + return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR); +#elif defined(JS_PUNBOX64) + return (lhs.isDouble() && rhs.isDouble()) || + (((lhs.data.asBits ^ rhs.data.asBits) & 0xFFFF800000000000ULL) == 0); +#endif } } // namespace JS diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index 593fb77d243e..42b4834c43e3 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -1604,9 +1604,9 @@ JSStructuredCloneWriter::write(HandleValue v) bool JSStructuredCloneReader::checkDouble(double d) { - jsval_layout l; - l.asDouble = d; - if (!JSVAL_IS_DOUBLE_IMPL(l)) { + JS::Value v; + v.setDoubleNoCheck(d); + if (!v.isDouble()) { JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA, "unrecognized NaN"); return false; From a552563f547da7d253c1b3d9b481db0a5a4c2502 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:00 +0900 Subject: [PATCH 44/80] Bug 1304191 - Part 5: Move JSVAL_TRACE_KIND_IMPL into JS::Value methods. r=jwalden --- js/public/Value.h | 54 ++++++++++++----------------------------------- 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index f9137c923a09..e1f2c79bf73a 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -392,32 +392,12 @@ JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l) return l.s.payload.ptr; } -static inline bool -JSVAL_IS_PRIVATE_GCTHING_IMPL(const jsval_layout& l) -{ - return l.s.tag == JSVAL_TAG_PRIVATE_GCTHING; -} - static inline js::gc::Cell* JSVAL_TO_GCTHING_IMPL(const jsval_layout& l) { return l.s.payload.cell; } -static inline uint32_t -JSVAL_TRACE_KIND_IMPL(const jsval_layout& l) -{ - static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String), - "Value type tags must correspond with JS::TraceKinds."); - static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol), - "Value type tags must correspond with JS::TraceKinds."); - static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object), - "Value type tags must correspond with JS::TraceKinds."); - if (MOZ_UNLIKELY(JSVAL_IS_PRIVATE_GCTHING_IMPL(l))) - return (uint32_t)JS::GCThingTraceKind(JSVAL_TO_GCTHING_IMPL(l)); - return l.s.tag & 0x03; -} - static inline JSValueType JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) { @@ -460,12 +440,6 @@ JSVAL_TO_OBJECT_IMPL(const jsval_layout& l) return (JSObject*)ptrBits; } -static inline bool -JSVAL_IS_PRIVATE_GCTHING_IMPL(const jsval_layout& l) -{ - return (l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_PRIVATE_GCTHING; -} - static inline js::gc::Cell* JSVAL_TO_GCTHING_IMPL(const jsval_layout& l) { @@ -474,20 +448,6 @@ JSVAL_TO_GCTHING_IMPL(const jsval_layout& l) return reinterpret_cast(ptrBits); } -static inline uint32_t -JSVAL_TRACE_KIND_IMPL(const jsval_layout& l) -{ - static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String), - "Value type tags must correspond with JS::TraceKinds."); - static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol), - "Value type tags must correspond with JS::TraceKinds."); - static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object), - "Value type tags must correspond with JS::TraceKinds."); - if (MOZ_UNLIKELY(JSVAL_IS_PRIVATE_GCTHING_IMPL(l))) - return (uint32_t)JS::GCThingTraceKind(JSVAL_TO_GCTHING_IMPL(l)); - return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) & 0x03; -} - static inline void* JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l) { @@ -845,7 +805,19 @@ class Value JS::TraceKind traceKind() const { MOZ_ASSERT(isMarkable()); - return JS::TraceKind(JSVAL_TRACE_KIND_IMPL(data)); + static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String), + "Value type tags must correspond with JS::TraceKinds."); + static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol), + "Value type tags must correspond with JS::TraceKinds."); + static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object), + "Value type tags must correspond with JS::TraceKinds."); + if (MOZ_UNLIKELY(isPrivateGCThing())) + return JS::GCThingTraceKind(JSVAL_TO_GCTHING_IMPL(data)); +#if defined(JS_NUNBOX32) + return JS::TraceKind(toTag() & 0x03); +#elif defined(JS_PUNBOX64) + return JS::TraceKind(uint32_t(data.asBits >> JSVAL_TAG_SHIFT) & 0x03); +#endif } JSWhyMagic whyMagic() const { From 6535bee8fbefa08c328260e06fd105ed8e1da828 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:00 +0900 Subject: [PATCH 45/80] Bug 1304191 - Part 5.1: Always use JS::Value::toTag() to get tag in JS::Value::traceKind. r=jwalden --- js/public/Value.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index e1f2c79bf73a..d4dcbe50f4a2 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -813,11 +813,7 @@ class Value "Value type tags must correspond with JS::TraceKinds."); if (MOZ_UNLIKELY(isPrivateGCThing())) return JS::GCThingTraceKind(JSVAL_TO_GCTHING_IMPL(data)); -#if defined(JS_NUNBOX32) return JS::TraceKind(toTag() & 0x03); -#elif defined(JS_PUNBOX64) - return JS::TraceKind(uint32_t(data.asBits >> JSVAL_TAG_SHIFT) & 0x03); -#endif } JSWhyMagic whyMagic() const { From a35fdf826742935ea2d7a8da798282413edf4b44 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:00 +0900 Subject: [PATCH 46/80] Bug 1304191 - Part 6: Move JSVAL_TO_*_IMPL into JS::Value methods. r=jwalden --- js/public/Value.h | 144 +++++++++++++++------------------------------- 1 file changed, 46 insertions(+), 98 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index d4dcbe50f4a2..5f21fd6b914f 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -356,48 +356,6 @@ JS_STATIC_ASSERT(sizeof(jsval_layout) == 8); #if defined(JS_NUNBOX32) -static inline int32_t -JSVAL_TO_INT32_IMPL(const jsval_layout& l) -{ - return l.s.payload.i32; -} - -static inline JSString* -JSVAL_TO_STRING_IMPL(const jsval_layout& l) -{ - return l.s.payload.str; -} - -static inline JS::Symbol* -JSVAL_TO_SYMBOL_IMPL(const jsval_layout& l) -{ - return l.s.payload.sym; -} - -static inline bool -JSVAL_TO_BOOLEAN_IMPL(const jsval_layout& l) -{ - return bool(l.s.payload.boo); -} - -static inline JSObject* -JSVAL_TO_OBJECT_IMPL(const jsval_layout& l) -{ - return l.s.payload.obj; -} - -static inline void* -JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l) -{ - return l.s.payload.ptr; -} - -static inline js::gc::Cell* -JSVAL_TO_GCTHING_IMPL(const jsval_layout& l) -{ - return l.s.payload.cell; -} - static inline JSValueType JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) { @@ -408,53 +366,6 @@ JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) #elif defined(JS_PUNBOX64) -static inline int32_t -JSVAL_TO_INT32_IMPL(const jsval_layout& l) -{ - return (int32_t)l.asBits; -} - -static inline JSString* -JSVAL_TO_STRING_IMPL(const jsval_layout& l) -{ - return (JSString*)(l.asBits & JSVAL_PAYLOAD_MASK); -} - -static inline JS::Symbol* -JSVAL_TO_SYMBOL_IMPL(const jsval_layout& l) -{ - return (JS::Symbol*)(l.asBits & JSVAL_PAYLOAD_MASK); -} - -static inline bool -JSVAL_TO_BOOLEAN_IMPL(const jsval_layout& l) -{ - return (bool)(l.asBits & JSVAL_PAYLOAD_MASK); -} - -static inline JSObject* -JSVAL_TO_OBJECT_IMPL(const jsval_layout& l) -{ - uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK; - MOZ_ASSERT((ptrBits & 0x7) == 0); - return (JSObject*)ptrBits; -} - -static inline js::gc::Cell* -JSVAL_TO_GCTHING_IMPL(const jsval_layout& l) -{ - uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK; - MOZ_ASSERT((ptrBits & 0x7) == 0); - return reinterpret_cast(ptrBits); -} - -static inline void* -JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l) -{ - MOZ_ASSERT((l.asBits & 0x8000000000000000LL) == 0); - return (void*)(l.asBits << 1); -} - static inline JSValueType JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) { @@ -812,7 +723,7 @@ class Value static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object), "Value type tags must correspond with JS::TraceKinds."); if (MOZ_UNLIKELY(isPrivateGCThing())) - return JS::GCThingTraceKind(JSVAL_TO_GCTHING_IMPL(data)); + return JS::GCThingTraceKind(toGCThing()); return JS::TraceKind(toTag() & 0x03); } @@ -842,7 +753,11 @@ class Value int32_t toInt32() const { MOZ_ASSERT(isInt32()); - return JSVAL_TO_INT32_IMPL(data); +#if defined(JS_NUNBOX32) + return data.s.payload.i32; +#elif defined(JS_PUNBOX64) + return int32_t(data.asBits); +#endif } double toDouble() const { @@ -857,27 +772,51 @@ class Value JSString* toString() const { MOZ_ASSERT(isString()); - return JSVAL_TO_STRING_IMPL(data); +#if defined(JS_NUNBOX32) + return data.s.payload.str; +#elif defined(JS_PUNBOX64) + return reinterpret_cast(data.asBits & JSVAL_PAYLOAD_MASK); +#endif } JS::Symbol* toSymbol() const { MOZ_ASSERT(isSymbol()); - return JSVAL_TO_SYMBOL_IMPL(data); +#if defined(JS_NUNBOX32) + return data.s.payload.sym; +#elif defined(JS_PUNBOX64) + return reinterpret_cast(data.asBits & JSVAL_PAYLOAD_MASK); +#endif } JSObject& toObject() const { MOZ_ASSERT(isObject()); - return *JSVAL_TO_OBJECT_IMPL(data); +#if defined(JS_NUNBOX32) + return *data.s.payload.obj; +#elif defined(JS_PUNBOX64) + return *toObjectOrNull(); +#endif } JSObject* toObjectOrNull() const { MOZ_ASSERT(isObjectOrNull()); - return JSVAL_TO_OBJECT_IMPL(data); +#if defined(JS_NUNBOX32) + return data.s.payload.obj; +#elif defined(JS_PUNBOX64) + uint64_t ptrBits = data.asBits & JSVAL_PAYLOAD_MASK; + MOZ_ASSERT((ptrBits & 0x7) == 0); + return reinterpret_cast(ptrBits); +#endif } js::gc::Cell* toGCThing() const { MOZ_ASSERT(isGCThing()); - return JSVAL_TO_GCTHING_IMPL(data); +#if defined(JS_NUNBOX32) + return data.s.payload.cell; +#elif defined(JS_PUNBOX64) + uint64_t ptrBits = data.asBits & JSVAL_PAYLOAD_MASK; + MOZ_ASSERT((ptrBits & 0x7) == 0); + return reinterpret_cast(ptrBits); +#endif } GCCellPtr toGCCellPtr() const { @@ -886,7 +825,11 @@ class Value bool toBoolean() const { MOZ_ASSERT(isBoolean()); - return JSVAL_TO_BOOLEAN_IMPL(data); +#if defined(JS_NUNBOX32) + return bool(data.s.payload.boo); +#elif defined(JS_PUNBOX64) + return bool(data.asBits & JSVAL_PAYLOAD_MASK); +#endif } uint32_t payloadAsRawUint32() const { @@ -924,7 +867,12 @@ class Value void* toPrivate() const { MOZ_ASSERT(isDouble()); - return JSVAL_TO_PRIVATE_PTR_IMPL(data); +#if defined(JS_NUNBOX32) + return data.s.payload.ptr; +#elif defined(JS_PUNBOX64) + MOZ_ASSERT((data.asBits & 0x8000000000000000ULL) == 0); + return reinterpret_cast(data.asBits << 1); +#endif } void setPrivateUint32(uint32_t ui) { From ab5fd275532760a4742fe3cabe9f5e95fd8bab3b Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:00 +0900 Subject: [PATCH 47/80] Bug 1304191 - Part 7: Move JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL into JS::Value methods. r=jwalden --- js/public/Value.h | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 5f21fd6b914f..6177726d9780 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -354,28 +354,6 @@ JS_STATIC_ASSERT(sizeof(jsval_layout) == 8); # define JS_VALUE_CONSTEXPR_VAR const #endif -#if defined(JS_NUNBOX32) - -static inline JSValueType -JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) -{ - uint32_t type = l.s.tag & 0xF; - MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE); - return (JSValueType)type; -} - -#elif defined(JS_PUNBOX64) - -static inline JSValueType -JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l) -{ - uint64_t type = (l.asBits >> JSVAL_TAG_SHIFT) & 0xF; - MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE); - return (JSValueType)type; -} - -#endif /* JS_PUNBOX64 */ - static inline jsval_layout JSVAL_TO_IMPL(const JS::Value& v); static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(const jsval_layout& l); @@ -842,7 +820,9 @@ class Value } JSValueType extractNonDoubleType() const { - return JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(data); + uint32_t type = toTag() & 0xF; + MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE); + return JSValueType(type); } /* From 58ffefe531f0c0aadf8c485b7082ab6c020afe44 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:01 +0900 Subject: [PATCH 48/80] Bug 1304191 - Part 8: Stop using jsval_layout in JIT. r=jwalden --- js/public/Value.h | 20 +++++++++ js/src/jit/CodeGenerator.cpp | 2 +- js/src/jit/JitFrames.cpp | 45 +++++++------------ js/src/jit/Lowering.cpp | 2 +- js/src/jit/MacroAssembler.cpp | 6 +-- js/src/jit/arm/MacroAssembler-arm.cpp | 24 +++++----- js/src/jit/arm/MacroAssembler-arm.h | 27 +++++------ js/src/jit/arm64/Assembler-arm64.cpp | 8 ++-- js/src/jit/arm64/MacroAssembler-arm64.h | 7 ++- js/src/jit/mips32/MacroAssembler-mips32.cpp | 8 ++-- js/src/jit/mips32/MacroAssembler-mips32.h | 7 ++- js/src/jit/mips64/Assembler-mips64.cpp | 6 +-- js/src/jit/mips64/MacroAssembler-mips64.cpp | 8 ++-- js/src/jit/mips64/MacroAssembler-mips64.h | 2 +- js/src/jit/x64/MacroAssembler-x64.h | 15 +++---- .../jit/x86-shared/Assembler-x86-shared.cpp | 8 ++-- js/src/jit/x86/MacroAssembler-x86.cpp | 9 ++-- js/src/jit/x86/MacroAssembler-x86.h | 22 ++++----- 18 files changed, 102 insertions(+), 124 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 6177726d9780..563171fdbf23 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -570,6 +570,21 @@ class Value } public: + /*** JIT-only interfaces to interact with and create raw Values ***/ +#if defined(JS_NUNBOX32) + PayloadType toNunboxPayload() const { + return data.s.payload.i32; + } + + JSValueTag toNunboxTag() const { + return data.s.tag; + } +#elif defined(JS_PUNBOX64) + const void* bitsAsPunboxPointer() const { + return reinterpret_cast(data.asBits); + } +#endif + /*** Value type queries ***/ /* @@ -797,6 +812,11 @@ class Value #endif } + js::gc::Cell* toMarkablePointer() const { + MOZ_ASSERT(isMarkable()); + return toGCThing(); + } + GCCellPtr toGCCellPtr() const { return GCCellPtr(toGCThing(), traceKind()); } diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 29c20429fc04..680748482a3c 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -8443,7 +8443,7 @@ StoreUnboxedPointer(MacroAssembler& masm, T address, MIRType type, const LAlloca if (value->isConstant()) { Value v = value->toConstant()->toJSValue(); if (v.isMarkable()) { - masm.storePtr(ImmGCPtr(v.toGCThing()), address); + masm.storePtr(ImmGCPtr(v.toMarkablePointer()), address); } else { MOZ_ASSERT(v.isNull()); masm.storePtr(ImmWord(0), address); diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index feb24367499f..a3caf63f9dfd 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -1069,17 +1069,16 @@ MarkIonJSFrame(JSTracer* trc, const JitFrameIterator& frame) #ifdef JS_NUNBOX32 LAllocation type, payload; while (safepoint.getNunboxSlot(&type, &payload)) { - jsval_layout layout; - layout.s.tag = (JSValueTag)ReadAllocation(frame, &type); - layout.s.payload.uintptr = ReadAllocation(frame, &payload); + JSValueTag tag = JSValueTag(ReadAllocation(frame, &type)); + uintptr_t rawPayload = ReadAllocation(frame, &payload); - Value v = IMPL_TO_JSVAL(layout); + Value v = Value::fromTagAndPayload(tag, rawPayload); TraceRoot(trc, &v, "ion-torn-value"); - if (v != IMPL_TO_JSVAL(layout)) { + if (v != Value::fromTagAndPayload(tag, rawPayload)) { // GC moved the value, replace the stored payload. - layout = JSVAL_TO_IMPL(v); - WriteAllocation(frame, &payload, layout.s.payload.uintptr); + rawPayload = *v.payloadUIntPtr(); + WriteAllocation(frame, &payload, rawPayload); } } #endif @@ -1829,48 +1828,36 @@ SnapshotIterator::allocationValue(const RValueAllocation& alloc, ReadMethod rm) #if defined(JS_NUNBOX32) case RValueAllocation::UNTYPED_REG_REG: { - jsval_layout layout; - layout.s.tag = (JSValueTag) fromRegister(alloc.reg()); - layout.s.payload.word = fromRegister(alloc.reg2()); - return IMPL_TO_JSVAL(layout); + return Value::fromTagAndPayload(JSValueTag(fromRegister(alloc.reg())), + fromRegister(alloc.reg2())); } case RValueAllocation::UNTYPED_REG_STACK: { - jsval_layout layout; - layout.s.tag = (JSValueTag) fromRegister(alloc.reg()); - layout.s.payload.word = fromStack(alloc.stackOffset2()); - return IMPL_TO_JSVAL(layout); + return Value::fromTagAndPayload(JSValueTag(fromRegister(alloc.reg())), + fromStack(alloc.stackOffset2())); } case RValueAllocation::UNTYPED_STACK_REG: { - jsval_layout layout; - layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset()); - layout.s.payload.word = fromRegister(alloc.reg2()); - return IMPL_TO_JSVAL(layout); + return Value::fromTagAndPayload(JSValueTag(fromStack(alloc.stackOffset())), + fromRegister(alloc.reg2())); } case RValueAllocation::UNTYPED_STACK_STACK: { - jsval_layout layout; - layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset()); - layout.s.payload.word = fromStack(alloc.stackOffset2()); - return IMPL_TO_JSVAL(layout); + return Value::fromTagAndPayload(JSValueTag(fromStack(alloc.stackOffset())), + fromStack(alloc.stackOffset2())); } #elif defined(JS_PUNBOX64) case RValueAllocation::UNTYPED_REG: { - jsval_layout layout; - layout.asBits = fromRegister(alloc.reg()); - return IMPL_TO_JSVAL(layout); + return Value::fromRawBits(fromRegister(alloc.reg())); } case RValueAllocation::UNTYPED_STACK: { - jsval_layout layout; - layout.asBits = fromStack(alloc.stackOffset()); - return IMPL_TO_JSVAL(layout); + return Value::fromRawBits(fromStack(alloc.stackOffset())); } #endif diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 1fcbc12c3dde..92fa8af57c85 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2662,7 +2662,7 @@ IsNonNurseryConstant(MDefinition* def) if (!def->isConstant()) return false; Value v = def->toConstant()->toJSValue(); - return !v.isMarkable() || !IsInsideNursery(v.toGCThing()); + return !v.isMarkable() || !IsInsideNursery(v.toMarkablePointer()); } void diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 1993dd06feec..454b7757da8f 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -972,15 +972,13 @@ MacroAssembler::fillSlotsWithConstantValue(Address base, Register temp, #ifdef JS_NUNBOX32 // We only have a single spare register, so do the initialization as two // strided writes of the tag and body. - jsval_layout jv = JSVAL_TO_IMPL(v); - Address addr = base; - move32(Imm32(jv.s.payload.i32), temp); + move32(Imm32(v.toNunboxPayload()), temp); for (unsigned i = start; i < end; ++i, addr.offset += sizeof(GCPtrValue)) store32(temp, ToPayload(addr)); addr = base; - move32(Imm32(jv.s.tag), temp); + move32(Imm32(v.toNunboxTag()), temp); for (unsigned i = start; i < end; ++i, addr.offset += sizeof(GCPtrValue)) store32(temp, ToType(addr)); #else diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 17134772c6ee..122424e12387 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3269,12 +3269,11 @@ MacroAssemblerARMCompat::extractTag(const BaseIndex& address, Register scratch) void MacroAssemblerARMCompat::moveValue(const Value& val, Register type, Register data) { - jsval_layout jv = JSVAL_TO_IMPL(val); - ma_mov(Imm32(jv.s.tag), type); + ma_mov(Imm32(val.toNunboxTag()), type); if (val.isMarkable()) - ma_mov(ImmGCPtr(reinterpret_cast(val.toGCThing())), data); + ma_mov(ImmGCPtr(val.toMarkablePointer()), data); else - ma_mov(Imm32(jv.s.payload.i32), data); + ma_mov(Imm32(val.toNunboxPayload()), data); } void @@ -3447,11 +3446,10 @@ MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest) ScratchRegisterScope scratch(asMasm()); SecondScratchRegisterScope scratch2(asMasm()); - jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) - ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch); + ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch); else - ma_mov(Imm32(jv.s.payload.i32), scratch); + ma_mov(Imm32(val.toNunboxPayload()), scratch); ma_str(scratch, ToPayload(dest), scratch2); } @@ -3470,11 +3468,10 @@ MacroAssemblerARMCompat::storePayload(const Value& val, const BaseIndex& dest) ScratchRegisterScope scratch(asMasm()); SecondScratchRegisterScope scratch2(asMasm()); - jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) - ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch); + ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch); else - ma_mov(Imm32(jv.s.payload.i32), scratch); + ma_mov(Imm32(val.toNunboxPayload()), scratch); // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index // << shift + imm] cannot be encoded into a single instruction, and cannot @@ -5302,12 +5299,11 @@ MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, // equal, short circuit false (NotEqual). ScratchRegisterScope scratch(*this); - jsval_layout jv = JSVAL_TO_IMPL(rhs); if (rhs.isMarkable()) - ma_cmp(lhs.payloadReg(), ImmGCPtr(reinterpret_cast(rhs.toGCThing())), scratch); + ma_cmp(lhs.payloadReg(), ImmGCPtr(rhs.toMarkablePointer()), scratch); else - ma_cmp(lhs.payloadReg(), Imm32(jv.s.payload.i32), scratch); - ma_cmp(lhs.typeReg(), Imm32(jv.s.tag), scratch, Equal); + ma_cmp(lhs.payloadReg(), Imm32(rhs.toNunboxPayload()), scratch); + ma_cmp(lhs.typeReg(), Imm32(rhs.toNunboxTag()), scratch, Equal); ma_b(label, cond); } diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 63af3b4f03b8..cf8f335bcaa2 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -913,19 +913,17 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM ScratchRegisterScope scratch(asMasm()); SecondScratchRegisterScope scratch2(asMasm()); - jsval_layout jv = JSVAL_TO_IMPL(val); - ma_mov(Imm32(jv.s.tag), scratch); + ma_mov(Imm32(val.toNunboxTag()), scratch); ma_str(scratch, ToType(dest), scratch2); if (val.isMarkable()) - ma_mov(ImmGCPtr(reinterpret_cast(val.toGCThing())), scratch); + ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch); else - ma_mov(Imm32(jv.s.payload.i32), scratch); + ma_mov(Imm32(val.toNunboxPayload()), scratch); ma_str(scratch, ToPayload(dest), scratch2); } void storeValue(const Value& val, BaseIndex dest) { ScratchRegisterScope scratch(asMasm()); SecondScratchRegisterScope scratch2(asMasm()); - jsval_layout jv = JSVAL_TO_IMPL(val); int32_t typeoffset = dest.offset + NUNBOX32_TYPE_OFFSET; int32_t payloadoffset = dest.offset + NUNBOX32_PAYLOAD_OFFSET; @@ -934,11 +932,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM // Store the type. if (typeoffset < 4096 && typeoffset > -4096) { - ma_mov(Imm32(jv.s.tag), scratch2); + ma_mov(Imm32(val.toNunboxTag()), scratch2); ma_str(scratch2, DTRAddr(scratch, DtrOffImm(typeoffset))); } else { ma_add(Imm32(typeoffset), scratch, scratch2); - ma_mov(Imm32(jv.s.tag), scratch2); + ma_mov(Imm32(val.toNunboxTag()), scratch2); ma_str(scratch2, DTRAddr(scratch, DtrOffImm(0))); // Restore scratch for the payload store. ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd); @@ -947,16 +945,16 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM // Store the payload, marking if necessary. if (payloadoffset < 4096 && payloadoffset > -4096) { if (val.isMarkable()) - ma_mov(ImmGCPtr(reinterpret_cast(val.toGCThing())), scratch2); + ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch2); else - ma_mov(Imm32(jv.s.payload.i32), scratch2); + ma_mov(Imm32(val.toNunboxPayload()), scratch2); ma_str(scratch2, DTRAddr(scratch, DtrOffImm(payloadoffset))); } else { ma_add(Imm32(payloadoffset), scratch, scratch2); if (val.isMarkable()) - ma_mov(ImmGCPtr(reinterpret_cast(val.toGCThing())), scratch2); + ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch2); else - ma_mov(Imm32(jv.s.payload.i32), scratch2); + ma_mov(Imm32(val.toNunboxPayload()), scratch2); ma_str(scratch2, DTRAddr(scratch, DtrOffImm(0))); } } @@ -978,12 +976,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void pushValue(ValueOperand val); void popValue(ValueOperand val); void pushValue(const Value& val) { - jsval_layout jv = JSVAL_TO_IMPL(val); - push(Imm32(jv.s.tag)); + push(Imm32(val.toNunboxTag())); if (val.isMarkable()) - push(ImmGCPtr(reinterpret_cast(val.toGCThing()))); + push(ImmGCPtr(val.toMarkablePointer())); else - push(Imm32(jv.s.payload.i32)); + push(Imm32(val.toNunboxPayload())); } void pushValue(JSValueType type, Register reg) { push(ImmTag(JSVAL_TYPE_TO_TAG(type))); diff --git a/js/src/jit/arm64/Assembler-arm64.cpp b/js/src/jit/arm64/Assembler-arm64.cpp index 360abdf3c9e7..3beda7479e4d 100644 --- a/js/src/jit/arm64/Assembler-arm64.cpp +++ b/js/src/jit/arm64/Assembler-arm64.cpp @@ -564,14 +564,12 @@ TraceDataRelocations(JSTracer* trc, uint8_t* buffer, CompactBufferReader& reader // All pointers on AArch64 will have the top bits cleared. // If those bits are not cleared, this must be a Value. if (literal >> JSVAL_TAG_SHIFT) { - jsval_layout layout; - layout.asBits = literal; - Value v = IMPL_TO_JSVAL(layout); + Value v = Value::fromRawBits(literal); TraceManuallyBarrieredEdge(trc, &v, "ion-masm-value"); - if (*literalAddr != JSVAL_TO_IMPL(v).asBits) { + if (*literalAddr != v.asRawBits()) { // Only update the code if the value changed, because the code // is not writable if we're not moving objects. - *literalAddr = JSVAL_TO_IMPL(v).asBits; + *literalAddr = v.asRawBits(); } // TODO: When we can, flush caches here if a pointer was moved. diff --git a/js/src/jit/arm64/MacroAssembler-arm64.h b/js/src/jit/arm64/MacroAssembler-arm64.h index 4eaffbbe33e0..e9cb706b03c8 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.h +++ b/js/src/jit/arm64/MacroAssembler-arm64.h @@ -306,9 +306,8 @@ class MacroAssemblerCompat : public vixl::MacroAssembler void pushValue(const Value& val) { vixl::UseScratchRegisterScope temps(this); const Register scratch = temps.AcquireX().asUnsized(); - jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) { - BufferOffset load = movePatchablePtr(ImmPtr((void*)jv.asBits), scratch); + BufferOffset load = movePatchablePtr(ImmPtr(val.bitsAsPunboxPointer()), scratch); writeDataRelocation(val, load); push(scratch); } else { @@ -351,7 +350,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler } void moveValue(const Value& val, Register dest) { if (val.isMarkable()) { - BufferOffset load = movePatchablePtr(ImmPtr((void*)val.asRawBits()), dest); + BufferOffset load = movePatchablePtr(ImmPtr(val.bitsAsPunboxPointer()), dest); writeDataRelocation(val, load); } else { movePtr(ImmWord(val.asRawBits()), dest); @@ -1849,7 +1848,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler } void writeDataRelocation(const Value& val, BufferOffset load) { if (val.isMarkable()) { - gc::Cell* cell = reinterpret_cast(val.toGCThing()); + gc::Cell* cell = val.toMarkablePointer(); if (cell && gc::IsInsideNursery(cell)) embedsNurseryPointers_ = true; dataRelocations_.writeUnsigned(load.getOffset()); diff --git a/js/src/jit/mips32/MacroAssembler-mips32.cpp b/js/src/jit/mips32/MacroAssembler-mips32.cpp index 2983ed1943b3..4ba74a32bcc4 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -1521,18 +1521,16 @@ MacroAssemblerMIPSCompat::extractTag(const BaseIndex& address, Register scratch) uint32_t MacroAssemblerMIPSCompat::getType(const Value& val) { - jsval_layout jv = JSVAL_TO_IMPL(val); - return jv.s.tag; + return val.toNunboxTag(); } void MacroAssemblerMIPSCompat::moveData(const Value& val, Register data) { - jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) - ma_li(data, ImmGCPtr(reinterpret_cast(val.toGCThing()))); + ma_li(data, ImmGCPtr(val.toMarkablePointer())); else - ma_li(data, Imm32(jv.s.payload.i32)); + ma_li(data, Imm32(val.toNunboxPayload())); } void diff --git a/js/src/jit/mips32/MacroAssembler-mips32.h b/js/src/jit/mips32/MacroAssembler-mips32.h index 11e76c505b75..bc83357c5426 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.h +++ b/js/src/jit/mips32/MacroAssembler-mips32.h @@ -479,12 +479,11 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS void pushValue(ValueOperand val); void popValue(ValueOperand val); void pushValue(const Value& val) { - jsval_layout jv = JSVAL_TO_IMPL(val); - push(Imm32(jv.s.tag)); + push(Imm32(val.toNunboxTag())); if (val.isMarkable()) - push(ImmGCPtr(reinterpret_cast(val.toGCThing()))); + push(ImmGCPtr(val.toMarkablePointer())); else - push(Imm32(jv.s.payload.i32)); + push(Imm32(val.toNunboxPayload())); } void pushValue(JSValueType type, Register reg) { push(ImmTag(JSVAL_TYPE_TO_TAG(type))); diff --git a/js/src/jit/mips64/Assembler-mips64.cpp b/js/src/jit/mips64/Assembler-mips64.cpp index ab25805c054f..0c88991f3c54 100644 --- a/js/src/jit/mips64/Assembler-mips64.cpp +++ b/js/src/jit/mips64/Assembler-mips64.cpp @@ -175,11 +175,9 @@ TraceOneDataRelocation(JSTracer* trc, Instruction* inst) // are not cleared, this must be a Value. uintptr_t word = reinterpret_cast(ptr); if (word >> JSVAL_TAG_SHIFT) { - jsval_layout layout; - layout.asBits = word; - Value v = IMPL_TO_JSVAL(layout); + Value v = Value::fromRawBits(word); TraceManuallyBarrieredEdge(trc, &v, "ion-masm-value"); - ptr = (void*)JSVAL_TO_IMPL(v).asBits; + ptr = v.bitsAsPunboxPointer(); } else { // No barrier needed since these are constants. TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast(&ptr), diff --git a/js/src/jit/mips64/MacroAssembler-mips64.cpp b/js/src/jit/mips64/MacroAssembler-mips64.cpp index 6d10864355c6..5161c4741717 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.cpp +++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp @@ -1746,9 +1746,8 @@ MacroAssemblerMIPS64Compat::extractTag(const BaseIndex& address, Register scratc void MacroAssemblerMIPS64Compat::moveValue(const Value& val, Register dest) { - jsval_layout jv = JSVAL_TO_IMPL(val); writeDataRelocation(val); - movWithPatch(ImmWord(jv.asBits), dest); + movWithPatch(ImmWord(val.asRawBits()), dest); } void @@ -1886,12 +1885,11 @@ MacroAssemblerMIPS64Compat::storeValue(JSValueType type, Register reg, Address d void MacroAssemblerMIPS64Compat::storeValue(const Value& val, Address dest) { - jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) { writeDataRelocation(val); - movWithPatch(ImmWord(jv.asBits), SecondScratchReg); + movWithPatch(ImmWord(val.asRawBits()), SecondScratchReg); } else { - ma_li(SecondScratchReg, ImmWord(jv.asBits)); + ma_li(SecondScratchReg, ImmWord(val.asRawBits())); } storePtr(SecondScratchReg, Address(dest.base, dest.offset)); } diff --git a/js/src/jit/mips64/MacroAssembler-mips64.h b/js/src/jit/mips64/MacroAssembler-mips64.h index 9a44debc0e64..b5b12afb506a 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.h +++ b/js/src/jit/mips64/MacroAssembler-mips64.h @@ -222,7 +222,7 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64 void writeDataRelocation(const Value& val) { if (val.isMarkable()) { - gc::Cell* cell = reinterpret_cast(val.toGCThing()); + gc::Cell* cell = val.toMarkablePointer(); if (cell && gc::IsInsideNursery(cell)) embedsNurseryPointers_ = true; dataRelocations_.writeUnsigned(currentOffset()); diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index 0213ba6829d8..31a55bd9c925 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -59,7 +59,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared ///////////////////////////////////////////////////////////////// void writeDataRelocation(const Value& val) { if (val.isMarkable()) { - gc::Cell* cell = reinterpret_cast(val.toGCThing()); + gc::Cell* cell = val.toMarkablePointer(); if (cell && gc::IsInsideNursery(cell)) embedsNurseryPointers_ = true; dataRelocations_.writeUnsigned(masm.currentOffset()); @@ -132,12 +132,11 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared template void storeValue(const Value& val, const T& dest) { ScratchRegisterScope scratch(asMasm()); - jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) { - movWithPatch(ImmWord(jv.asBits), scratch); + movWithPatch(ImmWord(val.asRawBits()), scratch); writeDataRelocation(val); } else { - mov(ImmWord(jv.asBits), scratch); + mov(ImmWord(val.asRawBits()), scratch); } movq(scratch, Operand(dest)); } @@ -172,14 +171,13 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared pop(val.valueReg()); } void pushValue(const Value& val) { - jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) { ScratchRegisterScope scratch(asMasm()); - movWithPatch(ImmWord(jv.asBits), scratch); + movWithPatch(ImmWord(val.asRawBits()), scratch); writeDataRelocation(val); push(scratch); } else { - push(ImmWord(jv.asBits)); + push(ImmWord(val.asRawBits())); } } void pushValue(JSValueType type, Register reg) { @@ -192,8 +190,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared } void moveValue(const Value& val, Register dest) { - jsval_layout jv = JSVAL_TO_IMPL(val); - movWithPatch(ImmWord(jv.asBits), dest); + movWithPatch(ImmWord(val.asRawBits()), dest); writeDataRelocation(val); } void moveValue(const Value& src, const ValueOperand& dest) { diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.cpp b/js/src/jit/x86-shared/Assembler-x86-shared.cpp index e3eb883e2e0f..8d761c1380c2 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.cpp +++ b/js/src/jit/x86-shared/Assembler-x86-shared.cpp @@ -58,14 +58,12 @@ TraceDataRelocations(JSTracer* trc, uint8_t* buffer, CompactBufferReader& reader // are not cleared, this must be a Value. uintptr_t word = reinterpret_cast(ptr); if (word >> JSVAL_TAG_SHIFT) { - jsval_layout layout; - layout.asBits = word; - Value v = IMPL_TO_JSVAL(layout); + Value v = Value::fromRawBits(word); TraceManuallyBarrieredEdge(trc, &v, "jit-masm-value"); - if (word != JSVAL_TO_IMPL(v).asBits) { + if (word != v.asRawBits()) { // Only update the code if the Value changed, because the code // is not writable if we're not moving objects. - X86Encoding::SetPointer(buffer + offset, (void*)JSVAL_TO_IMPL(v).asBits); + X86Encoding::SetPointer(buffer + offset, v.bitsAsPunboxPointer()); } continue; } diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index 538b70bfc573..d6a0a2d5642c 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -503,24 +503,23 @@ MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, const Value& rhs, Label* label) { MOZ_ASSERT(cond == Equal || cond == NotEqual); - jsval_layout jv = JSVAL_TO_IMPL(rhs); if (rhs.isMarkable()) - cmpPtr(lhs.payloadReg(), ImmGCPtr(reinterpret_cast(rhs.toGCThing()))); + cmpPtr(lhs.payloadReg(), ImmGCPtr(rhs.toMarkablePointer())); else - cmpPtr(lhs.payloadReg(), ImmWord(jv.s.payload.i32)); + cmpPtr(lhs.payloadReg(), ImmWord(rhs.toNunboxPayload())); if (cond == Equal) { Label done; j(NotEqual, &done); { - cmp32(lhs.typeReg(), Imm32(jv.s.tag)); + cmp32(lhs.typeReg(), Imm32(rhs.toNunboxTag())); j(Equal, label); } bind(&done); } else { j(NotEqual, label); - cmp32(lhs.typeReg(), Imm32(jv.s.tag)); + cmp32(lhs.typeReg(), Imm32(rhs.toNunboxTag())); j(NotEqual, label); } } diff --git a/js/src/jit/x86/MacroAssembler-x86.h b/js/src/jit/x86/MacroAssembler-x86.h index ed42ae4254ce..93a8f8937260 100644 --- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -93,12 +93,11 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared return ToType(Operand(base)).toAddress(); } void moveValue(const Value& val, Register type, Register data) { - jsval_layout jv = JSVAL_TO_IMPL(val); - movl(Imm32(jv.s.tag), type); + movl(Imm32(val.toNunboxTag()), type); if (val.isMarkable()) - movl(ImmGCPtr(reinterpret_cast(val.toGCThing())), data); + movl(ImmGCPtr(val.toMarkablePointer()), data); else - movl(Imm32(jv.s.payload.i32), data); + movl(Imm32(val.toNunboxPayload()), data); } void moveValue(const Value& val, const ValueOperand& dest) { moveValue(val, dest.typeReg(), dest.payloadReg()); @@ -143,8 +142,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared } template void storeValue(const Value& val, const T& dest) { - jsval_layout jv = JSVAL_TO_IMPL(val); - storeTypeTag(ImmTag(jv.s.tag), Operand(dest)); + storeTypeTag(ImmTag(val.toNunboxTag()), Operand(dest)); storePayload(val, Operand(dest)); } void storeValue(ValueOperand val, BaseIndex dest) { @@ -214,12 +212,11 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared pop(val.typeReg()); } void pushValue(const Value& val) { - jsval_layout jv = JSVAL_TO_IMPL(val); - push(Imm32(jv.s.tag)); + push(Imm32(val.toNunboxTag())); if (val.isMarkable()) - push(ImmGCPtr(reinterpret_cast(val.toGCThing()))); + push(ImmGCPtr(val.toMarkablePointer())); else - push(Imm32(jv.s.payload.i32)); + push(Imm32(val.toNunboxPayload())); } void pushValue(JSValueType type, Register reg) { push(ImmTag(JSVAL_TYPE_TO_TAG(type))); @@ -238,11 +235,10 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared pop(dest.high); } void storePayload(const Value& val, Operand dest) { - jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) - movl(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), ToPayload(dest)); + movl(ImmGCPtr(val.toMarkablePointer()), ToPayload(dest)); else - movl(Imm32(jv.s.payload.i32), ToPayload(dest)); + movl(Imm32(val.toNunboxPayload()), ToPayload(dest)); } void storePayload(Register src, Operand dest) { movl(src, ToPayload(dest)); From 45ce3de0fff562c72ac912850eb8888bdcd83ba0 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:01 +0900 Subject: [PATCH 49/80] Bug 1304191 - Part 8.1: Remove JS::Value::setUnmarkedPtr and JS::Value::toUnmarkedPtr. r=jwalden --- js/public/Value.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 563171fdbf23..476ebcbeae2d 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -916,20 +916,6 @@ class Value return toTag() == JSVAL_TAG_PRIVATE_GCTHING; } - /* - * An unmarked value is just a void* cast as a Value. Thus, the Value is - * not safe for GC and must not be marked. This API avoids raw casts - * and the ensuing strict-aliasing warnings. - */ - - void setUnmarkedPtr(void* ptr) { - data.asPtr = ptr; - } - - void* toUnmarkedPtr() const { - return data.asPtr; - } - const size_t* payloadWord() const { #if defined(JS_NUNBOX32) return &data.s.payload.word; From b6f7a1d6dd379535d0ae7f21122720dfa22f649a Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:01 +0900 Subject: [PATCH 50/80] Bug 1304191 - Part 9: Add Value::fromDouble and use it in CanonicalizedDoubleValue. r=jwalden --- js/public/Value.h | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 476ebcbeae2d..1a8d0a0dcf41 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -944,6 +944,7 @@ class Value private: #if defined(JS_VALUE_IS_CONSTEXPR) explicit JS_VALUE_CONSTEXPR Value(uint64_t asBits) : data({ .asBits = asBits }) {} + explicit JS_VALUE_CONSTEXPR Value(double d) : data({ .asDouble = d }) {} MOZ_IMPLICIT JS_VALUE_CONSTEXPR Value(const jsval_layout& layout) : data(layout) {} #endif @@ -990,6 +991,17 @@ class Value fromInt32(int32_t i) { return fromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i)); } + + static JS_VALUE_CONSTEXPR Value + fromDouble(double d) { +#if defined(JS_VALUE_IS_CONSTEXPR) + return Value(d); +#else + Value v; + v.data.asDouble = d; + return v; +#endif + } } JS_HAZ_GC_POINTER; inline bool @@ -1042,24 +1054,9 @@ DoubleValue(double dbl) static inline JS_VALUE_CONSTEXPR Value CanonicalizedDoubleValue(double d) { - /* - * This is a manually inlined version of: - * d = JS_CANONICALIZE_NAN(d); - * return IMPL_TO_JSVAL(DOUBLE_TO_JSVAL_IMPL(d)); - * because GCC from XCode 3.1.4 miscompiles the above code. - */ -#if defined(JS_VALUE_IS_CONSTEXPR) - return IMPL_TO_JSVAL(MOZ_UNLIKELY(mozilla::IsNaN(d)) - ? (jsval_layout) { .asBits = 0x7FF8000000000000LL } - : (jsval_layout) { .asDouble = d }); -#else - jsval_layout l; - if (MOZ_UNLIKELY(d != d)) - l.asBits = 0x7FF8000000000000LL; - else - l.asDouble = d; - return IMPL_TO_JSVAL(l); -#endif + return MOZ_UNLIKELY(mozilla::IsNaN(d)) + ? Value::fromRawBits(0x7FF8000000000000ULL) + : Value::fromDouble(d); } static inline Value From 934d8428a62094cab6a825249c639736e65bef07 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:01 +0900 Subject: [PATCH 51/80] Bug 1304191 - Part 10: Remove JSVAL_TO_IMPL and IMPL_TO_JSVAL. r=jwalden --- js/public/Value.h | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 1a8d0a0dcf41..267898dfa134 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -354,9 +354,6 @@ JS_STATIC_ASSERT(sizeof(jsval_layout) == 8); # define JS_VALUE_CONSTEXPR_VAR const #endif -static inline jsval_layout JSVAL_TO_IMPL(const JS::Value& v); -static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(const jsval_layout& l); - namespace JS { static inline JS_VALUE_CONSTEXPR JS::Value UndefinedValue(); @@ -945,7 +942,6 @@ class Value #if defined(JS_VALUE_IS_CONSTEXPR) explicit JS_VALUE_CONSTEXPR Value(uint64_t asBits) : data({ .asBits = asBits }) {} explicit JS_VALUE_CONSTEXPR Value(double d) : data({ .asDouble = d }) {} - MOZ_IMPLICIT JS_VALUE_CONSTEXPR Value(const jsval_layout& layout) : data(layout) {} #endif void staticAssertions() { @@ -955,8 +951,6 @@ class Value JS_STATIC_ASSERT(sizeof(Value) == 8); } - friend jsval_layout (::JSVAL_TO_IMPL)(const Value&); - friend Value JS_VALUE_CONSTEXPR (::IMPL_TO_JSVAL)(const jsval_layout& l); friend Value JS_VALUE_CONSTEXPR (JS::UndefinedValue)(); public: @@ -1521,24 +1515,6 @@ template struct BoolDefaultAdaptor { static bool defaultValue( } // namespace js -inline jsval_layout -JSVAL_TO_IMPL(const JS::Value& v) -{ - return v.data; -} - -inline JS_VALUE_CONSTEXPR JS::Value -IMPL_TO_JSVAL(const jsval_layout& l) -{ -#if defined(JS_VALUE_IS_CONSTEXPR) - return JS::Value(l); -#else - JS::Value v; - v.data = l; - return v; -#endif -} - namespace JS { #ifdef JS_DEBUG From 4777efc768036af8a21151a819cd1220f8598d7a Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:01 +0900 Subject: [PATCH 52/80] Bug 1304191 - Part 11: Fold jsval_layout into JS::Value. r=jwalden --- js/public/Value.h | 217 ++++++++++++++++++++-------------------------- 1 file changed, 95 insertions(+), 122 deletions(-) diff --git a/js/public/Value.h b/js/public/Value.h index 267898dfa134..ad3708dda04a 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -33,7 +33,7 @@ namespace JS { class Value; } # define JSVAL_TAG_SHIFT 47 #endif -// Use enums so that printing a jsval_layout in the debugger shows nice +// Use enums so that printing a JS::Value in the debugger shows nice // symbolic type tags. #if defined(_MSC_VER) @@ -219,104 +219,6 @@ typedef enum JSWhyMagic JS_WHY_MAGIC_COUNT } JSWhyMagic; -#if MOZ_LITTLE_ENDIAN -# if defined(JS_NUNBOX32) -union MOZ_NON_PARAM alignas(8) jsval_layout -{ - uint64_t asBits; - struct { - union { - int32_t i32; - uint32_t u32; - uint32_t boo; // Don't use |bool| -- it must be four bytes. - JSString* str; - JS::Symbol* sym; - JSObject* obj; - js::gc::Cell* cell; - void* ptr; - JSWhyMagic why; - size_t word; - uintptr_t uintptr; - } payload; - JSValueTag tag; - } s; - double asDouble; - void* asPtr; -}; -# elif defined(JS_PUNBOX64) -union MOZ_NON_PARAM alignas(8) jsval_layout -{ - uint64_t asBits; -#if !defined(_WIN64) - /* MSVC does not pack these correctly :-( */ - struct { - uint64_t payload47 : 47; - JSValueTag tag : 17; - } debugView; -#endif - struct { - union { - int32_t i32; - uint32_t u32; - JSWhyMagic why; - } payload; - } s; - double asDouble; - void* asPtr; - size_t asWord; - uintptr_t asUIntPtr; -}; -# endif /* JS_PUNBOX64 */ -#else /* MOZ_LITTLE_ENDIAN */ -# if defined(JS_NUNBOX32) -union MOZ_NON_PARAM alignas(8) jsval_layout -{ - uint64_t asBits; - struct { - JSValueTag tag; - union { - int32_t i32; - uint32_t u32; - uint32_t boo; // Don't use |bool| -- it must be four bytes. - JSString* str; - JS::Symbol* sym; - JSObject* obj; - js::gc::Cell* cell; - void* ptr; - JSWhyMagic why; - size_t word; - uintptr_t uintptr; - } payload; - } s; - double asDouble; - void* asPtr; -}; -# elif defined(JS_PUNBOX64) -union MOZ_NON_PARAM alignas(8) jsval_layout -{ - uint64_t asBits; - struct { - JSValueTag tag : 17; - uint64_t payload47 : 47; - } debugView; - struct { - uint32_t padding; - union { - int32_t i32; - uint32_t u32; - JSWhyMagic why; - } payload; - } s; - double asDouble; - void* asPtr; - size_t asWord; - uintptr_t asUIntPtr; -}; -# endif /* JS_PUNBOX64 */ -#endif /* MOZ_LITTLE_ENDIAN */ - -JS_STATIC_ASSERT(sizeof(jsval_layout) == 8); - /* * For codesize purposes on some platforms, it's important that the * compiler know that JS::Values constructed from constant values can be @@ -417,7 +319,7 @@ CanonicalizeNaN(double d) * 32-bit user code should avoid copying jsval/JS::Value as much as possible, * preferring to pass by const Value&. */ -class Value +class MOZ_NON_PARAM alignas(8) Value { public: #if defined(JS_NUNBOX32) @@ -936,7 +838,97 @@ class Value private: #endif - jsval_layout data; +#if MOZ_LITTLE_ENDIAN +# if defined(JS_NUNBOX32) + union { + uint64_t asBits; + struct { + union { + int32_t i32; + uint32_t u32; + uint32_t boo; // Don't use |bool| -- it must be four bytes. + JSString* str; + JS::Symbol* sym; + JSObject* obj; + js::gc::Cell* cell; + void* ptr; + JSWhyMagic why; + size_t word; + uintptr_t uintptr; + } payload; + JSValueTag tag; + } s; + double asDouble; + void* asPtr; + } data; +# elif defined(JS_PUNBOX64) + union { + uint64_t asBits; +#if !defined(_WIN64) + /* MSVC does not pack these correctly :-( */ + struct { + uint64_t payload47 : 47; + JSValueTag tag : 17; + } debugView; +#endif + struct { + union { + int32_t i32; + uint32_t u32; + JSWhyMagic why; + } payload; + } s; + double asDouble; + void* asPtr; + size_t asWord; + uintptr_t asUIntPtr; + } data; +# endif /* JS_PUNBOX64 */ +#else /* MOZ_LITTLE_ENDIAN */ +# if defined(JS_NUNBOX32) + union { + uint64_t asBits; + struct { + JSValueTag tag; + union { + int32_t i32; + uint32_t u32; + uint32_t boo; // Don't use |bool| -- it must be four bytes. + JSString* str; + JS::Symbol* sym; + JSObject* obj; + js::gc::Cell* cell; + void* ptr; + JSWhyMagic why; + size_t word; + uintptr_t uintptr; + } payload; + } s; + double asDouble; + void* asPtr; + } data; +# elif defined(JS_PUNBOX64) + union { + uint64_t asBits; + struct { + JSValueTag tag : 17; + uint64_t payload47 : 47; + } debugView; + struct { + uint32_t padding; + union { + int32_t i32; + uint32_t u32; + JSWhyMagic why; + } payload; + } s; + double asDouble; + void* asPtr; + size_t asWord; + uintptr_t asUIntPtr; + } data; +# endif /* JS_PUNBOX64 */ +#endif /* MOZ_LITTLE_ENDIAN */ private: #if defined(JS_VALUE_IS_CONSTEXPR) @@ -998,6 +990,8 @@ class Value } } JS_HAZ_GC_POINTER; +static_assert(sizeof(Value) == 8, "Value size must leave three tag bits, be a binary power, and is ubiquitously depended upon everywhere"); + inline bool IsOptimizedPlaceholderMagicValue(const Value& v) { @@ -1515,27 +1509,6 @@ template struct BoolDefaultAdaptor { static bool defaultValue( } // namespace js -namespace JS { - -#ifdef JS_DEBUG -namespace detail { - -struct ValueAlignmentTester { char c; JS::Value v; }; -static_assert(sizeof(ValueAlignmentTester) == 16, - "JS::Value must be 16-byte-aligned"); - -struct LayoutAlignmentTester { char c; jsval_layout l; }; -static_assert(sizeof(LayoutAlignmentTester) == 16, - "jsval_layout must be 16-byte-aligned"); - -} // namespace detail -#endif /* JS_DEBUG */ - -} // namespace JS - -static_assert(sizeof(jsval_layout) == sizeof(JS::Value), - "jsval_layout and JS::Value must have identical layouts"); - /************************************************************************/ namespace JS { From 72306c773523cb16a1f6bc10b4ab376dfd0e07ef Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 16:46:30 +0900 Subject: [PATCH 53/80] Bug 1308775 - Use Element.scrollBy with behavior: "instant" in auto scroll. r=felipe --- toolkit/content/browser-content.js | 11 +++++------ .../browser/browser_bug295977_autoscroll_overflow.js | 9 ++++++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/toolkit/content/browser-content.js b/toolkit/content/browser-content.js index fe8934143a92..a32efe02832c 100644 --- a/toolkit/content/browser-content.js +++ b/toolkit/content/browser-content.js @@ -215,12 +215,11 @@ var ClickEventHandler = { const kAutoscroll = 15; // defined in mozilla/layers/ScrollInputMethods.h Services.telemetry.getHistogramById("SCROLL_INPUT_METHODS").add(kAutoscroll); - if (this._scrollable instanceof content.Window) { - this._scrollable.scrollBy(actualScrollX, actualScrollY); - } else { // an element with overflow - this._scrollable.scrollLeft += actualScrollX; - this._scrollable.scrollTop += actualScrollY; - } + this._scrollable.scrollBy({ + left: actualScrollX, + top: actualScrollY, + behavior: "instant" + }); content.requestAnimationFrame(this.autoscrollLoop); }, diff --git a/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js b/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js index 59d113f8f604..6e82ed3bf796 100644 --- a/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js +++ b/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js @@ -79,7 +79,14 @@ add_task(function* ()
\ '}, {elem: 'r', expected: expectScrollNone, testwindow: true, middlemousepastepref: true}, - {elem: 's', expected: expectScrollNone, testwindow: true, middlemousepastepref: true} + {elem: 's', expected: expectScrollNone, testwindow: true, middlemousepastepref: true}, + {dataUri: 'data:text/html,\ +\ +
'}, + {elem: 't', expected: expectScrollBoth}, ]; for (let test of allTests) { From d639977b83abf8bd385dada53b5dcaf0f2555a71 Mon Sep 17 00:00:00 2001 From: Iris Hsiao Date: Tue, 18 Oct 2016 15:51:38 +0800 Subject: [PATCH 54/80] Backed out changeset c659503d33ce (bug 1309180) for wrong commit message --HG-- extra : rebase_source : 4a818eb56623eb0e5a67ebfc99e58c764d2baf01 --- js/src/jit/arm/Simulator-arm.cpp | 32 -------------------------------- js/src/jit/arm/Simulator-arm.h | 23 ----------------------- 2 files changed, 55 deletions(-) diff --git a/js/src/jit/arm/Simulator-arm.cpp b/js/src/jit/arm/Simulator-arm.cpp index b652f00e5ca8..43a2de559791 100644 --- a/js/src/jit/arm/Simulator-arm.cpp +++ b/js/src/jit/arm/Simulator-arm.cpp @@ -421,38 +421,6 @@ Simulator::Destroy(Simulator* sim) js_delete(sim); } -void -Simulator::disassemble(SimInstruction* instr, size_t n) -{ - disasm::NameConverter converter; - disasm::Disassembler dasm(converter); - disasm::EmbeddedVector buffer; - while (n-- > 0) { - dasm.InstructionDecode(buffer, - reinterpret_cast(instr)); - printf(" 0x%08x %s\n", uint32_t(instr), buffer.start()); - instr = reinterpret_cast(reinterpret_cast(instr) + 4); - } -} - -void -Simulator::disasm(SimInstruction* instr) -{ - disassemble(instr, 1); -} - -void -Simulator::disasm(SimInstruction* instr, size_t n) -{ - disassemble(instr, n); -} - -void -Simulator::disasm(SimInstruction* instr, size_t m, size_t n) -{ - disassemble(reinterpret_cast(reinterpret_cast(instr) - m * 4), n); -} - // The ArmDebugger class is used by the simulator while debugging simulated ARM // code. class ArmDebugger { diff --git a/js/src/jit/arm/Simulator-arm.h b/js/src/jit/arm/Simulator-arm.h index ce7e4cdeece4..7889a68e07c8 100644 --- a/js/src/jit/arm/Simulator-arm.h +++ b/js/src/jit/arm/Simulator-arm.h @@ -116,29 +116,6 @@ class Simulator return Simulator::Current()->stackLimit(); } - // Disassemble some instructions starting at instr and print them - // on stdout. Useful for working within GDB after a MOZ_CRASH(), - // among other things. - // - // Typical use within a crashed instruction decoding method is simply: - // - // call Simulator::disassemble(instr, 1) - // - // or use one of the (much) more convenient inline methods below. - static void disassemble(SimInstruction* instr, size_t n); - - // Disassemble one instruction. - // "call disasm(instr)" - void disasm(SimInstruction* instr); - - // Disassemble n instructions starting at instr. - // "call disasm(instr, 3)" - void disasm(SimInstruction* instr, size_t n); - - // Skip backwards m instructions before starting, then disassemble n instructions. - // "call disasm(instr, 3, 7)" - void disasm(SimInstruction* instr, size_t m, size_t n); - uintptr_t* addressOfStackLimit(); // Accessors for register state. Reading the pc value adheres to the ARM From ad4fb47b9569095547366c186dcd16923147faa4 Mon Sep 17 00:00:00 2001 From: Jonathan Hao Date: Tue, 18 Oct 2016 15:56:00 +0800 Subject: [PATCH 55/80] Bug 1264595 - Test whether auto-revocation is removed. r=karlt --- dom/media/mediasource/test/mochitest.ini | 2 + .../mediasource/test/test_AutoRevocation.html | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 dom/media/mediasource/test/test_AutoRevocation.html diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index 5e4196ce6eff..502da53d0a69 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -46,6 +46,8 @@ support-files = [test_AudioChange_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 +[test_AutoRevocation.html] +tags = firstpartyisolation [test_BufferedSeek.html] [test_BufferedSeek_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 diff --git a/dom/media/mediasource/test/test_AutoRevocation.html b/dom/media/mediasource/test/test_AutoRevocation.html new file mode 100644 index 000000000000..15474367ec3e --- /dev/null +++ b/dom/media/mediasource/test/test_AutoRevocation.html @@ -0,0 +1,40 @@ + + + + MSE: auto-revocation + + + + + +
+
+
+ + From 359856eb75dfb177f7d4376247c668b0442ec5d6 Mon Sep 17 00:00:00 2001 From: Andi-Bogdan Postelnicu Date: Mon, 17 Oct 2016 16:10:17 +0300 Subject: [PATCH 56/80] Bug 1308868 - Add ignore-initialization-check annotation to mErrorCode from U2FStatus. r=baku MozReview-Commit-ID: 1dIKcbbeStW --- dom/u2f/U2F.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/u2f/U2F.h b/dom/u2f/U2F.h index 23874c96f87f..9035ae2df297 100644 --- a/dom/u2f/U2F.h +++ b/dom/u2f/U2F.h @@ -191,7 +191,7 @@ private: uint16_t mCount; bool mIsStopped; nsString mResponse; - ErrorCode mErrorCode; + MOZ_INIT_OUTSIDE_CTOR ErrorCode mErrorCode; ReentrantMonitor mReentrantMonitor; }; From 08c0ed3b5c37bfaefadaa575f60ce683fc6e7feb Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Tue, 11 Oct 2016 12:18:03 +0200 Subject: [PATCH 57/80] Bug 1309178 - Add interactive disasm calls to ARM simulator. r=bbouvier --- js/src/jit/arm/Simulator-arm.cpp | 32 ++++++++++++++++++++++++++++++++ js/src/jit/arm/Simulator-arm.h | 23 +++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/js/src/jit/arm/Simulator-arm.cpp b/js/src/jit/arm/Simulator-arm.cpp index 43a2de559791..3714e0bf671c 100644 --- a/js/src/jit/arm/Simulator-arm.cpp +++ b/js/src/jit/arm/Simulator-arm.cpp @@ -421,6 +421,38 @@ Simulator::Destroy(Simulator* sim) js_delete(sim); } +void +Simulator::disassemble(SimInstruction* instr, size_t n) +{ + disasm::NameConverter converter; + disasm::Disassembler dasm(converter); + disasm::EmbeddedVector buffer; + while (n-- > 0) { + dasm.InstructionDecode(buffer, + reinterpret_cast(instr)); + printf(" 0x%08x %s\n", uint32_t(instr), buffer.start()); + instr = reinterpret_cast(reinterpret_cast(instr) + 4); + } +} + +void +Simulator::disasm(SimInstruction* instr) +{ + disassemble(instr, 1); +} + +void +Simulator::disasm(SimInstruction* instr, size_t n) +{ + disassemble(instr, n); +} + +void +Simulator::disasm(SimInstruction* instr, size_t m, size_t n) +{ + disassemble(reinterpret_cast(reinterpret_cast(instr)-m*4), n); +} + // The ArmDebugger class is used by the simulator while debugging simulated ARM // code. class ArmDebugger { diff --git a/js/src/jit/arm/Simulator-arm.h b/js/src/jit/arm/Simulator-arm.h index 7889a68e07c8..1c644cd3ad83 100644 --- a/js/src/jit/arm/Simulator-arm.h +++ b/js/src/jit/arm/Simulator-arm.h @@ -116,6 +116,29 @@ class Simulator return Simulator::Current()->stackLimit(); } + // Disassemble some instructions starting at instr and print them + // on stdout. Useful for working within GDB after a MOZ_CRASH(), + // among other things. + // + // Typical use within a crashed instruction decoding method is simply: + // + // call Simulator::disassemble(instr, 1) + // + // or use one of the more convenient inline methods below. + static void disassemble(SimInstruction* instr, size_t n); + + // Disassemble one instruction. + // "call disasm(instr)" + void disasm(SimInstruction* instr); + + // Disassemble n instructions starting at instr. + // "call disasm(instr, 3)" + void disasm(SimInstruction* instr, size_t n); + + // Skip backwards m instructions before starting, then disassemble n instructions. + // "call disasm(instr, 3, 7)" + void disasm(SimInstruction* instr, size_t m, size_t n); + uintptr_t* addressOfStackLimit(); // Accessors for register state. Reading the pc value adheres to the ARM From d4e1205058d4f54ae14c63f7371fa7791bf40c6b Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Mon, 17 Oct 2016 14:22:46 +0900 Subject: [PATCH 58/80] Bug 850043 - Part 1. Looking for better insertion point when using plain text editor. r=masayuki When typing character, current selection node might be anonymous DIV, not text node. So even if plain text, we might not get it. We should get text node correctly when using plain text editor. MozReview-Commit-ID: LmfYa7BqZnC --HG-- extra : rebase_source : d08c74c8cc5fdec4d19772f112e54a08b95afeb3 --- editor/libeditor/EditorBase.cpp | 9 +++++++++ editor/libeditor/EditorBase.h | 2 ++ editor/libeditor/TextEditor.cpp | 3 +++ 3 files changed, 14 insertions(+) diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 692d9e75c1fd..71e3d30fe2a3 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -2271,6 +2271,15 @@ EditorBase::ScrollSelectionIntoView(bool aScrollToAnchor) return NS_OK; } +void +EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, + int32_t& aOffset) +{ + nsCOMPtr node = do_QueryInterface(aNode); + FindBetterInsertionPoint(node, aOffset); + aNode = do_QueryInterface(node); +} + void EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, int32_t& aOffset) diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index 63b8fd97b44c..b807a6b3529f 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -922,6 +922,8 @@ public: * FindBetterInsertionPoint() tries to look for better insertion point which * is typically the nearest text node and offset in it. */ + void FindBetterInsertionPoint(nsCOMPtr& aNode, + int32_t& aOffset); void FindBetterInsertionPoint(nsCOMPtr& aNode, int32_t& aOffset); diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index 1e77931e551f..52707a902a0f 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -616,6 +616,9 @@ TextEditor::ExtendSelectionForDelete(Selection* aSelection, NS_ENSURE_SUCCESS(result, result); NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); + // node might be anonymous DIV, so we find better text node + FindBetterInsertionPoint(node, offset); + if (IsTextNode(node)) { nsCOMPtr charData = do_QueryInterface(node); if (charData) { From 167407b34f29447f3890298516eeef970c7b46cf Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Mon, 17 Oct 2016 14:24:47 +0900 Subject: [PATCH 59/80] Bug 850043 - Part 2. Add test. r=masayuki MozReview-Commit-ID: 9iY6udNCQdL --HG-- extra : rebase_source : 4d9ec835c619afa2fc2b8157118e31ee5ab9e0d0 --- editor/libeditor/tests/mochitest.ini | 1 + editor/libeditor/tests/test_bug850043.html | 64 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 editor/libeditor/tests/test_bug850043.html diff --git a/editor/libeditor/tests/mochitest.ini b/editor/libeditor/tests/mochitest.ini index 94e41e7eb980..fe667bad3038 100644 --- a/editor/libeditor/tests/mochitest.ini +++ b/editor/libeditor/tests/mochitest.ini @@ -147,6 +147,7 @@ subsuite = clipboard [test_bug795785.html] [test_bug796839.html] [test_bug832025.html] +[test_bug850043.html] [test_bug857487.html] [test_bug858918.html] [test_bug915962.html] diff --git a/editor/libeditor/tests/test_bug850043.html b/editor/libeditor/tests/test_bug850043.html new file mode 100644 index 000000000000..a110c15f994d --- /dev/null +++ b/editor/libeditor/tests/test_bug850043.html @@ -0,0 +1,64 @@ + + + + + Test for Bug 850043 + + + + + + + + +Mozilla Bug 850043 +
+ +
b邀󠄏辺󠄁
+
+ + +
+
+ + + From 4c192c39e88b37f2e868207abd3cc86088467d76 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 18:16:27 +0900 Subject: [PATCH 60/80] Backed out changeset 9d11e38b5fcf for bc test failure (bug 1308775) --- toolkit/content/browser-content.js | 11 ++++++----- .../browser/browser_bug295977_autoscroll_overflow.js | 9 +-------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/toolkit/content/browser-content.js b/toolkit/content/browser-content.js index a32efe02832c..fe8934143a92 100644 --- a/toolkit/content/browser-content.js +++ b/toolkit/content/browser-content.js @@ -215,11 +215,12 @@ var ClickEventHandler = { const kAutoscroll = 15; // defined in mozilla/layers/ScrollInputMethods.h Services.telemetry.getHistogramById("SCROLL_INPUT_METHODS").add(kAutoscroll); - this._scrollable.scrollBy({ - left: actualScrollX, - top: actualScrollY, - behavior: "instant" - }); + if (this._scrollable instanceof content.Window) { + this._scrollable.scrollBy(actualScrollX, actualScrollY); + } else { // an element with overflow + this._scrollable.scrollLeft += actualScrollX; + this._scrollable.scrollTop += actualScrollY; + } content.requestAnimationFrame(this.autoscrollLoop); }, diff --git a/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js b/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js index 6e82ed3bf796..59d113f8f604 100644 --- a/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js +++ b/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js @@ -79,14 +79,7 @@ add_task(function* ()
\ '}, {elem: 'r', expected: expectScrollNone, testwindow: true, middlemousepastepref: true}, - {elem: 's', expected: expectScrollNone, testwindow: true, middlemousepastepref: true}, - {dataUri: 'data:text/html,\ -\ -
'}, - {elem: 't', expected: expectScrollBoth}, + {elem: 's', expected: expectScrollNone, testwindow: true, middlemousepastepref: true} ]; for (let test of allTests) { From 5e4e74e85967eaf2a4c1d1c5842c2030e542d6d2 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 18 Oct 2016 12:37:23 +0200 Subject: [PATCH 61/80] Bug 1310973 - Fixing a comment in nsIMemoryReporter.idl, r=me --- xpcom/base/nsIMemoryReporter.idl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index ca710bfab562..9617877df690 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -17,8 +17,8 @@ interface nsISimpleEnumerator; /* * Memory reporters measure Firefox's memory usage. They are primarily used to * generate the about:memory page. You should read - * https://wiki.mozilla.org/Memory_Reporting before writing a memory - * reporter. + * https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Memory_reporting + * before writing a memory reporter. */ [scriptable, function, uuid(62ef0e1c-dbd6-11e3-aa75-3c970e9f4238)] From be96e28a0f415515a7b0cc0b8990af6ea3102e91 Mon Sep 17 00:00:00 2001 From: Andrew Osmond Date: Thu, 13 Oct 2016 08:35:55 -0400 Subject: [PATCH 62/80] Bug 1308289 - Hold strong reference to VectorImage when dispatching ProgressTracker::SyncNotifyProgress. r=tnikkel --- image/VectorImage.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp index ded69ba2fc67..8248ab07c70c 100644 --- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -153,7 +153,7 @@ public: // OnSVGDocumentParsed will release our owner's reference to us, so ensure // we stick around long enough to complete our work. - RefPtr kungFuDeathGroup(this); + RefPtr kungFuDeathGrip(this); mImage->OnSVGDocumentParsed(); } @@ -212,7 +212,7 @@ public: // OnSVGDocumentLoaded/OnSVGDocumentError will release our owner's reference // to us, so ensure we stick around long enough to complete our work. - RefPtr kungFuDeathGroup(this); + RefPtr kungFuDeathGrip(this); nsAutoString eventType; aEvent->GetType(eventType); @@ -536,8 +536,8 @@ VectorImage::RequestRefresh(const TimeStamp& aTime) mSVGDocumentWrapper->TickRefreshDriver(); if (mHasPendingInvalidation) { - SendInvalidationNotifications(); mHasPendingInvalidation = false; + SendInvalidationNotifications(); } } @@ -1140,6 +1140,10 @@ VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt) return rv; } + // ProgressTracker::SyncNotifyProgress may release us, so ensure we + // stick around long enough to complete our work. + RefPtr kungFuDeathGrip(this); + // Block page load until the document's ready. (We unblock it in // OnSVGDocumentLoaded or OnSVGDocumentError.) if (mProgressTracker) { @@ -1220,6 +1224,10 @@ VectorImage::OnSVGDocumentLoaded() // Start listening to our image for rendering updates. mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this); + // ProgressTracker::SyncNotifyProgress may release us, so ensure we + // stick around long enough to complete our work. + RefPtr kungFuDeathGrip(this); + // Tell *our* observers that we're done loading. if (mProgressTracker) { Progress progress = FLAG_SIZE_AVAILABLE | From cf2f92a099e79b0a2dc2dc83a188661c6641b814 Mon Sep 17 00:00:00 2001 From: Andi-Bogdan Postelnicu Date: Tue, 18 Oct 2016 12:57:57 +0300 Subject: [PATCH 63/80] Bug 1301333 - Add mark-initialisation-func annotation to functions Init and DoInitOnMainThread from GetUsageOp. r=baku MozReview-Commit-ID: KKUX4FE0pth --- dom/quota/ActorsParent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index 827721bb0c58..a581eb0e81be 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -1022,14 +1022,14 @@ class GetUsageOp final public: explicit GetUsageOp(const UsageRequestParams& aParams); - bool + MOZ_IS_CLASS_INIT bool Init(Quota* aQuota); private: ~GetUsageOp() { } - virtual nsresult + MOZ_IS_CLASS_INIT virtual nsresult DoInitOnMainThread() override; nsresult From 2a7c72118f7894293c2d863797761074f7caa8ae Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 18 Oct 2016 21:43:30 +0900 Subject: [PATCH 64/80] Bug 1306669 - Root an object that lives across ffi_call. r=jonco --- js/src/ctypes/CTypes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 601a9eee1f1a..0facd0009e5b 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -8465,7 +8465,7 @@ CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, Value* vp) Value valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE); MOZ_ASSERT(valType.isObject()); - JSObject* objCTypes = CType::GetGlobalCTypes(cx, &valType.toObject()); + RootedObject objCTypes(cx, CType::GetGlobalCTypes(cx, &valType.toObject())); if (!objCTypes) return false; From 5b993555180a0bcf78307ef0e5653b6931e6f84a Mon Sep 17 00:00:00 2001 From: Eugen Sawin Date: Tue, 4 Oct 2016 22:20:52 +0200 Subject: [PATCH 65/80] Bug 1307570 - [1.4] Add XZ Embedded support configuration, scripts and the initial library version. r=glandium --- config/external/moz.build | 3 + modules/xz-embedded/README.mozilla | 14 + modules/xz-embedded/moz.build | 31 + modules/xz-embedded/src/xz.h | 304 ++++++ modules/xz-embedded/src/xz_config.h | 124 +++ modules/xz-embedded/src/xz_crc32.c | 59 ++ modules/xz-embedded/src/xz_crc64.c | 50 + modules/xz-embedded/src/xz_dec_bcj.c | 574 +++++++++++ modules/xz-embedded/src/xz_dec_lzma2.c | 1175 +++++++++++++++++++++++ modules/xz-embedded/src/xz_dec_stream.c | 847 ++++++++++++++++ modules/xz-embedded/src/xz_lzma2.h | 204 ++++ modules/xz-embedded/src/xz_private.h | 156 +++ modules/xz-embedded/src/xz_stream.h | 62 ++ modules/xz-embedded/update.sh | 31 + mozglue/linker/moz.build | 4 + 15 files changed, 3638 insertions(+) create mode 100644 modules/xz-embedded/README.mozilla create mode 100644 modules/xz-embedded/moz.build create mode 100644 modules/xz-embedded/src/xz.h create mode 100644 modules/xz-embedded/src/xz_config.h create mode 100644 modules/xz-embedded/src/xz_crc32.c create mode 100644 modules/xz-embedded/src/xz_crc64.c create mode 100644 modules/xz-embedded/src/xz_dec_bcj.c create mode 100644 modules/xz-embedded/src/xz_dec_lzma2.c create mode 100644 modules/xz-embedded/src/xz_dec_stream.c create mode 100644 modules/xz-embedded/src/xz_lzma2.h create mode 100644 modules/xz-embedded/src/xz_private.h create mode 100644 modules/xz-embedded/src/xz_stream.h create mode 100755 modules/xz-embedded/update.sh diff --git a/config/external/moz.build b/config/external/moz.build index 37925af62dfd..029ff8504795 100644 --- a/config/external/moz.build +++ b/config/external/moz.build @@ -61,4 +61,7 @@ external_dirs += [ 'media/psshparser' ] +if CONFIG['MOZ_LINKER']: + external_dirs += ['modules/xz-embedded'] + DIRS += ['../../' + i for i in external_dirs] diff --git a/modules/xz-embedded/README.mozilla b/modules/xz-embedded/README.mozilla new file mode 100644 index 000000000000..d0532e1e9164 --- /dev/null +++ b/modules/xz-embedded/README.mozilla @@ -0,0 +1,14 @@ +This is the XZ Embedded decompression library from +http://tukaani.org/xz/embedded.html. + +Upstream code can be viewed at + http://git.tukaani.org/xz-embedded.git + +and cloned by + git clone http://git.tukaani.org/xz-embedded.git + +The in-tree copy is updated by running + sh update.sh +from within the modules/xz-embedded directory. + +Current version: [e75f4eb79165213a02d567940d344f5c2ff1be03]. diff --git a/modules/xz-embedded/moz.build b/modules/xz-embedded/moz.build new file mode 100644 index 000000000000..ea69382c8f75 --- /dev/null +++ b/modules/xz-embedded/moz.build @@ -0,0 +1,31 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS += [ + 'src/xz.h', +] + +UNIFIED_SOURCES += [ + 'src/xz_crc32.c', + 'src/xz_crc64.c', + 'src/xz_dec_bcj.c', + 'src/xz_dec_lzma2.c', + 'src/xz_dec_stream.c', +] + +ALLOW_COMPILER_WARNINGS = True + +if CONFIG['TARGET_CPU'] == 'arm': + if CONFIG['MOZ_THUMB2']: + DEFINES['XZ_DEC_ARMTHUMB'] = 1 + else: + DEFINES['XZ_DEC_ARM'] = 1 +elif CONFIG['TARGET_CPU'] == 'x86': + DEFINES['XZ_DEC_X86'] = 1 + +DEFINES['XZ_USE_CRC64'] = 1 + +Library('xz-embedded') diff --git a/modules/xz-embedded/src/xz.h b/modules/xz-embedded/src/xz.h new file mode 100644 index 000000000000..0a4b38d33c2c --- /dev/null +++ b/modules/xz-embedded/src/xz.h @@ -0,0 +1,304 @@ +/* + * XZ decompressor + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_H +#define XZ_H + +#ifdef __KERNEL__ +# include +# include +#else +# include +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* In Linux, this is used to make extern functions static when needed. */ +#ifndef XZ_EXTERN +# define XZ_EXTERN extern +#endif + +/** + * enum xz_mode - Operation mode + * + * @XZ_SINGLE: Single-call mode. This uses less RAM than + * than multi-call modes, because the LZMA2 + * dictionary doesn't need to be allocated as + * part of the decoder state. All required data + * structures are allocated at initialization, + * so xz_dec_run() cannot return XZ_MEM_ERROR. + * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2 + * dictionary buffer. All data structures are + * allocated at initialization, so xz_dec_run() + * cannot return XZ_MEM_ERROR. + * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is + * allocated once the required size has been + * parsed from the stream headers. If the + * allocation fails, xz_dec_run() will return + * XZ_MEM_ERROR. + * + * It is possible to enable support only for a subset of the above + * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC, + * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled + * with support for all operation modes, but the preboot code may + * be built with fewer features to minimize code size. + */ +enum xz_mode { + XZ_SINGLE, + XZ_PREALLOC, + XZ_DYNALLOC +}; + +/** + * enum xz_ret - Return codes + * @XZ_OK: Everything is OK so far. More input or more + * output space is required to continue. This + * return code is possible only in multi-call mode + * (XZ_PREALLOC or XZ_DYNALLOC). + * @XZ_STREAM_END: Operation finished successfully. + * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding + * is still possible in multi-call mode by simply + * calling xz_dec_run() again. + * Note that this return value is used only if + * XZ_DEC_ANY_CHECK was defined at build time, + * which is not used in the kernel. Unsupported + * check types return XZ_OPTIONS_ERROR if + * XZ_DEC_ANY_CHECK was not defined at build time. + * @XZ_MEM_ERROR: Allocating memory failed. This return code is + * possible only if the decoder was initialized + * with XZ_DYNALLOC. The amount of memory that was + * tried to be allocated was no more than the + * dict_max argument given to xz_dec_init(). + * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than + * allowed by the dict_max argument given to + * xz_dec_init(). This return value is possible + * only in multi-call mode (XZ_PREALLOC or + * XZ_DYNALLOC); the single-call mode (XZ_SINGLE) + * ignores the dict_max argument. + * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic + * bytes). + * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested + * compression options. In the decoder this means + * that the header CRC32 matches, but the header + * itself specifies something that we don't support. + * @XZ_DATA_ERROR: Compressed data is corrupt. + * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly + * different between multi-call and single-call + * mode; more information below. + * + * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls + * to XZ code cannot consume any input and cannot produce any new output. + * This happens when there is no new input available, or the output buffer + * is full while at least one output byte is still pending. Assuming your + * code is not buggy, you can get this error only when decoding a compressed + * stream that is truncated or otherwise corrupt. + * + * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer + * is too small or the compressed input is corrupt in a way that makes the + * decoder produce more output than the caller expected. When it is + * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR + * is used instead of XZ_BUF_ERROR. + */ +enum xz_ret { + XZ_OK, + XZ_STREAM_END, + XZ_UNSUPPORTED_CHECK, + XZ_MEM_ERROR, + XZ_MEMLIMIT_ERROR, + XZ_FORMAT_ERROR, + XZ_OPTIONS_ERROR, + XZ_DATA_ERROR, + XZ_BUF_ERROR +}; + +/** + * struct xz_buf - Passing input and output buffers to XZ code + * @in: Beginning of the input buffer. This may be NULL if and only + * if in_pos is equal to in_size. + * @in_pos: Current position in the input buffer. This must not exceed + * in_size. + * @in_size: Size of the input buffer + * @out: Beginning of the output buffer. This may be NULL if and only + * if out_pos is equal to out_size. + * @out_pos: Current position in the output buffer. This must not exceed + * out_size. + * @out_size: Size of the output buffer + * + * Only the contents of the output buffer from out[out_pos] onward, and + * the variables in_pos and out_pos are modified by the XZ code. + */ +struct xz_buf { + const uint8_t *in; + size_t in_pos; + size_t in_size; + + uint8_t *out; + size_t out_pos; + size_t out_size; +}; + +/** + * struct xz_dec - Opaque type to hold the XZ decoder state + */ +struct xz_dec; + +/** + * xz_dec_init() - Allocate and initialize a XZ decoder state + * @mode: Operation mode + * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for + * multi-call decoding. This is ignored in single-call mode + * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes + * or 2^n + 2^(n-1) bytes (the latter sizes are less common + * in practice), so other values for dict_max don't make sense. + * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB, + * 512 KiB, and 1 MiB are probably the only reasonable values, + * except for kernel and initramfs images where a bigger + * dictionary can be fine and useful. + * + * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at + * once. The caller must provide enough output space or the decoding will + * fail. The output space is used as the dictionary buffer, which is why + * there is no need to allocate the dictionary as part of the decoder's + * internal state. + * + * Because the output buffer is used as the workspace, streams encoded using + * a big dictionary are not a problem in single-call mode. It is enough that + * the output buffer is big enough to hold the actual uncompressed data; it + * can be smaller than the dictionary size stored in the stream headers. + * + * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes + * of memory is preallocated for the LZMA2 dictionary. This way there is no + * risk that xz_dec_run() could run out of memory, since xz_dec_run() will + * never allocate any memory. Instead, if the preallocated dictionary is too + * small for decoding the given input stream, xz_dec_run() will return + * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be + * decoded to avoid allocating excessive amount of memory for the dictionary. + * + * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC): + * dict_max specifies the maximum allowed dictionary size that xz_dec_run() + * may allocate once it has parsed the dictionary size from the stream + * headers. This way excessive allocations can be avoided while still + * limiting the maximum memory usage to a sane value to prevent running the + * system out of memory when decompressing streams from untrusted sources. + * + * On success, xz_dec_init() returns a pointer to struct xz_dec, which is + * ready to be used with xz_dec_run(). If memory allocation fails, + * xz_dec_init() returns NULL. + */ +XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max); + +/** + * xz_dec_run() - Run the XZ decoder + * @s: Decoder state allocated using xz_dec_init() + * @b: Input and output buffers + * + * The possible return values depend on build options and operation mode. + * See enum xz_ret for details. + * + * Note that if an error occurs in single-call mode (return value is not + * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the + * contents of the output buffer from b->out[b->out_pos] onward are + * undefined. This is true even after XZ_BUF_ERROR, because with some filter + * chains, there may be a second pass over the output buffer, and this pass + * cannot be properly done if the output buffer is truncated. Thus, you + * cannot give the single-call decoder a too small buffer and then expect to + * get that amount valid data from the beginning of the stream. You must use + * the multi-call decoder if you don't want to uncompress the whole stream. + */ +XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b); + +/** + * xz_dec_reset() - Reset an already allocated decoder state + * @s: Decoder state allocated using xz_dec_init() + * + * This function can be used to reset the multi-call decoder state without + * freeing and reallocating memory with xz_dec_end() and xz_dec_init(). + * + * In single-call mode, xz_dec_reset() is always called in the beginning of + * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in + * multi-call mode. + */ +XZ_EXTERN void xz_dec_reset(struct xz_dec *s); + +/** + * xz_dec_end() - Free the memory allocated for the decoder state + * @s: Decoder state allocated using xz_dec_init(). If s is NULL, + * this function does nothing. + */ +XZ_EXTERN void xz_dec_end(struct xz_dec *s); + +/* + * Standalone build (userspace build or in-kernel build for boot time use) + * needs a CRC32 implementation. For normal in-kernel use, kernel's own + * CRC32 module is used instead, and users of this module don't need to + * care about the functions below. + */ +#ifndef XZ_INTERNAL_CRC32 +# ifdef __KERNEL__ +# define XZ_INTERNAL_CRC32 0 +# else +# define XZ_INTERNAL_CRC32 1 +# endif +#endif + +/* + * If CRC64 support has been enabled with XZ_USE_CRC64, a CRC64 + * implementation is needed too. + */ +#ifndef XZ_USE_CRC64 +# undef XZ_INTERNAL_CRC64 +# define XZ_INTERNAL_CRC64 0 +#endif +#ifndef XZ_INTERNAL_CRC64 +# ifdef __KERNEL__ +# error Using CRC64 in the kernel has not been implemented. +# else +# define XZ_INTERNAL_CRC64 1 +# endif +#endif + +#if XZ_INTERNAL_CRC32 +/* + * This must be called before any other xz_* function to initialize + * the CRC32 lookup table. + */ +XZ_EXTERN void xz_crc32_init(void); + +/* + * Update CRC32 value using the polynomial from IEEE-802.3. To start a new + * calculation, the third argument must be zero. To continue the calculation, + * the previously returned value is passed as the third argument. + */ +XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc); +#endif + +#if XZ_INTERNAL_CRC64 +/* + * This must be called before any other xz_* function (except xz_crc32_init()) + * to initialize the CRC64 lookup table. + */ +XZ_EXTERN void xz_crc64_init(void); + +/* + * Update CRC64 value using the polynomial from ECMA-182. To start a new + * calculation, the third argument must be zero. To continue the calculation, + * the previously returned value is passed as the third argument. + */ +XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/xz-embedded/src/xz_config.h b/modules/xz-embedded/src/xz_config.h new file mode 100644 index 000000000000..eb9dac1a4bda --- /dev/null +++ b/modules/xz-embedded/src/xz_config.h @@ -0,0 +1,124 @@ +/* + * Private includes and definitions for userspace use of XZ Embedded + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_CONFIG_H +#define XZ_CONFIG_H + +/* Uncomment to enable CRC64 support. */ +/* #define XZ_USE_CRC64 */ + +/* Uncomment as needed to enable BCJ filter decoders. */ +/* #define XZ_DEC_X86 */ +/* #define XZ_DEC_POWERPC */ +/* #define XZ_DEC_IA64 */ +/* #define XZ_DEC_ARM */ +/* #define XZ_DEC_ARMTHUMB */ +/* #define XZ_DEC_SPARC */ + +/* + * MSVC doesn't support modern C but XZ Embedded is mostly C89 + * so these are enough. + */ +#ifdef _MSC_VER +typedef unsigned char bool; +# define true 1 +# define false 0 +# define inline __inline +#else +# include +#endif + +#include +#include + +#include "xz.h" + +#define kmalloc(size, flags) malloc(size) +#define kfree(ptr) free(ptr) +#define vmalloc(size) malloc(size) +#define vfree(ptr) free(ptr) + +#define memeq(a, b, size) (memcmp(a, b, size) == 0) +#define memzero(buf, size) memset(buf, 0, size) + +#ifndef min +# define min(x, y) ((x) < (y) ? (x) : (y)) +#endif +#define min_t(type, x, y) min(x, y) + +/* + * Some functions have been marked with __always_inline to keep the + * performance reasonable even when the compiler is optimizing for + * small code size. You may be able to save a few bytes by #defining + * __always_inline to plain inline, but don't complain if the code + * becomes slow. + * + * NOTE: System headers on GNU/Linux may #define this macro already, + * so if you want to change it, you need to #undef it first. + */ +#ifndef __always_inline +# ifdef __GNUC__ +# define __always_inline \ + inline __attribute__((__always_inline__)) +# else +# define __always_inline inline +# endif +#endif + +/* Inline functions to access unaligned unsigned 32-bit integers */ +#ifndef get_unaligned_le32 +static inline uint32_t get_unaligned_le32(const uint8_t *buf) +{ + return (uint32_t)buf[0] + | ((uint32_t)buf[1] << 8) + | ((uint32_t)buf[2] << 16) + | ((uint32_t)buf[3] << 24); +} +#endif + +#ifndef get_unaligned_be32 +static inline uint32_t get_unaligned_be32(const uint8_t *buf) +{ + return (uint32_t)(buf[0] << 24) + | ((uint32_t)buf[1] << 16) + | ((uint32_t)buf[2] << 8) + | (uint32_t)buf[3]; +} +#endif + +#ifndef put_unaligned_le32 +static inline void put_unaligned_le32(uint32_t val, uint8_t *buf) +{ + buf[0] = (uint8_t)val; + buf[1] = (uint8_t)(val >> 8); + buf[2] = (uint8_t)(val >> 16); + buf[3] = (uint8_t)(val >> 24); +} +#endif + +#ifndef put_unaligned_be32 +static inline void put_unaligned_be32(uint32_t val, uint8_t *buf) +{ + buf[0] = (uint8_t)(val >> 24); + buf[1] = (uint8_t)(val >> 16); + buf[2] = (uint8_t)(val >> 8); + buf[3] = (uint8_t)val; +} +#endif + +/* + * Use get_unaligned_le32() also for aligned access for simplicity. On + * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr)) + * could save a few bytes in code size. + */ +#ifndef get_le32 +# define get_le32 get_unaligned_le32 +#endif + +#endif diff --git a/modules/xz-embedded/src/xz_crc32.c b/modules/xz-embedded/src/xz_crc32.c new file mode 100644 index 000000000000..34532d14fd4c --- /dev/null +++ b/modules/xz-embedded/src/xz_crc32.c @@ -0,0 +1,59 @@ +/* + * CRC32 using the polynomial from IEEE-802.3 + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +/* + * This is not the fastest implementation, but it is pretty compact. + * The fastest versions of xz_crc32() on modern CPUs without hardware + * accelerated CRC instruction are 3-5 times as fast as this version, + * but they are bigger and use more memory for the lookup table. + */ + +#include "xz_private.h" + +/* + * STATIC_RW_DATA is used in the pre-boot environment on some architectures. + * See for details. + */ +#ifndef STATIC_RW_DATA +# define STATIC_RW_DATA static +#endif + +STATIC_RW_DATA uint32_t xz_crc32_table[256]; + +XZ_EXTERN void xz_crc32_init(void) +{ + const uint32_t poly = 0xEDB88320; + + uint32_t i; + uint32_t j; + uint32_t r; + + for (i = 0; i < 256; ++i) { + r = i; + for (j = 0; j < 8; ++j) + r = (r >> 1) ^ (poly & ~((r & 1) - 1)); + + xz_crc32_table[i] = r; + } + + return; +} + +XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc) +{ + crc = ~crc; + + while (size != 0) { + crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8); + --size; + } + + return ~crc; +} diff --git a/modules/xz-embedded/src/xz_crc64.c b/modules/xz-embedded/src/xz_crc64.c new file mode 100644 index 000000000000..ca1caee899ae --- /dev/null +++ b/modules/xz-embedded/src/xz_crc64.c @@ -0,0 +1,50 @@ +/* + * CRC64 using the polynomial from ECMA-182 + * + * This file is similar to xz_crc32.c. See the comments there. + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" + +#ifndef STATIC_RW_DATA +# define STATIC_RW_DATA static +#endif + +STATIC_RW_DATA uint64_t xz_crc64_table[256]; + +XZ_EXTERN void xz_crc64_init(void) +{ + const uint64_t poly = 0xC96C5795D7870F42; + + uint32_t i; + uint32_t j; + uint64_t r; + + for (i = 0; i < 256; ++i) { + r = i; + for (j = 0; j < 8; ++j) + r = (r >> 1) ^ (poly & ~((r & 1) - 1)); + + xz_crc64_table[i] = r; + } + + return; +} + +XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc) +{ + crc = ~crc; + + while (size != 0) { + crc = xz_crc64_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8); + --size; + } + + return ~crc; +} diff --git a/modules/xz-embedded/src/xz_dec_bcj.c b/modules/xz-embedded/src/xz_dec_bcj.c new file mode 100644 index 000000000000..a768e6d28bbb --- /dev/null +++ b/modules/xz-embedded/src/xz_dec_bcj.c @@ -0,0 +1,574 @@ +/* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" + +/* + * The rest of the file is inside this ifdef. It makes things a little more + * convenient when building without support for any BCJ filters. + */ +#ifdef XZ_DEC_BCJ + +struct xz_dec_bcj { + /* Type of the BCJ filter being used */ + enum { + BCJ_X86 = 4, /* x86 or x86-64 */ + BCJ_POWERPC = 5, /* Big endian only */ + BCJ_IA64 = 6, /* Big or little endian */ + BCJ_ARM = 7, /* Little endian only */ + BCJ_ARMTHUMB = 8, /* Little endian only */ + BCJ_SPARC = 9 /* Big or little endian */ + } type; + + /* + * Return value of the next filter in the chain. We need to preserve + * this information across calls, because we must not call the next + * filter anymore once it has returned XZ_STREAM_END. + */ + enum xz_ret ret; + + /* True if we are operating in single-call mode. */ + bool single_call; + + /* + * Absolute position relative to the beginning of the uncompressed + * data (in a single .xz Block). We care only about the lowest 32 + * bits so this doesn't need to be uint64_t even with big files. + */ + uint32_t pos; + + /* x86 filter state */ + uint32_t x86_prev_mask; + + /* Temporary space to hold the variables from struct xz_buf */ + uint8_t *out; + size_t out_pos; + size_t out_size; + + struct { + /* Amount of already filtered data in the beginning of buf */ + size_t filtered; + + /* Total amount of data currently stored in buf */ + size_t size; + + /* + * Buffer to hold a mix of filtered and unfiltered data. This + * needs to be big enough to hold Alignment + 2 * Look-ahead: + * + * Type Alignment Look-ahead + * x86 1 4 + * PowerPC 4 0 + * IA-64 16 0 + * ARM 4 0 + * ARM-Thumb 2 2 + * SPARC 4 0 + */ + uint8_t buf[16]; + } temp; +}; + +#ifdef XZ_DEC_X86 +/* + * This is used to test the most significant byte of a memory address + * in an x86 instruction. + */ +static inline int bcj_x86_test_msbyte(uint8_t b) +{ + return b == 0x00 || b == 0xFF; +} + +static size_t bcj_x86(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + static const bool mask_to_allowed_status[8] + = { true, true, true, false, true, false, false, false }; + + static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 }; + + size_t i; + size_t prev_pos = (size_t)-1; + uint32_t prev_mask = s->x86_prev_mask; + uint32_t src; + uint32_t dest; + uint32_t j; + uint8_t b; + + if (size <= 4) + return 0; + + size -= 4; + for (i = 0; i < size; ++i) { + if ((buf[i] & 0xFE) != 0xE8) + continue; + + prev_pos = i - prev_pos; + if (prev_pos > 3) { + prev_mask = 0; + } else { + prev_mask = (prev_mask << (prev_pos - 1)) & 7; + if (prev_mask != 0) { + b = buf[i + 4 - mask_to_bit_num[prev_mask]]; + if (!mask_to_allowed_status[prev_mask] + || bcj_x86_test_msbyte(b)) { + prev_pos = i; + prev_mask = (prev_mask << 1) | 1; + continue; + } + } + } + + prev_pos = i; + + if (bcj_x86_test_msbyte(buf[i + 4])) { + src = get_unaligned_le32(buf + i + 1); + while (true) { + dest = src - (s->pos + (uint32_t)i + 5); + if (prev_mask == 0) + break; + + j = mask_to_bit_num[prev_mask] * 8; + b = (uint8_t)(dest >> (24 - j)); + if (!bcj_x86_test_msbyte(b)) + break; + + src = dest ^ (((uint32_t)1 << (32 - j)) - 1); + } + + dest &= 0x01FFFFFF; + dest |= (uint32_t)0 - (dest & 0x01000000); + put_unaligned_le32(dest, buf + i + 1); + i += 4; + } else { + prev_mask = (prev_mask << 1) | 1; + } + } + + prev_pos = i - prev_pos; + s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1); + return i; +} +#endif + +#ifdef XZ_DEC_POWERPC +static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t instr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = get_unaligned_be32(buf + i); + if ((instr & 0xFC000003) == 0x48000001) { + instr &= 0x03FFFFFC; + instr -= s->pos + (uint32_t)i; + instr &= 0x03FFFFFC; + instr |= 0x48000001; + put_unaligned_be32(instr, buf + i); + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_IA64 +static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + static const uint8_t branch_table[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 6, 6, 0, 0, 7, 7, + 4, 4, 0, 0, 4, 4, 0, 0 + }; + + /* + * The local variables take a little bit stack space, but it's less + * than what LZMA2 decoder takes, so it doesn't make sense to reduce + * stack usage here without doing that for the LZMA2 decoder too. + */ + + /* Loop counters */ + size_t i; + size_t j; + + /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */ + uint32_t slot; + + /* Bitwise offset of the instruction indicated by slot */ + uint32_t bit_pos; + + /* bit_pos split into byte and bit parts */ + uint32_t byte_pos; + uint32_t bit_res; + + /* Address part of an instruction */ + uint32_t addr; + + /* Mask used to detect which instructions to convert */ + uint32_t mask; + + /* 41-bit instruction stored somewhere in the lowest 48 bits */ + uint64_t instr; + + /* Instruction normalized with bit_res for easier manipulation */ + uint64_t norm; + + for (i = 0; i + 16 <= size; i += 16) { + mask = branch_table[buf[i] & 0x1F]; + for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) { + if (((mask >> slot) & 1) == 0) + continue; + + byte_pos = bit_pos >> 3; + bit_res = bit_pos & 7; + instr = 0; + for (j = 0; j < 6; ++j) + instr |= (uint64_t)(buf[i + j + byte_pos]) + << (8 * j); + + norm = instr >> bit_res; + + if (((norm >> 37) & 0x0F) == 0x05 + && ((norm >> 9) & 0x07) == 0) { + addr = (norm >> 13) & 0x0FFFFF; + addr |= ((uint32_t)(norm >> 36) & 1) << 20; + addr <<= 4; + addr -= s->pos + (uint32_t)i; + addr >>= 4; + + norm &= ~((uint64_t)0x8FFFFF << 13); + norm |= (uint64_t)(addr & 0x0FFFFF) << 13; + norm |= (uint64_t)(addr & 0x100000) + << (36 - 20); + + instr &= (1 << bit_res) - 1; + instr |= norm << bit_res; + + for (j = 0; j < 6; j++) + buf[i + j + byte_pos] + = (uint8_t)(instr >> (8 * j)); + } + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_ARM +static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + if (buf[i + 3] == 0xEB) { + addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) + | ((uint32_t)buf[i + 2] << 16); + addr <<= 2; + addr -= s->pos + (uint32_t)i + 8; + addr >>= 2; + buf[i] = (uint8_t)addr; + buf[i + 1] = (uint8_t)(addr >> 8); + buf[i + 2] = (uint8_t)(addr >> 16); + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_ARMTHUMB +static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 2) { + if ((buf[i + 1] & 0xF8) == 0xF0 + && (buf[i + 3] & 0xF8) == 0xF8) { + addr = (((uint32_t)buf[i + 1] & 0x07) << 19) + | ((uint32_t)buf[i] << 11) + | (((uint32_t)buf[i + 3] & 0x07) << 8) + | (uint32_t)buf[i + 2]; + addr <<= 1; + addr -= s->pos + (uint32_t)i + 4; + addr >>= 1; + buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07)); + buf[i] = (uint8_t)(addr >> 11); + buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07)); + buf[i + 2] = (uint8_t)addr; + i += 2; + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_SPARC +static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t instr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = get_unaligned_be32(buf + i); + if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) { + instr <<= 2; + instr -= s->pos + (uint32_t)i; + instr >>= 2; + instr = ((uint32_t)0x40000000 - (instr & 0x400000)) + | 0x40000000 | (instr & 0x3FFFFF); + put_unaligned_be32(instr, buf + i); + } + } + + return i; +} +#endif + +/* + * Apply the selected BCJ filter. Update *pos and s->pos to match the amount + * of data that got filtered. + * + * NOTE: This is implemented as a switch statement to avoid using function + * pointers, which could be problematic in the kernel boot code, which must + * avoid pointers to static data (at least on x86). + */ +static void bcj_apply(struct xz_dec_bcj *s, + uint8_t *buf, size_t *pos, size_t size) +{ + size_t filtered; + + buf += *pos; + size -= *pos; + + switch (s->type) { +#ifdef XZ_DEC_X86 + case BCJ_X86: + filtered = bcj_x86(s, buf, size); + break; +#endif +#ifdef XZ_DEC_POWERPC + case BCJ_POWERPC: + filtered = bcj_powerpc(s, buf, size); + break; +#endif +#ifdef XZ_DEC_IA64 + case BCJ_IA64: + filtered = bcj_ia64(s, buf, size); + break; +#endif +#ifdef XZ_DEC_ARM + case BCJ_ARM: + filtered = bcj_arm(s, buf, size); + break; +#endif +#ifdef XZ_DEC_ARMTHUMB + case BCJ_ARMTHUMB: + filtered = bcj_armthumb(s, buf, size); + break; +#endif +#ifdef XZ_DEC_SPARC + case BCJ_SPARC: + filtered = bcj_sparc(s, buf, size); + break; +#endif + default: + /* Never reached but silence compiler warnings. */ + filtered = 0; + break; + } + + *pos += filtered; + s->pos += filtered; +} + +/* + * Flush pending filtered data from temp to the output buffer. + * Move the remaining mixture of possibly filtered and unfiltered + * data to the beginning of temp. + */ +static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b) +{ + size_t copy_size; + + copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos); + memcpy(b->out + b->out_pos, s->temp.buf, copy_size); + b->out_pos += copy_size; + + s->temp.filtered -= copy_size; + s->temp.size -= copy_size; + memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size); +} + +/* + * The BCJ filter functions are primitive in sense that they process the + * data in chunks of 1-16 bytes. To hide this issue, this function does + * some buffering. + */ +XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, + struct xz_dec_lzma2 *lzma2, + struct xz_buf *b) +{ + size_t out_start; + + /* + * Flush pending already filtered data to the output buffer. Return + * immediatelly if we couldn't flush everything, or if the next + * filter in the chain had already returned XZ_STREAM_END. + */ + if (s->temp.filtered > 0) { + bcj_flush(s, b); + if (s->temp.filtered > 0) + return XZ_OK; + + if (s->ret == XZ_STREAM_END) + return XZ_STREAM_END; + } + + /* + * If we have more output space than what is currently pending in + * temp, copy the unfiltered data from temp to the output buffer + * and try to fill the output buffer by decoding more data from the + * next filter in the chain. Apply the BCJ filter on the new data + * in the output buffer. If everything cannot be filtered, copy it + * to temp and rewind the output buffer position accordingly. + * + * This needs to be always run when temp.size == 0 to handle a special + * case where the output buffer is full and the next filter has no + * more output coming but hasn't returned XZ_STREAM_END yet. + */ + if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) { + out_start = b->out_pos; + memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size); + b->out_pos += s->temp.size; + + s->ret = xz_dec_lzma2_run(lzma2, b); + if (s->ret != XZ_STREAM_END + && (s->ret != XZ_OK || s->single_call)) + return s->ret; + + bcj_apply(s, b->out, &out_start, b->out_pos); + + /* + * As an exception, if the next filter returned XZ_STREAM_END, + * we can do that too, since the last few bytes that remain + * unfiltered are meant to remain unfiltered. + */ + if (s->ret == XZ_STREAM_END) + return XZ_STREAM_END; + + s->temp.size = b->out_pos - out_start; + b->out_pos -= s->temp.size; + memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size); + + /* + * If there wasn't enough input to the next filter to fill + * the output buffer with unfiltered data, there's no point + * to try decoding more data to temp. + */ + if (b->out_pos + s->temp.size < b->out_size) + return XZ_OK; + } + + /* + * We have unfiltered data in temp. If the output buffer isn't full + * yet, try to fill the temp buffer by decoding more data from the + * next filter. Apply the BCJ filter on temp. Then we hopefully can + * fill the actual output buffer by copying filtered data from temp. + * A mix of filtered and unfiltered data may be left in temp; it will + * be taken care on the next call to this function. + */ + if (b->out_pos < b->out_size) { + /* Make b->out{,_pos,_size} temporarily point to s->temp. */ + s->out = b->out; + s->out_pos = b->out_pos; + s->out_size = b->out_size; + b->out = s->temp.buf; + b->out_pos = s->temp.size; + b->out_size = sizeof(s->temp.buf); + + s->ret = xz_dec_lzma2_run(lzma2, b); + + s->temp.size = b->out_pos; + b->out = s->out; + b->out_pos = s->out_pos; + b->out_size = s->out_size; + + if (s->ret != XZ_OK && s->ret != XZ_STREAM_END) + return s->ret; + + bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size); + + /* + * If the next filter returned XZ_STREAM_END, we mark that + * everything is filtered, since the last unfiltered bytes + * of the stream are meant to be left as is. + */ + if (s->ret == XZ_STREAM_END) + s->temp.filtered = s->temp.size; + + bcj_flush(s, b); + if (s->temp.filtered > 0) + return XZ_OK; + } + + return s->ret; +} + +XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call) +{ + struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s != NULL) + s->single_call = single_call; + + return s; +} + +XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id) +{ + switch (id) { +#ifdef XZ_DEC_X86 + case BCJ_X86: +#endif +#ifdef XZ_DEC_POWERPC + case BCJ_POWERPC: +#endif +#ifdef XZ_DEC_IA64 + case BCJ_IA64: +#endif +#ifdef XZ_DEC_ARM + case BCJ_ARM: +#endif +#ifdef XZ_DEC_ARMTHUMB + case BCJ_ARMTHUMB: +#endif +#ifdef XZ_DEC_SPARC + case BCJ_SPARC: +#endif + break; + + default: + /* Unsupported Filter ID */ + return XZ_OPTIONS_ERROR; + } + + s->type = id; + s->ret = XZ_OK; + s->pos = 0; + s->x86_prev_mask = 0; + s->temp.filtered = 0; + s->temp.size = 0; + + return XZ_OK; +} + +#endif diff --git a/modules/xz-embedded/src/xz_dec_lzma2.c b/modules/xz-embedded/src/xz_dec_lzma2.c new file mode 100644 index 000000000000..08c3c8049998 --- /dev/null +++ b/modules/xz-embedded/src/xz_dec_lzma2.c @@ -0,0 +1,1175 @@ +/* + * LZMA2 decoder + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_lzma2.h" + +/* + * Range decoder initialization eats the first five bytes of each LZMA chunk. + */ +#define RC_INIT_BYTES 5 + +/* + * Minimum number of usable input buffer to safely decode one LZMA symbol. + * The worst case is that we decode 22 bits using probabilities and 26 + * direct bits. This may decode at maximum of 20 bytes of input. However, + * lzma_main() does an extra normalization before returning, thus we + * need to put 21 here. + */ +#define LZMA_IN_REQUIRED 21 + +/* + * Dictionary (history buffer) + * + * These are always true: + * start <= pos <= full <= end + * pos <= limit <= end + * + * In multi-call mode, also these are true: + * end == size + * size <= size_max + * allocated <= size + * + * Most of these variables are size_t to support single-call mode, + * in which the dictionary variables address the actual output + * buffer directly. + */ +struct dictionary { + /* Beginning of the history buffer */ + uint8_t *buf; + + /* Old position in buf (before decoding more data) */ + size_t start; + + /* Position in buf */ + size_t pos; + + /* + * How full dictionary is. This is used to detect corrupt input that + * would read beyond the beginning of the uncompressed stream. + */ + size_t full; + + /* Write limit; we don't write to buf[limit] or later bytes. */ + size_t limit; + + /* + * End of the dictionary buffer. In multi-call mode, this is + * the same as the dictionary size. In single-call mode, this + * indicates the size of the output buffer. + */ + size_t end; + + /* + * Size of the dictionary as specified in Block Header. This is used + * together with "full" to detect corrupt input that would make us + * read beyond the beginning of the uncompressed stream. + */ + uint32_t size; + + /* + * Maximum allowed dictionary size in multi-call mode. + * This is ignored in single-call mode. + */ + uint32_t size_max; + + /* + * Amount of memory currently allocated for the dictionary. + * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC, + * size_max is always the same as the allocated size.) + */ + uint32_t allocated; + + /* Operation mode */ + enum xz_mode mode; +}; + +/* Range decoder */ +struct rc_dec { + uint32_t range; + uint32_t code; + + /* + * Number of initializing bytes remaining to be read + * by rc_read_init(). + */ + uint32_t init_bytes_left; + + /* + * Buffer from which we read our input. It can be either + * temp.buf or the caller-provided input buffer. + */ + const uint8_t *in; + size_t in_pos; + size_t in_limit; +}; + +/* Probabilities for a length decoder. */ +struct lzma_len_dec { + /* Probability of match length being at least 10 */ + uint16_t choice; + + /* Probability of match length being at least 18 */ + uint16_t choice2; + + /* Probabilities for match lengths 2-9 */ + uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS]; + + /* Probabilities for match lengths 10-17 */ + uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS]; + + /* Probabilities for match lengths 18-273 */ + uint16_t high[LEN_HIGH_SYMBOLS]; +}; + +struct lzma_dec { + /* Distances of latest four matches */ + uint32_t rep0; + uint32_t rep1; + uint32_t rep2; + uint32_t rep3; + + /* Types of the most recently seen LZMA symbols */ + enum lzma_state state; + + /* + * Length of a match. This is updated so that dict_repeat can + * be called again to finish repeating the whole match. + */ + uint32_t len; + + /* + * LZMA properties or related bit masks (number of literal + * context bits, a mask dervied from the number of literal + * position bits, and a mask dervied from the number + * position bits) + */ + uint32_t lc; + uint32_t literal_pos_mask; /* (1 << lp) - 1 */ + uint32_t pos_mask; /* (1 << pb) - 1 */ + + /* If 1, it's a match. Otherwise it's a single 8-bit literal. */ + uint16_t is_match[STATES][POS_STATES_MAX]; + + /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */ + uint16_t is_rep[STATES]; + + /* + * If 0, distance of a repeated match is rep0. + * Otherwise check is_rep1. + */ + uint16_t is_rep0[STATES]; + + /* + * If 0, distance of a repeated match is rep1. + * Otherwise check is_rep2. + */ + uint16_t is_rep1[STATES]; + + /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */ + uint16_t is_rep2[STATES]; + + /* + * If 1, the repeated match has length of one byte. Otherwise + * the length is decoded from rep_len_decoder. + */ + uint16_t is_rep0_long[STATES][POS_STATES_MAX]; + + /* + * Probability tree for the highest two bits of the match + * distance. There is a separate probability tree for match + * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273]. + */ + uint16_t dist_slot[DIST_STATES][DIST_SLOTS]; + + /* + * Probility trees for additional bits for match distance + * when the distance is in the range [4, 127]. + */ + uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END]; + + /* + * Probability tree for the lowest four bits of a match + * distance that is equal to or greater than 128. + */ + uint16_t dist_align[ALIGN_SIZE]; + + /* Length of a normal match */ + struct lzma_len_dec match_len_dec; + + /* Length of a repeated match */ + struct lzma_len_dec rep_len_dec; + + /* Probabilities of literals */ + uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE]; +}; + +struct lzma2_dec { + /* Position in xz_dec_lzma2_run(). */ + enum lzma2_seq { + SEQ_CONTROL, + SEQ_UNCOMPRESSED_1, + SEQ_UNCOMPRESSED_2, + SEQ_COMPRESSED_0, + SEQ_COMPRESSED_1, + SEQ_PROPERTIES, + SEQ_LZMA_PREPARE, + SEQ_LZMA_RUN, + SEQ_COPY + } sequence; + + /* Next position after decoding the compressed size of the chunk. */ + enum lzma2_seq next_sequence; + + /* Uncompressed size of LZMA chunk (2 MiB at maximum) */ + uint32_t uncompressed; + + /* + * Compressed size of LZMA chunk or compressed/uncompressed + * size of uncompressed chunk (64 KiB at maximum) + */ + uint32_t compressed; + + /* + * True if dictionary reset is needed. This is false before + * the first chunk (LZMA or uncompressed). + */ + bool need_dict_reset; + + /* + * True if new LZMA properties are needed. This is false + * before the first LZMA chunk. + */ + bool need_props; +}; + +struct xz_dec_lzma2 { + /* + * The order below is important on x86 to reduce code size and + * it shouldn't hurt on other platforms. Everything up to and + * including lzma.pos_mask are in the first 128 bytes on x86-32, + * which allows using smaller instructions to access those + * variables. On x86-64, fewer variables fit into the first 128 + * bytes, but this is still the best order without sacrificing + * the readability by splitting the structures. + */ + struct rc_dec rc; + struct dictionary dict; + struct lzma2_dec lzma2; + struct lzma_dec lzma; + + /* + * Temporary buffer which holds small number of input bytes between + * decoder calls. See lzma2_lzma() for details. + */ + struct { + uint32_t size; + uint8_t buf[3 * LZMA_IN_REQUIRED]; + } temp; +}; + +/************** + * Dictionary * + **************/ + +/* + * Reset the dictionary state. When in single-call mode, set up the beginning + * of the dictionary to point to the actual output buffer. + */ +static void dict_reset(struct dictionary *dict, struct xz_buf *b) +{ + if (DEC_IS_SINGLE(dict->mode)) { + dict->buf = b->out + b->out_pos; + dict->end = b->out_size - b->out_pos; + } + + dict->start = 0; + dict->pos = 0; + dict->limit = 0; + dict->full = 0; +} + +/* Set dictionary write limit */ +static void dict_limit(struct dictionary *dict, size_t out_max) +{ + if (dict->end - dict->pos <= out_max) + dict->limit = dict->end; + else + dict->limit = dict->pos + out_max; +} + +/* Return true if at least one byte can be written into the dictionary. */ +static inline bool dict_has_space(const struct dictionary *dict) +{ + return dict->pos < dict->limit; +} + +/* + * Get a byte from the dictionary at the given distance. The distance is + * assumed to valid, or as a special case, zero when the dictionary is + * still empty. This special case is needed for single-call decoding to + * avoid writing a '\0' to the end of the destination buffer. + */ +static inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist) +{ + size_t offset = dict->pos - dist - 1; + + if (dist >= dict->pos) + offset += dict->end; + + return dict->full > 0 ? dict->buf[offset] : 0; +} + +/* + * Put one byte into the dictionary. It is assumed that there is space for it. + */ +static inline void dict_put(struct dictionary *dict, uint8_t byte) +{ + dict->buf[dict->pos++] = byte; + + if (dict->full < dict->pos) + dict->full = dict->pos; +} + +/* + * Repeat given number of bytes from the given distance. If the distance is + * invalid, false is returned. On success, true is returned and *len is + * updated to indicate how many bytes were left to be repeated. + */ +static bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist) +{ + size_t back; + uint32_t left; + + if (dist >= dict->full || dist >= dict->size) + return false; + + left = min_t(size_t, dict->limit - dict->pos, *len); + *len -= left; + + back = dict->pos - dist - 1; + if (dist >= dict->pos) + back += dict->end; + + do { + dict->buf[dict->pos++] = dict->buf[back++]; + if (back == dict->end) + back = 0; + } while (--left > 0); + + if (dict->full < dict->pos) + dict->full = dict->pos; + + return true; +} + +/* Copy uncompressed data as is from input to dictionary and output buffers. */ +static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b, + uint32_t *left) +{ + size_t copy_size; + + while (*left > 0 && b->in_pos < b->in_size + && b->out_pos < b->out_size) { + copy_size = min(b->in_size - b->in_pos, + b->out_size - b->out_pos); + if (copy_size > dict->end - dict->pos) + copy_size = dict->end - dict->pos; + if (copy_size > *left) + copy_size = *left; + + *left -= copy_size; + + memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size); + dict->pos += copy_size; + + if (dict->full < dict->pos) + dict->full = dict->pos; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos == dict->end) + dict->pos = 0; + + memcpy(b->out + b->out_pos, b->in + b->in_pos, + copy_size); + } + + dict->start = dict->pos; + + b->out_pos += copy_size; + b->in_pos += copy_size; + } +} + +/* + * Flush pending data from dictionary to b->out. It is assumed that there is + * enough space in b->out. This is guaranteed because caller uses dict_limit() + * before decoding data into the dictionary. + */ +static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b) +{ + size_t copy_size = dict->pos - dict->start; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos == dict->end) + dict->pos = 0; + + memcpy(b->out + b->out_pos, dict->buf + dict->start, + copy_size); + } + + dict->start = dict->pos; + b->out_pos += copy_size; + return copy_size; +} + +/***************** + * Range decoder * + *****************/ + +/* Reset the range decoder. */ +static void rc_reset(struct rc_dec *rc) +{ + rc->range = (uint32_t)-1; + rc->code = 0; + rc->init_bytes_left = RC_INIT_BYTES; +} + +/* + * Read the first five initial bytes into rc->code if they haven't been + * read already. (Yes, the first byte gets completely ignored.) + */ +static bool rc_read_init(struct rc_dec *rc, struct xz_buf *b) +{ + while (rc->init_bytes_left > 0) { + if (b->in_pos == b->in_size) + return false; + + rc->code = (rc->code << 8) + b->in[b->in_pos++]; + --rc->init_bytes_left; + } + + return true; +} + +/* Return true if there may not be enough input for the next decoding loop. */ +static inline bool rc_limit_exceeded(const struct rc_dec *rc) +{ + return rc->in_pos > rc->in_limit; +} + +/* + * Return true if it is possible (from point of view of range decoder) that + * we have reached the end of the LZMA chunk. + */ +static inline bool rc_is_finished(const struct rc_dec *rc) +{ + return rc->code == 0; +} + +/* Read the next input byte if needed. */ +static __always_inline void rc_normalize(struct rc_dec *rc) +{ + if (rc->range < RC_TOP_VALUE) { + rc->range <<= RC_SHIFT_BITS; + rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++]; + } +} + +/* + * Decode one bit. In some versions, this function has been splitted in three + * functions so that the compiler is supposed to be able to more easily avoid + * an extra branch. In this particular version of the LZMA decoder, this + * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3 + * on x86). Using a non-splitted version results in nicer looking code too. + * + * NOTE: This must return an int. Do not make it return a bool or the speed + * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care, + * and it generates 10-20 % faster code than GCC 3.x from this file anyway.) + */ +static __always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob) +{ + uint32_t bound; + int bit; + + rc_normalize(rc); + bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob; + if (rc->code < bound) { + rc->range = bound; + *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS; + bit = 0; + } else { + rc->range -= bound; + rc->code -= bound; + *prob -= *prob >> RC_MOVE_BITS; + bit = 1; + } + + return bit; +} + +/* Decode a bittree starting from the most significant bit. */ +static __always_inline uint32_t rc_bittree(struct rc_dec *rc, + uint16_t *probs, uint32_t limit) +{ + uint32_t symbol = 1; + + do { + if (rc_bit(rc, &probs[symbol])) + symbol = (symbol << 1) + 1; + else + symbol <<= 1; + } while (symbol < limit); + + return symbol; +} + +/* Decode a bittree starting from the least significant bit. */ +static __always_inline void rc_bittree_reverse(struct rc_dec *rc, + uint16_t *probs, + uint32_t *dest, uint32_t limit) +{ + uint32_t symbol = 1; + uint32_t i = 0; + + do { + if (rc_bit(rc, &probs[symbol])) { + symbol = (symbol << 1) + 1; + *dest += 1 << i; + } else { + symbol <<= 1; + } + } while (++i < limit); +} + +/* Decode direct bits (fixed fifty-fifty probability) */ +static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit) +{ + uint32_t mask; + + do { + rc_normalize(rc); + rc->range >>= 1; + rc->code -= rc->range; + mask = (uint32_t)0 - (rc->code >> 31); + rc->code += rc->range & mask; + *dest = (*dest << 1) + (mask + 1); + } while (--limit > 0); +} + +/******** + * LZMA * + ********/ + +/* Get pointer to literal coder probability array. */ +static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s) +{ + uint32_t prev_byte = dict_get(&s->dict, 0); + uint32_t low = prev_byte >> (8 - s->lzma.lc); + uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc; + return s->lzma.literal[low + high]; +} + +/* Decode a literal (one 8-bit byte) */ +static void lzma_literal(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + uint32_t symbol; + uint32_t match_byte; + uint32_t match_bit; + uint32_t offset; + uint32_t i; + + probs = lzma_literal_probs(s); + + if (lzma_state_is_literal(s->lzma.state)) { + symbol = rc_bittree(&s->rc, probs, 0x100); + } else { + symbol = 1; + match_byte = dict_get(&s->dict, s->lzma.rep0) << 1; + offset = 0x100; + + do { + match_bit = match_byte & offset; + match_byte <<= 1; + i = offset + match_bit + symbol; + + if (rc_bit(&s->rc, &probs[i])) { + symbol = (symbol << 1) + 1; + offset &= match_bit; + } else { + symbol <<= 1; + offset &= ~match_bit; + } + } while (symbol < 0x100); + } + + dict_put(&s->dict, (uint8_t)symbol); + lzma_state_literal(&s->lzma.state); +} + +/* Decode the length of the match into s->lzma.len. */ +static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l, + uint32_t pos_state) +{ + uint16_t *probs; + uint32_t limit; + + if (!rc_bit(&s->rc, &l->choice)) { + probs = l->low[pos_state]; + limit = LEN_LOW_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN; + } else { + if (!rc_bit(&s->rc, &l->choice2)) { + probs = l->mid[pos_state]; + limit = LEN_MID_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS; + } else { + probs = l->high; + limit = LEN_HIGH_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS + + LEN_MID_SYMBOLS; + } + } + + s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit; +} + +/* Decode a match. The distance will be stored in s->lzma.rep0. */ +static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint16_t *probs; + uint32_t dist_slot; + uint32_t limit; + + lzma_state_match(&s->lzma.state); + + s->lzma.rep3 = s->lzma.rep2; + s->lzma.rep2 = s->lzma.rep1; + s->lzma.rep1 = s->lzma.rep0; + + lzma_len(s, &s->lzma.match_len_dec, pos_state); + + probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)]; + dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS; + + if (dist_slot < DIST_MODEL_START) { + s->lzma.rep0 = dist_slot; + } else { + limit = (dist_slot >> 1) - 1; + s->lzma.rep0 = 2 + (dist_slot & 1); + + if (dist_slot < DIST_MODEL_END) { + s->lzma.rep0 <<= limit; + probs = s->lzma.dist_special + s->lzma.rep0 + - dist_slot - 1; + rc_bittree_reverse(&s->rc, probs, + &s->lzma.rep0, limit); + } else { + rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS); + s->lzma.rep0 <<= ALIGN_BITS; + rc_bittree_reverse(&s->rc, s->lzma.dist_align, + &s->lzma.rep0, ALIGN_BITS); + } + } +} + +/* + * Decode a repeated match. The distance is one of the four most recently + * seen matches. The distance will be stored in s->lzma.rep0. + */ +static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint32_t tmp; + + if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) { + if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[ + s->lzma.state][pos_state])) { + lzma_state_short_rep(&s->lzma.state); + s->lzma.len = 1; + return; + } + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) { + tmp = s->lzma.rep1; + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) { + tmp = s->lzma.rep2; + } else { + tmp = s->lzma.rep3; + s->lzma.rep3 = s->lzma.rep2; + } + + s->lzma.rep2 = s->lzma.rep1; + } + + s->lzma.rep1 = s->lzma.rep0; + s->lzma.rep0 = tmp; + } + + lzma_state_long_rep(&s->lzma.state); + lzma_len(s, &s->lzma.rep_len_dec, pos_state); +} + +/* LZMA decoder core */ +static bool lzma_main(struct xz_dec_lzma2 *s) +{ + uint32_t pos_state; + + /* + * If the dictionary was reached during the previous call, try to + * finish the possibly pending repeat in the dictionary. + */ + if (dict_has_space(&s->dict) && s->lzma.len > 0) + dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0); + + /* + * Decode more LZMA symbols. One iteration may consume up to + * LZMA_IN_REQUIRED - 1 bytes. + */ + while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) { + pos_state = s->dict.pos & s->lzma.pos_mask; + + if (!rc_bit(&s->rc, &s->lzma.is_match[ + s->lzma.state][pos_state])) { + lzma_literal(s); + } else { + if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state])) + lzma_rep_match(s, pos_state); + else + lzma_match(s, pos_state); + + if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0)) + return false; + } + } + + /* + * Having the range decoder always normalized when we are outside + * this function makes it easier to correctly handle end of the chunk. + */ + rc_normalize(&s->rc); + + return true; +} + +/* + * Reset the LZMA decoder and range decoder state. Dictionary is nore reset + * here, because LZMA state may be reset without resetting the dictionary. + */ +static void lzma_reset(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + size_t i; + + s->lzma.state = STATE_LIT_LIT; + s->lzma.rep0 = 0; + s->lzma.rep1 = 0; + s->lzma.rep2 = 0; + s->lzma.rep3 = 0; + + /* + * All probabilities are initialized to the same value. This hack + * makes the code smaller by avoiding a separate loop for each + * probability array. + * + * This could be optimized so that only that part of literal + * probabilities that are actually required. In the common case + * we would write 12 KiB less. + */ + probs = s->lzma.is_match[0]; + for (i = 0; i < PROBS_TOTAL; ++i) + probs[i] = RC_BIT_MODEL_TOTAL / 2; + + rc_reset(&s->rc); +} + +/* + * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks + * from the decoded lp and pb values. On success, the LZMA decoder state is + * reset and true is returned. + */ +static bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props) +{ + if (props > (4 * 5 + 4) * 9 + 8) + return false; + + s->lzma.pos_mask = 0; + while (props >= 9 * 5) { + props -= 9 * 5; + ++s->lzma.pos_mask; + } + + s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1; + + s->lzma.literal_pos_mask = 0; + while (props >= 9) { + props -= 9; + ++s->lzma.literal_pos_mask; + } + + s->lzma.lc = props; + + if (s->lzma.lc + s->lzma.literal_pos_mask > 4) + return false; + + s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1; + + lzma_reset(s); + + return true; +} + +/********* + * LZMA2 * + *********/ + +/* + * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't + * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This + * wrapper function takes care of making the LZMA decoder's assumption safe. + * + * As long as there is plenty of input left to be decoded in the current LZMA + * chunk, we decode directly from the caller-supplied input buffer until + * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into + * s->temp.buf, which (hopefully) gets filled on the next call to this + * function. We decode a few bytes from the temporary buffer so that we can + * continue decoding from the caller-supplied input buffer again. + */ +static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b) +{ + size_t in_avail; + uint32_t tmp; + + in_avail = b->in_size - b->in_pos; + if (s->temp.size > 0 || s->lzma2.compressed == 0) { + tmp = 2 * LZMA_IN_REQUIRED - s->temp.size; + if (tmp > s->lzma2.compressed - s->temp.size) + tmp = s->lzma2.compressed - s->temp.size; + if (tmp > in_avail) + tmp = in_avail; + + memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp); + + if (s->temp.size + tmp == s->lzma2.compressed) { + memzero(s->temp.buf + s->temp.size + tmp, + sizeof(s->temp.buf) + - s->temp.size - tmp); + s->rc.in_limit = s->temp.size + tmp; + } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) { + s->temp.size += tmp; + b->in_pos += tmp; + return true; + } else { + s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED; + } + + s->rc.in = s->temp.buf; + s->rc.in_pos = 0; + + if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp) + return false; + + s->lzma2.compressed -= s->rc.in_pos; + + if (s->rc.in_pos < s->temp.size) { + s->temp.size -= s->rc.in_pos; + memmove(s->temp.buf, s->temp.buf + s->rc.in_pos, + s->temp.size); + return true; + } + + b->in_pos += s->rc.in_pos - s->temp.size; + s->temp.size = 0; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail >= LZMA_IN_REQUIRED) { + s->rc.in = b->in; + s->rc.in_pos = b->in_pos; + + if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED) + s->rc.in_limit = b->in_pos + s->lzma2.compressed; + else + s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED; + + if (!lzma_main(s)) + return false; + + in_avail = s->rc.in_pos - b->in_pos; + if (in_avail > s->lzma2.compressed) + return false; + + s->lzma2.compressed -= in_avail; + b->in_pos = s->rc.in_pos; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail < LZMA_IN_REQUIRED) { + if (in_avail > s->lzma2.compressed) + in_avail = s->lzma2.compressed; + + memcpy(s->temp.buf, b->in + b->in_pos, in_avail); + s->temp.size = in_avail; + b->in_pos += in_avail; + } + + return true; +} + +/* + * Take care of the LZMA2 control layer, and forward the job of actual LZMA + * decoding or copying of uncompressed chunks to other functions. + */ +XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, + struct xz_buf *b) +{ + uint32_t tmp; + + while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) { + switch (s->lzma2.sequence) { + case SEQ_CONTROL: + /* + * LZMA2 control byte + * + * Exact values: + * 0x00 End marker + * 0x01 Dictionary reset followed by + * an uncompressed chunk + * 0x02 Uncompressed chunk (no dictionary reset) + * + * Highest three bits (s->control & 0xE0): + * 0xE0 Dictionary reset, new properties and state + * reset, followed by LZMA compressed chunk + * 0xC0 New properties and state reset, followed + * by LZMA compressed chunk (no dictionary + * reset) + * 0xA0 State reset using old properties, + * followed by LZMA compressed chunk (no + * dictionary reset) + * 0x80 LZMA chunk (no dictionary or state reset) + * + * For LZMA compressed chunks, the lowest five bits + * (s->control & 1F) are the highest bits of the + * uncompressed size (bits 16-20). + * + * A new LZMA2 stream must begin with a dictionary + * reset. The first LZMA chunk must set new + * properties and reset the LZMA state. + * + * Values that don't match anything described above + * are invalid and we return XZ_DATA_ERROR. + */ + tmp = b->in[b->in_pos++]; + + if (tmp == 0x00) + return XZ_STREAM_END; + + if (tmp >= 0xE0 || tmp == 0x01) { + s->lzma2.need_props = true; + s->lzma2.need_dict_reset = false; + dict_reset(&s->dict, b); + } else if (s->lzma2.need_dict_reset) { + return XZ_DATA_ERROR; + } + + if (tmp >= 0x80) { + s->lzma2.uncompressed = (tmp & 0x1F) << 16; + s->lzma2.sequence = SEQ_UNCOMPRESSED_1; + + if (tmp >= 0xC0) { + /* + * When there are new properties, + * state reset is done at + * SEQ_PROPERTIES. + */ + s->lzma2.need_props = false; + s->lzma2.next_sequence + = SEQ_PROPERTIES; + + } else if (s->lzma2.need_props) { + return XZ_DATA_ERROR; + + } else { + s->lzma2.next_sequence + = SEQ_LZMA_PREPARE; + if (tmp >= 0xA0) + lzma_reset(s); + } + } else { + if (tmp > 0x02) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_COMPRESSED_0; + s->lzma2.next_sequence = SEQ_COPY; + } + + break; + + case SEQ_UNCOMPRESSED_1: + s->lzma2.uncompressed + += (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_UNCOMPRESSED_2; + break; + + case SEQ_UNCOMPRESSED_2: + s->lzma2.uncompressed + += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = SEQ_COMPRESSED_0; + break; + + case SEQ_COMPRESSED_0: + s->lzma2.compressed + = (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_COMPRESSED_1; + break; + + case SEQ_COMPRESSED_1: + s->lzma2.compressed + += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = s->lzma2.next_sequence; + break; + + case SEQ_PROPERTIES: + if (!lzma_props(s, b->in[b->in_pos++])) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_LZMA_PREPARE; + + /* Fall through */ + + case SEQ_LZMA_PREPARE: + if (s->lzma2.compressed < RC_INIT_BYTES) + return XZ_DATA_ERROR; + + if (!rc_read_init(&s->rc, b)) + return XZ_OK; + + s->lzma2.compressed -= RC_INIT_BYTES; + s->lzma2.sequence = SEQ_LZMA_RUN; + + /* Fall through */ + + case SEQ_LZMA_RUN: + /* + * Set dictionary limit to indicate how much we want + * to be encoded at maximum. Decode new data into the + * dictionary. Flush the new data from dictionary to + * b->out. Check if we finished decoding this chunk. + * In case the dictionary got full but we didn't fill + * the output buffer yet, we may run this loop + * multiple times without changing s->lzma2.sequence. + */ + dict_limit(&s->dict, min_t(size_t, + b->out_size - b->out_pos, + s->lzma2.uncompressed)); + if (!lzma2_lzma(s, b)) + return XZ_DATA_ERROR; + + s->lzma2.uncompressed -= dict_flush(&s->dict, b); + + if (s->lzma2.uncompressed == 0) { + if (s->lzma2.compressed > 0 || s->lzma.len > 0 + || !rc_is_finished(&s->rc)) + return XZ_DATA_ERROR; + + rc_reset(&s->rc); + s->lzma2.sequence = SEQ_CONTROL; + + } else if (b->out_pos == b->out_size + || (b->in_pos == b->in_size + && s->temp.size + < s->lzma2.compressed)) { + return XZ_OK; + } + + break; + + case SEQ_COPY: + dict_uncompressed(&s->dict, b, &s->lzma2.compressed); + if (s->lzma2.compressed > 0) + return XZ_OK; + + s->lzma2.sequence = SEQ_CONTROL; + break; + } + } + + return XZ_OK; +} + +XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, + uint32_t dict_max) +{ + struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->dict.mode = mode; + s->dict.size_max = dict_max; + + if (DEC_IS_PREALLOC(mode)) { + s->dict.buf = vmalloc(dict_max); + if (s->dict.buf == NULL) { + kfree(s); + return NULL; + } + } else if (DEC_IS_DYNALLOC(mode)) { + s->dict.buf = NULL; + s->dict.allocated = 0; + } + + return s; +} + +XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props) +{ + /* This limits dictionary size to 3 GiB to keep parsing simpler. */ + if (props > 39) + return XZ_OPTIONS_ERROR; + + s->dict.size = 2 + (props & 1); + s->dict.size <<= (props >> 1) + 11; + + if (DEC_IS_MULTI(s->dict.mode)) { + if (s->dict.size > s->dict.size_max) + return XZ_MEMLIMIT_ERROR; + + s->dict.end = s->dict.size; + + if (DEC_IS_DYNALLOC(s->dict.mode)) { + if (s->dict.allocated < s->dict.size) { + vfree(s->dict.buf); + s->dict.buf = vmalloc(s->dict.size); + if (s->dict.buf == NULL) { + s->dict.allocated = 0; + return XZ_MEM_ERROR; + } + } + } + } + + s->lzma.len = 0; + + s->lzma2.sequence = SEQ_CONTROL; + s->lzma2.need_dict_reset = true; + + s->temp.size = 0; + + return XZ_OK; +} + +XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s) +{ + if (DEC_IS_MULTI(s->dict.mode)) + vfree(s->dict.buf); + + kfree(s); +} diff --git a/modules/xz-embedded/src/xz_dec_stream.c b/modules/xz-embedded/src/xz_dec_stream.c new file mode 100644 index 000000000000..d6525506a1ed --- /dev/null +++ b/modules/xz-embedded/src/xz_dec_stream.c @@ -0,0 +1,847 @@ +/* + * .xz Stream decoder + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_stream.h" + +#ifdef XZ_USE_CRC64 +# define IS_CRC64(check_type) ((check_type) == XZ_CHECK_CRC64) +#else +# define IS_CRC64(check_type) false +#endif + +/* Hash used to validate the Index field */ +struct xz_dec_hash { + vli_type unpadded; + vli_type uncompressed; + uint32_t crc32; +}; + +struct xz_dec { + /* Position in dec_main() */ + enum { + SEQ_STREAM_HEADER, + SEQ_BLOCK_START, + SEQ_BLOCK_HEADER, + SEQ_BLOCK_UNCOMPRESS, + SEQ_BLOCK_PADDING, + SEQ_BLOCK_CHECK, + SEQ_INDEX, + SEQ_INDEX_PADDING, + SEQ_INDEX_CRC32, + SEQ_STREAM_FOOTER + } sequence; + + /* Position in variable-length integers and Check fields */ + uint32_t pos; + + /* Variable-length integer decoded by dec_vli() */ + vli_type vli; + + /* Saved in_pos and out_pos */ + size_t in_start; + size_t out_start; + +#ifdef XZ_USE_CRC64 + /* CRC32 or CRC64 value in Block or CRC32 value in Index */ + uint64_t crc; +#else + /* CRC32 value in Block or Index */ + uint32_t crc; +#endif + + /* Type of the integrity check calculated from uncompressed data */ + enum xz_check check_type; + + /* Operation mode */ + enum xz_mode mode; + + /* + * True if the next call to xz_dec_run() is allowed to return + * XZ_BUF_ERROR. + */ + bool allow_buf_error; + + /* Information stored in Block Header */ + struct { + /* + * Value stored in the Compressed Size field, or + * VLI_UNKNOWN if Compressed Size is not present. + */ + vli_type compressed; + + /* + * Value stored in the Uncompressed Size field, or + * VLI_UNKNOWN if Uncompressed Size is not present. + */ + vli_type uncompressed; + + /* Size of the Block Header field */ + uint32_t size; + } block_header; + + /* Information collected when decoding Blocks */ + struct { + /* Observed compressed size of the current Block */ + vli_type compressed; + + /* Observed uncompressed size of the current Block */ + vli_type uncompressed; + + /* Number of Blocks decoded so far */ + vli_type count; + + /* + * Hash calculated from the Block sizes. This is used to + * validate the Index field. + */ + struct xz_dec_hash hash; + } block; + + /* Variables needed when verifying the Index field */ + struct { + /* Position in dec_index() */ + enum { + SEQ_INDEX_COUNT, + SEQ_INDEX_UNPADDED, + SEQ_INDEX_UNCOMPRESSED + } sequence; + + /* Size of the Index in bytes */ + vli_type size; + + /* Number of Records (matches block.count in valid files) */ + vli_type count; + + /* + * Hash calculated from the Records (matches block.hash in + * valid files). + */ + struct xz_dec_hash hash; + } index; + + /* + * Temporary buffer needed to hold Stream Header, Block Header, + * and Stream Footer. The Block Header is the biggest (1 KiB) + * so we reserve space according to that. buf[] has to be aligned + * to a multiple of four bytes; the size_t variables before it + * should guarantee this. + */ + struct { + size_t pos; + size_t size; + uint8_t buf[1024]; + } temp; + + struct xz_dec_lzma2 *lzma2; + +#ifdef XZ_DEC_BCJ + struct xz_dec_bcj *bcj; + bool bcj_active; +#endif +}; + +#ifdef XZ_DEC_ANY_CHECK +/* Sizes of the Check field with different Check IDs */ +static const uint8_t check_sizes[16] = { + 0, + 4, 4, 4, + 8, 8, 8, + 16, 16, 16, + 32, 32, 32, + 64, 64, 64 +}; +#endif + +/* + * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller + * must have set s->temp.pos to indicate how much data we are supposed + * to copy into s->temp.buf. Return true once s->temp.pos has reached + * s->temp.size. + */ +static bool fill_temp(struct xz_dec *s, struct xz_buf *b) +{ + size_t copy_size = min_t(size_t, + b->in_size - b->in_pos, s->temp.size - s->temp.pos); + + memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size); + b->in_pos += copy_size; + s->temp.pos += copy_size; + + if (s->temp.pos == s->temp.size) { + s->temp.pos = 0; + return true; + } + + return false; +} + +/* Decode a variable-length integer (little-endian base-128 encoding) */ +static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in, + size_t *in_pos, size_t in_size) +{ + uint8_t byte; + + if (s->pos == 0) + s->vli = 0; + + while (*in_pos < in_size) { + byte = in[*in_pos]; + ++*in_pos; + + s->vli |= (vli_type)(byte & 0x7F) << s->pos; + + if ((byte & 0x80) == 0) { + /* Don't allow non-minimal encodings. */ + if (byte == 0 && s->pos != 0) + return XZ_DATA_ERROR; + + s->pos = 0; + return XZ_STREAM_END; + } + + s->pos += 7; + if (s->pos == 7 * VLI_BYTES_MAX) + return XZ_DATA_ERROR; + } + + return XZ_OK; +} + +/* + * Decode the Compressed Data field from a Block. Update and validate + * the observed compressed and uncompressed sizes of the Block so that + * they don't exceed the values possibly stored in the Block Header + * (validation assumes that no integer overflow occurs, since vli_type + * is normally uint64_t). Update the CRC32 or CRC64 value if presence of + * the CRC32 or CRC64 field was indicated in Stream Header. + * + * Once the decoding is finished, validate that the observed sizes match + * the sizes possibly stored in the Block Header. Update the hash and + * Block count, which are later used to validate the Index field. + */ +static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + s->in_start = b->in_pos; + s->out_start = b->out_pos; + +#ifdef XZ_DEC_BCJ + if (s->bcj_active) + ret = xz_dec_bcj_run(s->bcj, s->lzma2, b); + else +#endif + ret = xz_dec_lzma2_run(s->lzma2, b); + + s->block.compressed += b->in_pos - s->in_start; + s->block.uncompressed += b->out_pos - s->out_start; + + /* + * There is no need to separately check for VLI_UNKNOWN, since + * the observed sizes are always smaller than VLI_UNKNOWN. + */ + if (s->block.compressed > s->block_header.compressed + || s->block.uncompressed + > s->block_header.uncompressed) + return XZ_DATA_ERROR; + + if (s->check_type == XZ_CHECK_CRC32) + s->crc = xz_crc32(b->out + s->out_start, + b->out_pos - s->out_start, s->crc); +#ifdef XZ_USE_CRC64 + else if (s->check_type == XZ_CHECK_CRC64) + s->crc = xz_crc64(b->out + s->out_start, + b->out_pos - s->out_start, s->crc); +#endif + + if (ret == XZ_STREAM_END) { + if (s->block_header.compressed != VLI_UNKNOWN + && s->block_header.compressed + != s->block.compressed) + return XZ_DATA_ERROR; + + if (s->block_header.uncompressed != VLI_UNKNOWN + && s->block_header.uncompressed + != s->block.uncompressed) + return XZ_DATA_ERROR; + + s->block.hash.unpadded += s->block_header.size + + s->block.compressed; + +#ifdef XZ_DEC_ANY_CHECK + s->block.hash.unpadded += check_sizes[s->check_type]; +#else + if (s->check_type == XZ_CHECK_CRC32) + s->block.hash.unpadded += 4; + else if (IS_CRC64(s->check_type)) + s->block.hash.unpadded += 8; +#endif + + s->block.hash.uncompressed += s->block.uncompressed; + s->block.hash.crc32 = xz_crc32( + (const uint8_t *)&s->block.hash, + sizeof(s->block.hash), s->block.hash.crc32); + + ++s->block.count; + } + + return ret; +} + +/* Update the Index size and the CRC32 value. */ +static void index_update(struct xz_dec *s, const struct xz_buf *b) +{ + size_t in_used = b->in_pos - s->in_start; + s->index.size += in_used; + s->crc = xz_crc32(b->in + s->in_start, in_used, s->crc); +} + +/* + * Decode the Number of Records, Unpadded Size, and Uncompressed Size + * fields from the Index field. That is, Index Padding and CRC32 are not + * decoded by this function. + * + * This can return XZ_OK (more input needed), XZ_STREAM_END (everything + * successfully decoded), or XZ_DATA_ERROR (input is corrupt). + */ +static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + do { + ret = dec_vli(s, b->in, &b->in_pos, b->in_size); + if (ret != XZ_STREAM_END) { + index_update(s, b); + return ret; + } + + switch (s->index.sequence) { + case SEQ_INDEX_COUNT: + s->index.count = s->vli; + + /* + * Validate that the Number of Records field + * indicates the same number of Records as + * there were Blocks in the Stream. + */ + if (s->index.count != s->block.count) + return XZ_DATA_ERROR; + + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + + case SEQ_INDEX_UNPADDED: + s->index.hash.unpadded += s->vli; + s->index.sequence = SEQ_INDEX_UNCOMPRESSED; + break; + + case SEQ_INDEX_UNCOMPRESSED: + s->index.hash.uncompressed += s->vli; + s->index.hash.crc32 = xz_crc32( + (const uint8_t *)&s->index.hash, + sizeof(s->index.hash), + s->index.hash.crc32); + --s->index.count; + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + } + } while (s->index.count > 0); + + return XZ_STREAM_END; +} + +/* + * Validate that the next four or eight input bytes match the value + * of s->crc. s->pos must be zero when starting to validate the first byte. + * The "bits" argument allows using the same code for both CRC32 and CRC64. + */ +static enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b, + uint32_t bits) +{ + do { + if (b->in_pos == b->in_size) + return XZ_OK; + + if (((s->crc >> s->pos) & 0xFF) != b->in[b->in_pos++]) + return XZ_DATA_ERROR; + + s->pos += 8; + + } while (s->pos < bits); + + s->crc = 0; + s->pos = 0; + + return XZ_STREAM_END; +} + +#ifdef XZ_DEC_ANY_CHECK +/* + * Skip over the Check field when the Check ID is not supported. + * Returns true once the whole Check field has been skipped over. + */ +static bool check_skip(struct xz_dec *s, struct xz_buf *b) +{ + while (s->pos < check_sizes[s->check_type]) { + if (b->in_pos == b->in_size) + return false; + + ++b->in_pos; + ++s->pos; + } + + s->pos = 0; + + return true; +} +#endif + +/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */ +static enum xz_ret dec_stream_header(struct xz_dec *s) +{ + if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE)) + return XZ_FORMAT_ERROR; + + if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0) + != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2)) + return XZ_DATA_ERROR; + + if (s->temp.buf[HEADER_MAGIC_SIZE] != 0) + return XZ_OPTIONS_ERROR; + + /* + * Of integrity checks, we support none (Check ID = 0), + * CRC32 (Check ID = 1), and optionally CRC64 (Check ID = 4). + * However, if XZ_DEC_ANY_CHECK is defined, we will accept other + * check types too, but then the check won't be verified and + * a warning (XZ_UNSUPPORTED_CHECK) will be given. + */ + s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1]; + +#ifdef XZ_DEC_ANY_CHECK + if (s->check_type > XZ_CHECK_MAX) + return XZ_OPTIONS_ERROR; + + if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type)) + return XZ_UNSUPPORTED_CHECK; +#else + if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type)) + return XZ_OPTIONS_ERROR; +#endif + + return XZ_OK; +} + +/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */ +static enum xz_ret dec_stream_footer(struct xz_dec *s) +{ + if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE)) + return XZ_DATA_ERROR; + + if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf)) + return XZ_DATA_ERROR; + + /* + * Validate Backward Size. Note that we never added the size of the + * Index CRC32 field to s->index.size, thus we use s->index.size / 4 + * instead of s->index.size / 4 - 1. + */ + if ((s->index.size >> 2) != get_le32(s->temp.buf + 4)) + return XZ_DATA_ERROR; + + if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type) + return XZ_DATA_ERROR; + + /* + * Use XZ_STREAM_END instead of XZ_OK to be more convenient + * for the caller. + */ + return XZ_STREAM_END; +} + +/* Decode the Block Header and initialize the filter chain. */ +static enum xz_ret dec_block_header(struct xz_dec *s) +{ + enum xz_ret ret; + + /* + * Validate the CRC32. We know that the temp buffer is at least + * eight bytes so this is safe. + */ + s->temp.size -= 4; + if (xz_crc32(s->temp.buf, s->temp.size, 0) + != get_le32(s->temp.buf + s->temp.size)) + return XZ_DATA_ERROR; + + s->temp.pos = 2; + + /* + * Catch unsupported Block Flags. We support only one or two filters + * in the chain, so we catch that with the same test. + */ +#ifdef XZ_DEC_BCJ + if (s->temp.buf[1] & 0x3E) +#else + if (s->temp.buf[1] & 0x3F) +#endif + return XZ_OPTIONS_ERROR; + + /* Compressed Size */ + if (s->temp.buf[1] & 0x40) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.compressed = s->vli; + } else { + s->block_header.compressed = VLI_UNKNOWN; + } + + /* Uncompressed Size */ + if (s->temp.buf[1] & 0x80) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.uncompressed = s->vli; + } else { + s->block_header.uncompressed = VLI_UNKNOWN; + } + +#ifdef XZ_DEC_BCJ + /* If there are two filters, the first one must be a BCJ filter. */ + s->bcj_active = s->temp.buf[1] & 0x01; + if (s->bcj_active) { + if (s->temp.size - s->temp.pos < 2) + return XZ_OPTIONS_ERROR; + + ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* + * We don't support custom start offset, + * so Size of Properties must be zero. + */ + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + } +#endif + + /* Valid Filter Flags always take at least two bytes. */ + if (s->temp.size - s->temp.pos < 2) + return XZ_DATA_ERROR; + + /* Filter ID = LZMA2 */ + if (s->temp.buf[s->temp.pos++] != 0x21) + return XZ_OPTIONS_ERROR; + + /* Size of Properties = 1-byte Filter Properties */ + if (s->temp.buf[s->temp.pos++] != 0x01) + return XZ_OPTIONS_ERROR; + + /* Filter Properties contains LZMA2 dictionary size. */ + if (s->temp.size - s->temp.pos < 1) + return XZ_DATA_ERROR; + + ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* The rest must be Header Padding. */ + while (s->temp.pos < s->temp.size) + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + + s->temp.pos = 0; + s->block.compressed = 0; + s->block.uncompressed = 0; + + return XZ_OK; +} + +static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + /* + * Store the start position for the case when we are in the middle + * of the Index field. + */ + s->in_start = b->in_pos; + + while (true) { + switch (s->sequence) { + case SEQ_STREAM_HEADER: + /* + * Stream Header is copied to s->temp, and then + * decoded from there. This way if the caller + * gives us only little input at a time, we can + * still keep the Stream Header decoding code + * simple. Similar approach is used in many places + * in this file. + */ + if (!fill_temp(s, b)) + return XZ_OK; + + /* + * If dec_stream_header() returns + * XZ_UNSUPPORTED_CHECK, it is still possible + * to continue decoding if working in multi-call + * mode. Thus, update s->sequence before calling + * dec_stream_header(). + */ + s->sequence = SEQ_BLOCK_START; + + ret = dec_stream_header(s); + if (ret != XZ_OK) + return ret; + + case SEQ_BLOCK_START: + /* We need one byte of input to continue. */ + if (b->in_pos == b->in_size) + return XZ_OK; + + /* See if this is the beginning of the Index field. */ + if (b->in[b->in_pos] == 0) { + s->in_start = b->in_pos++; + s->sequence = SEQ_INDEX; + break; + } + + /* + * Calculate the size of the Block Header and + * prepare to decode it. + */ + s->block_header.size + = ((uint32_t)b->in[b->in_pos] + 1) * 4; + + s->temp.size = s->block_header.size; + s->temp.pos = 0; + s->sequence = SEQ_BLOCK_HEADER; + + case SEQ_BLOCK_HEADER: + if (!fill_temp(s, b)) + return XZ_OK; + + ret = dec_block_header(s); + if (ret != XZ_OK) + return ret; + + s->sequence = SEQ_BLOCK_UNCOMPRESS; + + case SEQ_BLOCK_UNCOMPRESS: + ret = dec_block(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_BLOCK_PADDING; + + case SEQ_BLOCK_PADDING: + /* + * Size of Compressed Data + Block Padding + * must be a multiple of four. We don't need + * s->block.compressed for anything else + * anymore, so we use it here to test the size + * of the Block Padding field. + */ + while (s->block.compressed & 3) { + if (b->in_pos == b->in_size) + return XZ_OK; + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + + ++s->block.compressed; + } + + s->sequence = SEQ_BLOCK_CHECK; + + case SEQ_BLOCK_CHECK: + if (s->check_type == XZ_CHECK_CRC32) { + ret = crc_validate(s, b, 32); + if (ret != XZ_STREAM_END) + return ret; + } + else if (IS_CRC64(s->check_type)) { + ret = crc_validate(s, b, 64); + if (ret != XZ_STREAM_END) + return ret; + } +#ifdef XZ_DEC_ANY_CHECK + else if (!check_skip(s, b)) { + return XZ_OK; + } +#endif + + s->sequence = SEQ_BLOCK_START; + break; + + case SEQ_INDEX: + ret = dec_index(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_INDEX_PADDING; + + case SEQ_INDEX_PADDING: + while ((s->index.size + (b->in_pos - s->in_start)) + & 3) { + if (b->in_pos == b->in_size) { + index_update(s, b); + return XZ_OK; + } + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + } + + /* Finish the CRC32 value and Index size. */ + index_update(s, b); + + /* Compare the hashes to validate the Index field. */ + if (!memeq(&s->block.hash, &s->index.hash, + sizeof(s->block.hash))) + return XZ_DATA_ERROR; + + s->sequence = SEQ_INDEX_CRC32; + + case SEQ_INDEX_CRC32: + ret = crc_validate(s, b, 32); + if (ret != XZ_STREAM_END) + return ret; + + s->temp.size = STREAM_HEADER_SIZE; + s->sequence = SEQ_STREAM_FOOTER; + + case SEQ_STREAM_FOOTER: + if (!fill_temp(s, b)) + return XZ_OK; + + return dec_stream_footer(s); + } + } + + /* Never reached */ +} + +/* + * xz_dec_run() is a wrapper for dec_main() to handle some special cases in + * multi-call and single-call decoding. + * + * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we + * are not going to make any progress anymore. This is to prevent the caller + * from calling us infinitely when the input file is truncated or otherwise + * corrupt. Since zlib-style API allows that the caller fills the input buffer + * only when the decoder doesn't produce any new output, we have to be careful + * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only + * after the second consecutive call to xz_dec_run() that makes no progress. + * + * In single-call mode, if we couldn't decode everything and no error + * occurred, either the input is truncated or the output buffer is too small. + * Since we know that the last input byte never produces any output, we know + * that if all the input was consumed and decoding wasn't finished, the file + * must be corrupt. Otherwise the output buffer has to be too small or the + * file is corrupt in a way that decoding it produces too big output. + * + * If single-call decoding fails, we reset b->in_pos and b->out_pos back to + * their original values. This is because with some filter chains there won't + * be any valid uncompressed data in the output buffer unless the decoding + * actually succeeds (that's the price to pay of using the output buffer as + * the workspace). + */ +XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b) +{ + size_t in_start; + size_t out_start; + enum xz_ret ret; + + if (DEC_IS_SINGLE(s->mode)) + xz_dec_reset(s); + + in_start = b->in_pos; + out_start = b->out_pos; + ret = dec_main(s, b); + + if (DEC_IS_SINGLE(s->mode)) { + if (ret == XZ_OK) + ret = b->in_pos == b->in_size + ? XZ_DATA_ERROR : XZ_BUF_ERROR; + + if (ret != XZ_STREAM_END) { + b->in_pos = in_start; + b->out_pos = out_start; + } + + } else if (ret == XZ_OK && in_start == b->in_pos + && out_start == b->out_pos) { + if (s->allow_buf_error) + ret = XZ_BUF_ERROR; + + s->allow_buf_error = true; + } else { + s->allow_buf_error = false; + } + + return ret; +} + +XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max) +{ + struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->mode = mode; + +#ifdef XZ_DEC_BCJ + s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode)); + if (s->bcj == NULL) + goto error_bcj; +#endif + + s->lzma2 = xz_dec_lzma2_create(mode, dict_max); + if (s->lzma2 == NULL) + goto error_lzma2; + + xz_dec_reset(s); + return s; + +error_lzma2: +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +error_bcj: +#endif + kfree(s); + return NULL; +} + +XZ_EXTERN void xz_dec_reset(struct xz_dec *s) +{ + s->sequence = SEQ_STREAM_HEADER; + s->allow_buf_error = false; + s->pos = 0; + s->crc = 0; + memzero(&s->block, sizeof(s->block)); + memzero(&s->index, sizeof(s->index)); + s->temp.pos = 0; + s->temp.size = STREAM_HEADER_SIZE; +} + +XZ_EXTERN void xz_dec_end(struct xz_dec *s) +{ + if (s != NULL) { + xz_dec_lzma2_end(s->lzma2); +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +#endif + kfree(s); + } +} diff --git a/modules/xz-embedded/src/xz_lzma2.h b/modules/xz-embedded/src/xz_lzma2.h new file mode 100644 index 000000000000..071d67bee9f5 --- /dev/null +++ b/modules/xz-embedded/src/xz_lzma2.h @@ -0,0 +1,204 @@ +/* + * LZMA2 definitions + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_LZMA2_H +#define XZ_LZMA2_H + +/* Range coder constants */ +#define RC_SHIFT_BITS 8 +#define RC_TOP_BITS 24 +#define RC_TOP_VALUE (1 << RC_TOP_BITS) +#define RC_BIT_MODEL_TOTAL_BITS 11 +#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS) +#define RC_MOVE_BITS 5 + +/* + * Maximum number of position states. A position state is the lowest pb + * number of bits of the current uncompressed offset. In some places there + * are different sets of probabilities for different position states. + */ +#define POS_STATES_MAX (1 << 4) + +/* + * This enum is used to track which LZMA symbols have occurred most recently + * and in which order. This information is used to predict the next symbol. + * + * Symbols: + * - Literal: One 8-bit byte + * - Match: Repeat a chunk of data at some distance + * - Long repeat: Multi-byte match at a recently seen distance + * - Short repeat: One-byte repeat at a recently seen distance + * + * The symbol names are in from STATE_oldest_older_previous. REP means + * either short or long repeated match, and NONLIT means any non-literal. + */ +enum lzma_state { + STATE_LIT_LIT, + STATE_MATCH_LIT_LIT, + STATE_REP_LIT_LIT, + STATE_SHORTREP_LIT_LIT, + STATE_MATCH_LIT, + STATE_REP_LIT, + STATE_SHORTREP_LIT, + STATE_LIT_MATCH, + STATE_LIT_LONGREP, + STATE_LIT_SHORTREP, + STATE_NONLIT_MATCH, + STATE_NONLIT_REP +}; + +/* Total number of states */ +#define STATES 12 + +/* The lowest 7 states indicate that the previous state was a literal. */ +#define LIT_STATES 7 + +/* Indicate that the latest symbol was a literal. */ +static inline void lzma_state_literal(enum lzma_state *state) +{ + if (*state <= STATE_SHORTREP_LIT_LIT) + *state = STATE_LIT_LIT; + else if (*state <= STATE_LIT_SHORTREP) + *state -= 3; + else + *state -= 6; +} + +/* Indicate that the latest symbol was a match. */ +static inline void lzma_state_match(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH; +} + +/* Indicate that the latest state was a long repeated match. */ +static inline void lzma_state_long_rep(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP; +} + +/* Indicate that the latest symbol was a short match. */ +static inline void lzma_state_short_rep(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP; +} + +/* Test if the previous symbol was a literal. */ +static inline bool lzma_state_is_literal(enum lzma_state state) +{ + return state < LIT_STATES; +} + +/* Each literal coder is divided in three sections: + * - 0x001-0x0FF: Without match byte + * - 0x101-0x1FF: With match byte; match bit is 0 + * - 0x201-0x2FF: With match byte; match bit is 1 + * + * Match byte is used when the previous LZMA symbol was something else than + * a literal (that is, it was some kind of match). + */ +#define LITERAL_CODER_SIZE 0x300 + +/* Maximum number of literal coders */ +#define LITERAL_CODERS_MAX (1 << 4) + +/* Minimum length of a match is two bytes. */ +#define MATCH_LEN_MIN 2 + +/* Match length is encoded with 4, 5, or 10 bits. + * + * Length Bits + * 2-9 4 = Choice=0 + 3 bits + * 10-17 5 = Choice=1 + Choice2=0 + 3 bits + * 18-273 10 = Choice=1 + Choice2=1 + 8 bits + */ +#define LEN_LOW_BITS 3 +#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS) +#define LEN_MID_BITS 3 +#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS) +#define LEN_HIGH_BITS 8 +#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS) +#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS) + +/* + * Maximum length of a match is 273 which is a result of the encoding + * described above. + */ +#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1) + +/* + * Different sets of probabilities are used for match distances that have + * very short match length: Lengths of 2, 3, and 4 bytes have a separate + * set of probabilities for each length. The matches with longer length + * use a shared set of probabilities. + */ +#define DIST_STATES 4 + +/* + * Get the index of the appropriate probability array for decoding + * the distance slot. + */ +static inline uint32_t lzma_get_dist_state(uint32_t len) +{ + return len < DIST_STATES + MATCH_LEN_MIN + ? len - MATCH_LEN_MIN : DIST_STATES - 1; +} + +/* + * The highest two bits of a 32-bit match distance are encoded using six bits. + * This six-bit value is called a distance slot. This way encoding a 32-bit + * value takes 6-36 bits, larger values taking more bits. + */ +#define DIST_SLOT_BITS 6 +#define DIST_SLOTS (1 << DIST_SLOT_BITS) + +/* Match distances up to 127 are fully encoded using probabilities. Since + * the highest two bits (distance slot) are always encoded using six bits, + * the distances 0-3 don't need any additional bits to encode, since the + * distance slot itself is the same as the actual distance. DIST_MODEL_START + * indicates the first distance slot where at least one additional bit is + * needed. + */ +#define DIST_MODEL_START 4 + +/* + * Match distances greater than 127 are encoded in three pieces: + * - distance slot: the highest two bits + * - direct bits: 2-26 bits below the highest two bits + * - alignment bits: four lowest bits + * + * Direct bits don't use any probabilities. + * + * The distance slot value of 14 is for distances 128-191. + */ +#define DIST_MODEL_END 14 + +/* Distance slots that indicate a distance <= 127. */ +#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2) +#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS) + +/* + * For match distances greater than 127, only the highest two bits and the + * lowest four bits (alignment) is encoded using probabilities. + */ +#define ALIGN_BITS 4 +#define ALIGN_SIZE (1 << ALIGN_BITS) +#define ALIGN_MASK (ALIGN_SIZE - 1) + +/* Total number of all probability variables */ +#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE) + +/* + * LZMA remembers the four most recent match distances. Reusing these + * distances tends to take less space than re-encoding the actual + * distance value. + */ +#define REPS 4 + +#endif diff --git a/modules/xz-embedded/src/xz_private.h b/modules/xz-embedded/src/xz_private.h new file mode 100644 index 000000000000..482b90f363fe --- /dev/null +++ b/modules/xz-embedded/src/xz_private.h @@ -0,0 +1,156 @@ +/* + * Private includes and definitions + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_PRIVATE_H +#define XZ_PRIVATE_H + +#ifdef __KERNEL__ +# include +# include +# include + /* XZ_PREBOOT may be defined only via decompress_unxz.c. */ +# ifndef XZ_PREBOOT +# include +# include +# include +# ifdef CONFIG_XZ_DEC_X86 +# define XZ_DEC_X86 +# endif +# ifdef CONFIG_XZ_DEC_POWERPC +# define XZ_DEC_POWERPC +# endif +# ifdef CONFIG_XZ_DEC_IA64 +# define XZ_DEC_IA64 +# endif +# ifdef CONFIG_XZ_DEC_ARM +# define XZ_DEC_ARM +# endif +# ifdef CONFIG_XZ_DEC_ARMTHUMB +# define XZ_DEC_ARMTHUMB +# endif +# ifdef CONFIG_XZ_DEC_SPARC +# define XZ_DEC_SPARC +# endif +# define memeq(a, b, size) (memcmp(a, b, size) == 0) +# define memzero(buf, size) memset(buf, 0, size) +# endif +# define get_le32(p) le32_to_cpup((const uint32_t *)(p)) +#else + /* + * For userspace builds, use a separate header to define the required + * macros and functions. This makes it easier to adapt the code into + * different environments and avoids clutter in the Linux kernel tree. + */ +# include "xz_config.h" +#endif + +/* If no specific decoding mode is requested, enable support for all modes. */ +#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \ + && !defined(XZ_DEC_DYNALLOC) +# define XZ_DEC_SINGLE +# define XZ_DEC_PREALLOC +# define XZ_DEC_DYNALLOC +#endif + +/* + * The DEC_IS_foo(mode) macros are used in "if" statements. If only some + * of the supported modes are enabled, these macros will evaluate to true or + * false at compile time and thus allow the compiler to omit unneeded code. + */ +#ifdef XZ_DEC_SINGLE +# define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE) +#else +# define DEC_IS_SINGLE(mode) (false) +#endif + +#ifdef XZ_DEC_PREALLOC +# define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC) +#else +# define DEC_IS_PREALLOC(mode) (false) +#endif + +#ifdef XZ_DEC_DYNALLOC +# define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC) +#else +# define DEC_IS_DYNALLOC(mode) (false) +#endif + +#if !defined(XZ_DEC_SINGLE) +# define DEC_IS_MULTI(mode) (true) +#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC) +# define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE) +#else +# define DEC_IS_MULTI(mode) (false) +#endif + +/* + * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ. + * XZ_DEC_BCJ is used to enable generic support for BCJ decoders. + */ +#ifndef XZ_DEC_BCJ +# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \ + || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \ + || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \ + || defined(XZ_DEC_SPARC) +# define XZ_DEC_BCJ +# endif +#endif + +/* + * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used + * before calling xz_dec_lzma2_run(). + */ +XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, + uint32_t dict_max); + +/* + * Decode the LZMA2 properties (one byte) and reset the decoder. Return + * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not + * big enough, and XZ_OPTIONS_ERROR if props indicates something that this + * decoder doesn't support. + */ +XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, + uint8_t props); + +/* Decode raw LZMA2 stream from b->in to b->out. */ +XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, + struct xz_buf *b); + +/* Free the memory allocated for the LZMA2 decoder. */ +XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s); + +#ifdef XZ_DEC_BCJ +/* + * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before + * calling xz_dec_bcj_run(). + */ +XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call); + +/* + * Decode the Filter ID of a BCJ filter. This implementation doesn't + * support custom start offsets, so no decoding of Filter Properties + * is needed. Returns XZ_OK if the given Filter ID is supported. + * Otherwise XZ_OPTIONS_ERROR is returned. + */ +XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id); + +/* + * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is + * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run() + * must be called directly. + */ +XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, + struct xz_dec_lzma2 *lzma2, + struct xz_buf *b); + +/* Free the memory allocated for the BCJ filters. */ +#define xz_dec_bcj_end(s) kfree(s) +#endif + +#endif diff --git a/modules/xz-embedded/src/xz_stream.h b/modules/xz-embedded/src/xz_stream.h new file mode 100644 index 000000000000..66cb5a7055ec --- /dev/null +++ b/modules/xz-embedded/src/xz_stream.h @@ -0,0 +1,62 @@ +/* + * Definitions for handling the .xz file format + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_STREAM_H +#define XZ_STREAM_H + +#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32 +# include +# undef crc32 +# define xz_crc32(buf, size, crc) \ + (~crc32_le(~(uint32_t)(crc), buf, size)) +#endif + +/* + * See the .xz file format specification at + * http://tukaani.org/xz/xz-file-format.txt + * to understand the container format. + */ + +#define STREAM_HEADER_SIZE 12 + +#define HEADER_MAGIC "\3757zXZ" +#define HEADER_MAGIC_SIZE 6 + +#define FOOTER_MAGIC "YZ" +#define FOOTER_MAGIC_SIZE 2 + +/* + * Variable-length integer can hold a 63-bit unsigned integer or a special + * value indicating that the value is unknown. + * + * Experimental: vli_type can be defined to uint32_t to save a few bytes + * in code size (no effect on speed). Doing so limits the uncompressed and + * compressed size of the file to less than 256 MiB and may also weaken + * error detection slightly. + */ +typedef uint64_t vli_type; + +#define VLI_MAX ((vli_type)-1 / 2) +#define VLI_UNKNOWN ((vli_type)-1) + +/* Maximum encoded size of a VLI */ +#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7) + +/* Integrity Check types */ +enum xz_check { + XZ_CHECK_NONE = 0, + XZ_CHECK_CRC32 = 1, + XZ_CHECK_CRC64 = 4, + XZ_CHECK_SHA256 = 10 +}; + +/* Maximum possible Check ID */ +#define XZ_CHECK_MAX 15 + +#endif diff --git a/modules/xz-embedded/update.sh b/modules/xz-embedded/update.sh new file mode 100755 index 000000000000..ecc1e3610e74 --- /dev/null +++ b/modules/xz-embedded/update.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# Script to update the Mozilla in-tree copy of XZ Embedded. + +MY_TEMP_DIR=$(mktemp -d -t xz-embedded_update.XXXXXX) || exit 1 + +git clone http://git.tukaani.org/xz-embedded.git ${MY_TEMP_DIR}/xz-embedded + +COMMIT=$(git -C ${MY_TEMP_DIR}/xz-embedded rev-parse HEAD) +cd $(dirname $0) +perl -p -i -e "s/\[commit [0-9a-f]{40}\]/[${COMMIT}]/" README.mozilla; + +rm -rf src +mkdir src +mv ${MY_TEMP_DIR}/xz-embedded/userspace/xz_config.h src/ +mv ${MY_TEMP_DIR}/xz-embedded/linux/include/linux/xz.h src/ +mv ${MY_TEMP_DIR}/xz-embedded/linux/lib/xz/xz_private.h src/ +mv ${MY_TEMP_DIR}/xz-embedded/linux/lib/xz/xz_lzma2.h src/ +mv ${MY_TEMP_DIR}/xz-embedded/linux/lib/xz/xz_stream.h src/ +mv ${MY_TEMP_DIR}/xz-embedded/linux/lib/xz/xz_crc32.c src/ +mv ${MY_TEMP_DIR}/xz-embedded/linux/lib/xz/xz_crc64.c src/ +mv ${MY_TEMP_DIR}/xz-embedded/linux/lib/xz/xz_dec_bcj.c src/ +mv ${MY_TEMP_DIR}/xz-embedded/linux/lib/xz/xz_dec_stream.c src/ +mv ${MY_TEMP_DIR}/xz-embedded/linux/lib/xz/xz_dec_lzma2.c src/ +rm -rf ${MY_TEMP_DIR} +hg addremove src + +echo "###" +echo "### Updated xz-embedded/src to $COMMIT." +echo "### Remember to verify and commit the changes to source control!" +echo "###" diff --git a/mozglue/linker/moz.build b/mozglue/linker/moz.build index 7d7a41ccf78c..23217df77782 100644 --- a/mozglue/linker/moz.build +++ b/mozglue/linker/moz.build @@ -45,3 +45,7 @@ if CONFIG['CPU_ARCH'] == 'x86': if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wno-error=shadow'] + +USE_LIBS += [ + 'xz-embedded', +] From ed2a6c5769679cc025648a54cfaa5158738e1360 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Tue, 18 Oct 2016 08:42:12 -0400 Subject: [PATCH 66/80] Bug 1307445 part A - Don't ever show "A plugin is needed to display this content." or "This plugin is disabled." - Just always use fallback content or empty content. r=mconley+sebastian MozReview-Commit-ID: ClnOkK3sWIK --HG-- extra : rebase_source : 0325b830088bef3a7ee5a506041c8eafafbf50e2 --- browser/base/content/test/plugins/browser.ini | 1 - .../content/test/plugins/browser_bug797677.js | 4 -- .../test/plugins/browser_plugin_infolink.js | 48 ------------------ mobile/android/chrome/content/PluginHelper.js | 11 ---- .../chrome/pluginproblem/pluginproblem.dtd | 11 ---- .../test/browser/browser_CTP_plugins.js | 2 - .../content/pluginFinderBinding.css | 18 ------- .../pluginproblem/content/pluginProblem.xml | 3 -- .../content/pluginProblemBinding.css | 3 -- .../content/pluginProblemContent.css | 10 ---- toolkit/pluginproblem/pluginGlue.manifest | 1 - toolkit/themes/mobile/jar.mn | 3 -- .../mozapps/plugins/contentPluginDownload.png | Bin 1061 -> 0 bytes .../mozapps/plugins/contentPluginMissing.png | Bin 1572 -> 0 bytes .../mobile/mozapps/plugins/pluginProblem.css | 10 ---- toolkit/themes/shared/mozapps.inc.mn | 3 -- .../shared/plugins/contentPluginDownload.png | Bin 1061 -> 0 bytes .../shared/plugins/contentPluginMissing.png | Bin 1572 -> 0 bytes .../themes/shared/plugins/pluginProblem.css | 3 -- 19 files changed, 131 deletions(-) delete mode 100644 browser/base/content/test/plugins/browser_plugin_infolink.js delete mode 100644 toolkit/pluginproblem/content/pluginFinderBinding.css delete mode 100644 toolkit/themes/mobile/mozapps/plugins/contentPluginDownload.png delete mode 100644 toolkit/themes/mobile/mozapps/plugins/contentPluginMissing.png delete mode 100644 toolkit/themes/shared/plugins/contentPluginDownload.png delete mode 100644 toolkit/themes/shared/plugins/contentPluginMissing.png diff --git a/browser/base/content/test/plugins/browser.ini b/browser/base/content/test/plugins/browser.ini index f332715fbf7b..5d01032cd6da 100644 --- a/browser/base/content/test/plugins/browser.ini +++ b/browser/base/content/test/plugins/browser.ini @@ -65,7 +65,6 @@ skip-if = !crashreporter [browser_blocking.js] [browser_plugins_added_dynamically.js] [browser_pluginnotification.js] -[browser_plugin_infolink.js] [browser_plugin_reloading.js] [browser_blocklist_content.js] skip-if = !e10s diff --git a/browser/base/content/test/plugins/browser_bug797677.js b/browser/base/content/test/plugins/browser_bug797677.js index bb8490f94264..825b352441ec 100644 --- a/browser/base/content/test/plugins/browser_bug797677.js +++ b/browser/base/content/test/plugins/browser_bug797677.js @@ -19,8 +19,6 @@ add_task(function* () { gBrowser.selectedTab = gBrowser.addTab(); gTestBrowser = gBrowser.selectedBrowser; - let bindingPromise = waitForEvent(gTestBrowser, "PluginBindingAttached", null, true, true); - let consoleService = Cc["@mozilla.org/consoleservice;1"] .getService(Ci.nsIConsoleService); let errorListener = { @@ -33,8 +31,6 @@ add_task(function* () { yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_bug797677.html"); - yield bindingPromise; - let pluginInfo = yield promiseForPluginInfo("plugin"); is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED, "plugin should not have been found."); diff --git a/browser/base/content/test/plugins/browser_plugin_infolink.js b/browser/base/content/test/plugins/browser_plugin_infolink.js deleted file mode 100644 index 8995d4eaa74d..000000000000 --- a/browser/base/content/test/plugins/browser_plugin_infolink.js +++ /dev/null @@ -1,48 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); -var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost); -var oldBrowserOpenAddonsMgr = window.BrowserOpenAddonsMgr; - -registerCleanupFunction(function* cleanup() { - clearAllPluginPermissions(); - Services.prefs.clearUserPref("plugins.click_to_play"); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - window.BrowserOpenAddonsMgr = oldBrowserOpenAddonsMgr; - window.focus(); -}); - -add_task(function* test_clicking_manage_link_in_plugin_overlay_should_open_about_addons() { - Services.prefs.setBoolPref("plugins.click_to_play", true); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_DISABLED, "Test Plug-in"); - - let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gTestRoot + "plugin_test.html"); - let browser = tab.linkedBrowser; - yield promiseUpdatePluginBindings(browser); - - let pluginInfo = yield promiseForPluginInfo("test", browser); - is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED, - "plugin fallback type should be PLUGIN_DISABLED"); - - let awaitBrowserOpenAddonsMgr = new Promise(resolve => { - window.BrowserOpenAddonsMgr = function(view) { - resolve(view); - } - }); - - yield ContentTask.spawn(browser, null, function* () { - let pluginNode = content.document.getElementById("test"); - let manageLink = content.document.getAnonymousElementByAttribute(pluginNode, "anonid", "managePluginsLink"); - let bounds = manageLink.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindowUtils); - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - Assert.ok(true, "click on manage link"); - }); - - let requestedView = yield awaitBrowserOpenAddonsMgr; - is(requestedView, "addons://list/plugin", "The Add-ons Manager should open the plugin view"); - - yield BrowserTestUtils.removeTab(tab); -}); diff --git a/mobile/android/chrome/content/PluginHelper.js b/mobile/android/chrome/content/PluginHelper.js index b684a74555cf..59d87fa7c6db 100644 --- a/mobile/android/chrome/content/PluginHelper.js +++ b/mobile/android/chrome/content/PluginHelper.js @@ -200,17 +200,6 @@ var PluginHelper = { break; } - - case "PluginNotFound": { - // On devices where we don't support Flash, there will be a - // "Learn More..." link in the missing plugin error message. - let learnMoreLink = doc.getAnonymousElementByAttribute(plugin, "class", "unsupportedLearnMoreLink"); - let learnMoreUrl = Services.urlFormatter.formatURLPref("app.support.baseURL"); - learnMoreUrl += "mobile-flash-unsupported"; - learnMoreLink.href = learnMoreUrl; - overlay.classList.add("visible"); - break; - } } }, diff --git a/toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd b/toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd index cdca2666dfac..e8dba67144dd 100644 --- a/toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd +++ b/toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd @@ -2,21 +2,10 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - - - - - - - - - diff --git a/toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js b/toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js index d6c6b560030a..dd2992f810e6 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js +++ b/toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js @@ -98,8 +98,6 @@ add_task(function*() { pluginTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gHttpTestRoot + "plugin_test.html"); pluginBrowser = pluginTab.linkedBrowser; - condition = () => PopupNotifications.getNotification("click-to-play-plugins", pluginBrowser); - yield BrowserTestUtils.waitForCondition(condition, "part7: disabled plugins still show a notification"); yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() { let testPlugin = content.document.getElementById("test"); ok(testPlugin, "part7: should have a plugin element in the page"); diff --git a/toolkit/pluginproblem/content/pluginFinderBinding.css b/toolkit/pluginproblem/content/pluginFinderBinding.css deleted file mode 100644 index 6fc40666d723..000000000000 --- a/toolkit/pluginproblem/content/pluginFinderBinding.css +++ /dev/null @@ -1,18 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -@namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */ - -/* This binding is specified separately from the others so that Linux distros can - override the default Mozilla plugin finder service with their own mechanism. */ -embed:-moz-type-unsupported, -applet:-moz-type-unsupported, -object:-moz-type-unsupported, -embed:-moz-type-unsupported-platform, -applet:-moz-type-unsupported-platform, -object:-moz-type-unsupported-platform { - display: inline-block; - overflow: hidden; - -moz-binding: url('chrome://pluginproblem/content/pluginProblem.xml#pluginProblem') !important; -} diff --git a/toolkit/pluginproblem/content/pluginProblem.xml b/toolkit/pluginproblem/content/pluginProblem.xml index 6454f06bf794..d890be9006a4 100644 --- a/toolkit/pluginproblem/content/pluginProblem.xml +++ b/toolkit/pluginproblem/content/pluginProblem.xml @@ -31,9 +31,6 @@ &clickToActivatePlugin; - &missingPlugin; - &unsupportedPlatform.pre;&unsupportedPlatform.learnMore;&unsupportedPlatform.post; - &disabledPlugin; &blockedPlugin.label; diff --git a/toolkit/pluginproblem/content/pluginProblemBinding.css b/toolkit/pluginproblem/content/pluginProblemBinding.css index 4c111745ab49..48506de34445 100644 --- a/toolkit/pluginproblem/content/pluginProblemBinding.css +++ b/toolkit/pluginproblem/content/pluginProblemBinding.css @@ -4,19 +4,16 @@ @namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */ -embed:-moz-handler-disabled, embed:-moz-handler-blocked, embed:-moz-handler-crashed, embed:-moz-handler-clicktoplay, embed:-moz-handler-vulnerable-updatable, embed:-moz-handler-vulnerable-no-update, -applet:-moz-handler-disabled, applet:-moz-handler-blocked, applet:-moz-handler-crashed, applet:-moz-handler-clicktoplay, applet:-moz-handler-vulnerable-updatable, applet:-moz-handler-vulnerable-no-update, -object:-moz-handler-disabled, object:-moz-handler-blocked, object:-moz-handler-crashed, object:-moz-handler-clicktoplay, diff --git a/toolkit/pluginproblem/content/pluginProblemContent.css b/toolkit/pluginproblem/content/pluginProblemContent.css index eb7df1e83dcc..43a9f52dcba5 100644 --- a/toolkit/pluginproblem/content/pluginProblemContent.css +++ b/toolkit/pluginproblem/content/pluginProblemContent.css @@ -21,22 +21,16 @@ html|applet:not([height]), html|applet[height=""] { } a .mainBox, -:-moz-type-unsupported .mainBox, -:-moz-type-unsupported-platform .mainBox, :-moz-handler-clicktoplay .mainBox, :-moz-handler-vulnerable-updatable .mainBox, :-moz-handler-vulnerable-no-update .mainBox, -:-moz-handler-disabled .mainBox, :-moz-handler-blocked .mainBox { -moz-user-focus: normal; } a .mainBox:focus, -:-moz-type-unsupported .mainBox:focus, -:-moz-type-unsupported-platform .mainBox:focus, :-moz-handler-clicktoplay .mainBox:focus, :-moz-handler-vulnerable-updatable .mainBox:focus, :-moz-handler-vulnerable-no-update .mainBox:focus, -:-moz-handler-disabled .mainBox:focus, :-moz-handler-blocked .mainBox:focus { outline: 1px dotted; } @@ -93,8 +87,6 @@ a .hoverBox, a .msgClickToPlay, a .msgTapToPlay, -:-moz-type-unsupported .msgUnsupported, -:-moz-type-unsupported-platform .msgUnsupportedPlatform, :-moz-handler-clicktoplay .msgClickToPlay, :-moz-handler-vulnerable-updatable .msgVulnerabilityStatus, :-moz-handler-vulnerable-updatable .msgCheckForUpdates, @@ -102,8 +94,6 @@ a .msgTapToPlay, :-moz-handler-vulnerable-no-update .msgVulnerabilityStatus, :-moz-handler-vulnerable-no-update .msgClickToPlay, :-moz-handler-clicktoplay .msgTapToPlay, -:-moz-handler-disabled .msgDisabled, -:-moz-handler-disabled .msgManagePlugins, :-moz-handler-blocked .msgBlocked, :-moz-handler-crashed .msgCrashed { display: block; diff --git a/toolkit/pluginproblem/pluginGlue.manifest b/toolkit/pluginproblem/pluginGlue.manifest index 2e55e1ed90f7..96ebc9bd4cf6 100644 --- a/toolkit/pluginproblem/pluginGlue.manifest +++ b/toolkit/pluginproblem/pluginGlue.manifest @@ -1,2 +1 @@ -category agent-style-sheets pluginGlue-pluginFinder chrome://pluginproblem/content/pluginFinderBinding.css category agent-style-sheets pluginGlue-pluginProblem chrome://pluginproblem/content/pluginProblemBinding.css diff --git a/toolkit/themes/mobile/jar.mn b/toolkit/themes/mobile/jar.mn index 2f504ecf9b34..62f8893494ab 100644 --- a/toolkit/themes/mobile/jar.mn +++ b/toolkit/themes/mobile/jar.mn @@ -52,7 +52,4 @@ toolkit.jar: skin/classic/mozapps/plugins/contentPluginBlocked.png (mozapps/plugins/contentPluginBlocked.png) skin/classic/mozapps/plugins/contentPluginClose.png (mozapps/plugins/contentPluginClose.png) skin/classic/mozapps/plugins/contentPluginCrashed.png (mozapps/plugins/contentPluginCrashed.png) - skin/classic/mozapps/plugins/contentPluginDisabled.png (mozapps/plugins/contentPluginDisabled.png) - skin/classic/mozapps/plugins/contentPluginDownload.png (mozapps/plugins/contentPluginDownload.png) - skin/classic/mozapps/plugins/contentPluginMissing.png (mozapps/plugins/contentPluginMissing.png) skin/classic/mozapps/plugins/contentPluginStripe.png (mozapps/plugins/contentPluginStripe.png) diff --git a/toolkit/themes/mobile/mozapps/plugins/contentPluginDownload.png b/toolkit/themes/mobile/mozapps/plugins/contentPluginDownload.png deleted file mode 100644 index 714302dc26e66aaea12a27bc1c7f5c0ceea9d503..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1061 zcmV+=1ls$FP)ti=!ypnGbe?f}i69Cn0XD)RW!unCG4%;N zQnnK0bD>`$j+Twb=x;M@>(l)yZCAqq_aI%K{Uur3P8F&ra5mn`P~qUVOF zlS6@%D0B~9=OWsG19bT`w*LyknWcmmi;W(K}%JNFsG~$q~4NfA( zF%CfX`3cy^wjJ!};(y8mad>q|R1;X1*;9$r&NE9Q3cdA%DMtMvXe8evI_!fRUSeGZt zh_n@mYoRs{{s3dOMRKa#D0rZYmOnyeX!z@nf(Ja3tf`zu{q~08fw%%pXIG(Tky%#2 zHJn`q@l56w=#i;HZy7x8pr4uozk+t&+}wP-va(Wx;%^L{dxqRyDIn`Qu+cq)?=`?x zc|P9Q*tlITm)GKK%ceyb2q5@zwOYM}(dG~%Pe+?Zbo!|1B-^%SU&$kk%peN5VX(Zs zT&dM+cgP_1>0^-ve9}htk$uC;OI!horKP2{dcFPtr=&w}5Nxg7zon3EWuGXrf~)~A z%3NGrTwLGT+41M*=3bG7%=Bcg*=#VlC*qk z9#FKfu&}zlz3pK6e$tlL6%YyT#3xiLmF^|YQ_q^YHgG>JOC@=~MGeBW%3ey=N|Jh1 z37hPaa)EVQt=5)x_K7Dmz_E2nRx8e3En26K4O3X}Siv6Uf}ZHY^73%d>ZrO;uNKc; z?&81;*6Dkeoq;*8-pbT1_LHP{ph=G(5KL0(!gg|rVTLcWAw0<8Tjx$ frZI)2zXA*Z`)+5Io1<*f00000NkvXXu0mjf&9nF! diff --git a/toolkit/themes/mobile/mozapps/plugins/contentPluginMissing.png b/toolkit/themes/mobile/mozapps/plugins/contentPluginMissing.png deleted file mode 100644 index 9fb2eac26ed3717c2cdffdfec365c484c537984b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1572 zcmV+<2HW|GP)()8ndER^Lno23-uHChpH#4W-Oi^A_DN6Gkl-HGbce;Ql zvY2dfZ)GU1nZ|-5ac|r*FO{i)PG-|I4Izx)m-uQ6p;=7}3qd|#O`*V`yr@MfoI-WH z;FlH)8UxLQVYrF@eWuTN+`b@s{zO01c>0dNuQ1&PFT!LAoIOLx<_{{iZ9f*4oCV$I z?{wb-(3{++czlIj9q<_U7Iv^668JnA%e?jOoj@NQ32L|O8FonPw%c|sLRGpHD zUHbS7Qp$0h2jy}(g0N_MdO9M@1wjzOVye^Wm~b~S{1Vg#-D8(KGK6l(tHz2%OiETI zOH>J42pZulDf27n7YOJ;`z8c8V70S}iHTDHIEDMq$;H0!ldzYd-`CgI9|=AQFYdcG zFJVxP$(2>i5!O#)AI!gjg&w7o1y*T8SZ8KtCaBeF!R+j;IzK;mDJD-(PaV8>_1Lww zwa-CxyU1PpH|2%CZGDfb^poqlAB*`QcG6V`;Okj4<;u#+90YY178bn4#YGPQ$mMpW zQYpiNW%QACe&b4_+d~mTy1nd^mwjc{{U6(mW84c^<^`+9eG_aC@5M8b9E z=H?n8Z+?E>gYaNvWF&64+t%^%v3q!USUx&BDxaL3jJ8^>5)#sk<2a!tJ}pZT!Lka8 z>%;P`JYJwGyiq|oIY{W*($dmXt~T$;sw6|mmh%F@|4raw9UoR{A=$`M0jw0sH3~gb zKio_1Rse%S;@KL!ZlnMuF2;x8<>&DN4~q;ziA`9bfSkK@4wz1AtwQuc;&a!%kHjKD z9TGS-H5EXS5JF>7>%uBjFZ2X3zwsfoCM465sUV6_eFb znaB$!k;aAguEgwQtid(In220YZl^@+0_tM?pqmrP3!xa1Y>gueW6uyu*0gb~L<|4k7|64yKE7Qz#WT~@{@iQP{M`j{b`{Jh>ccP`B@?K z9TVk1%y}@3ut{=|0HksCmBcc6Sb?IGI1@|XNlc2XR2&T>dASl9*Gb|8FzRrC={&@e zAOHjTXP}Tqp##}AFgS#zTLfe&@IGsQPg{Rj^MLe%#?^NS>DbGgNM5lQa^+`v;5mQ% zVXB0wA*Fy$;^(QgP<|D9T|%gr7o5C0JZul-%|lk+Jb+Ef&~iY_zOD!Ae0@uQILr_d z;OqS;V2XLb!j8zsp47qy+bLQBl1Z`&17Q*J%`64v%kSx?*=&A98wviEXB-?HI7KRe zt*JwQD9+B#O2Fc3TxgFa=B}VtS6AQO-roL$Tu7U9Y%vR+iD?EV@bH35Sb7~Xx#Ayw z6`}WIm$C2h^@kh2zm+kF2$}~P{BP(Ywk}RYu>N?Fb7p1S@cqDJ7+WZgoz$h@iu?n$ z%J=%`kxHFW71i{=A5$2(!`5%}y|1fF#D@_&!+p*VYlu5Cd72rE^Rfkje1sVry^lf*-xnJ_j$O?Z!0R4H7p+Y0!S`LeYu9Z52`~V1 WdyupwJ%7;v0000ti=!ypnGbe?f}i69Cn0XD)RW!unCG4%;N zQnnK0bD>`$j+Twb=x;M@>(l)yZCAqq_aI%K{Uur3P8F&ra5mn`P~qUVOF zlS6@%D0B~9=OWsG19bT`w*LyknWcmmi;W(K}%JNFsG~$q~4NfA( zF%CfX`3cy^wjJ!};(y8mad>q|R1;X1*;9$r&NE9Q3cdA%DMtMvXe8evI_!fRUSeGZt zh_n@mYoRs{{s3dOMRKa#D0rZYmOnyeX!z@nf(Ja3tf`zu{q~08fw%%pXIG(Tky%#2 zHJn`q@l56w=#i;HZy7x8pr4uozk+t&+}wP-va(Wx;%^L{dxqRyDIn`Qu+cq)?=`?x zc|P9Q*tlITm)GKK%ceyb2q5@zwOYM}(dG~%Pe+?Zbo!|1B-^%SU&$kk%peN5VX(Zs zT&dM+cgP_1>0^-ve9}htk$uC;OI!horKP2{dcFPtr=&w}5Nxg7zon3EWuGXrf~)~A z%3NGrTwLGT+41M*=3bG7%=Bcg*=#VlC*qk z9#FKfu&}zlz3pK6e$tlL6%YyT#3xiLmF^|YQ_q^YHgG>JOC@=~MGeBW%3ey=N|Jh1 z37hPaa)EVQt=5)x_K7Dmz_E2nRx8e3En26K4O3X}Siv6Uf}ZHY^73%d>ZrO;uNKc; z?&81;*6Dkeoq;*8-pbT1_LHP{ph=G(5KL0(!gg|rVTLcWAw0<8Tjx$ frZI)2zXA*Z`)+5Io1<*f00000NkvXXu0mjf&9nF! diff --git a/toolkit/themes/shared/plugins/contentPluginMissing.png b/toolkit/themes/shared/plugins/contentPluginMissing.png deleted file mode 100644 index 9fb2eac26ed3717c2cdffdfec365c484c537984b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1572 zcmV+<2HW|GP)()8ndER^Lno23-uHChpH#4W-Oi^A_DN6Gkl-HGbce;Ql zvY2dfZ)GU1nZ|-5ac|r*FO{i)PG-|I4Izx)m-uQ6p;=7}3qd|#O`*V`yr@MfoI-WH z;FlH)8UxLQVYrF@eWuTN+`b@s{zO01c>0dNuQ1&PFT!LAoIOLx<_{{iZ9f*4oCV$I z?{wb-(3{++czlIj9q<_U7Iv^668JnA%e?jOoj@NQ32L|O8FonPw%c|sLRGpHD zUHbS7Qp$0h2jy}(g0N_MdO9M@1wjzOVye^Wm~b~S{1Vg#-D8(KGK6l(tHz2%OiETI zOH>J42pZulDf27n7YOJ;`z8c8V70S}iHTDHIEDMq$;H0!ldzYd-`CgI9|=AQFYdcG zFJVxP$(2>i5!O#)AI!gjg&w7o1y*T8SZ8KtCaBeF!R+j;IzK;mDJD-(PaV8>_1Lww zwa-CxyU1PpH|2%CZGDfb^poqlAB*`QcG6V`;Okj4<;u#+90YY178bn4#YGPQ$mMpW zQYpiNW%QACe&b4_+d~mTy1nd^mwjc{{U6(mW84c^<^`+9eG_aC@5M8b9E z=H?n8Z+?E>gYaNvWF&64+t%^%v3q!USUx&BDxaL3jJ8^>5)#sk<2a!tJ}pZT!Lka8 z>%;P`JYJwGyiq|oIY{W*($dmXt~T$;sw6|mmh%F@|4raw9UoR{A=$`M0jw0sH3~gb zKio_1Rse%S;@KL!ZlnMuF2;x8<>&DN4~q;ziA`9bfSkK@4wz1AtwQuc;&a!%kHjKD z9TGS-H5EXS5JF>7>%uBjFZ2X3zwsfoCM465sUV6_eFb znaB$!k;aAguEgwQtid(In220YZl^@+0_tM?pqmrP3!xa1Y>gueW6uyu*0gb~L<|4k7|64yKE7Qz#WT~@{@iQP{M`j{b`{Jh>ccP`B@?K z9TVk1%y}@3ut{=|0HksCmBcc6Sb?IGI1@|XNlc2XR2&T>dASl9*Gb|8FzRrC={&@e zAOHjTXP}Tqp##}AFgS#zTLfe&@IGsQPg{Rj^MLe%#?^NS>DbGgNM5lQa^+`v;5mQ% zVXB0wA*Fy$;^(QgP<|D9T|%gr7o5C0JZul-%|lk+Jb+Ef&~iY_zOD!Ae0@uQILr_d z;OqS;V2XLb!j8zsp47qy+bLQBl1Z`&17Q*J%`64v%kSx?*=&A98wviEXB-?HI7KRe zt*JwQD9+B#O2Fc3TxgFa=B}VtS6AQO-roL$Tu7U9Y%vR+iD?EV@bH35Sb7~Xx#Ayw z6`}WIm$C2h^@kh2zm+kF2$}~P{BP(Ywk}RYu>N?Fb7p1S@cqDJ7+WZgoz$h@iu?n$ z%J=%`kxHFW71i{=A5$2(!`5%}y|1fF#D@_&!+p*VYlu5Cd72rE^Rfkje1sVry^lf*-xnJ_j$O?Z!0R4H7p+Y0!S`LeYu9Z52`~V1 WdyupwJ%7;v0000 Date: Thu, 6 Oct 2016 13:25:56 -0400 Subject: [PATCH 67/80] Bug 1307445 part B - remove the gecko code that support :moz-type-unsupported and :moz-type-unsupported-platform, r=qdot MozReview-Commit-ID: Dybppkfrb7E --HG-- extra : rebase_source : 8dedfafb0edefa54b4a73a0f0204be46f989c87f --- dom/base/nsObjectLoadingContent.cpp | 10 +- dom/base/test/chrome/chrome.ini | 3 - dom/base/test/chrome/file_bug391728.html | 85 --------- dom/base/test/chrome/file_bug391728_2.html | 85 --------- dom/base/test/chrome/test_bug391728.html | 201 --------------------- dom/base/test/mochitest.ini | 1 - dom/base/test/test_bug368972.html | 26 ++- dom/base/test/test_bug425013.html | 85 --------- dom/events/EventStates.h | 4 - layout/style/nsCSSPseudoClassList.h | 6 - 10 files changed, 17 insertions(+), 489 deletions(-) delete mode 100644 dom/base/test/chrome/file_bug391728.html delete mode 100644 dom/base/test/chrome/file_bug391728_2.html delete mode 100644 dom/base/test/chrome/test_bug391728.html delete mode 100644 dom/base/test/test_bug425013.html diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 2725cc52461f..8a5b2aa9237d 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -1453,15 +1453,7 @@ nsObjectLoadingContent::ObjectState() const return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_BLOCKED; case eFallbackCrashed: return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_CRASHED; - case eFallbackUnsupported: { - // Check to see if plugins are blocked on this platform. - char* pluginsBlocked = PR_GetEnv("MOZ_PLUGINS_BLOCKED"); - if (pluginsBlocked && pluginsBlocked[0] == '1') { - return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM; - } else { - return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_TYPE_UNSUPPORTED; - } - } + case eFallbackUnsupported: case eFallbackOutdated: case eFallbackAlternate: return NS_EVENT_STATE_BROKEN; diff --git a/dom/base/test/chrome/chrome.ini b/dom/base/test/chrome/chrome.ini index 68bb9a063e44..a1ed9ec24fae 100644 --- a/dom/base/test/chrome/chrome.ini +++ b/dom/base/test/chrome/chrome.ini @@ -6,8 +6,6 @@ support-files = bug418986-1.js cpows_child.js cpows_parent.xul - file_bug391728.html - file_bug391728_2.html file_bug549682.xul file_bug616841.xul file_bug816340.xul @@ -36,7 +34,6 @@ support-files = ../file_bug357450.js [test_bug380418.html] [test_bug380418.html^headers^] [test_bug383430.html] -[test_bug391728.html] [test_bug418986-1.xul] [test_bug421622.xul] [test_bug429785.xul] diff --git a/dom/base/test/chrome/file_bug391728.html b/dom/base/test/chrome/file_bug391728.html deleted file mode 100644 index da591f49bfb2..000000000000 --- a/dom/base/test/chrome/file_bug391728.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - -
-
- - -
-
- - -
- -
-
- -
- - -
- - -
-
- - -
- - -
-

Fallback content

-
-
-

Fallback content

-
- - -
- -
-
- -
- - -
- -
- -
- - Fallback content -
- - - diff --git a/dom/base/test/chrome/file_bug391728_2.html b/dom/base/test/chrome/file_bug391728_2.html deleted file mode 100644 index ad405241191d..000000000000 --- a/dom/base/test/chrome/file_bug391728_2.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - -
-
- - -
-
- - -
- -
-
- -
- - -
- - -
-
- - -
- - -
-

Fallback content

-
-
-

Fallback content

-
- - -
- -
-
- -
- - -
- -
- -
- - Fallback content -
- - - diff --git a/dom/base/test/chrome/test_bug391728.html b/dom/base/test/chrome/test_bug391728.html deleted file mode 100644 index edd0d0d1198e..000000000000 --- a/dom/base/test/chrome/test_bug391728.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - Test for Bug 391728 - - - - - -Mozilla Bug 391728 -

-
- -
-
-
-
- - diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 894a44343497..f66a382f3cca 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -391,7 +391,6 @@ skip-if = buildapp == 'b2g' skip-if = buildapp == 'b2g' [test_bug424359-2.html] skip-if = buildapp == 'b2g' -[test_bug425013.html] [test_bug426308.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') [test_bug426646.html] diff --git a/dom/base/test/test_bug368972.html b/dom/base/test/test_bug368972.html index 8883ef7a1672..74c6c055eb5b 100644 --- a/dom/base/test/test_bug368972.html +++ b/dom/base/test/test_bug368972.html @@ -80,31 +80,37 @@ Object height=100 (stylesheet width:400px height:400px) function check_size(id, width, height) { var element = document.getElementById(id); ok(element, "Plugin element " + id + " did not exist"); + if (width != "auto") { + width = width + "px"; + } + if (height != "auto") { + height = height + "px"; + } var style = window.getComputedStyle(element, null); - is(style.width, width + "px", "Plugin element " + id + " had an incorrect width"); - is(style.height, height + "px", "Plugin element " + id + " had an incorrect height"); + is(style.width, width, "Plugin element " + id + " had an incorrect width"); + is(style.height, height, "Plugin element " + id + " had an incorrect height"); } -check_size("embed1", 240, 200); +check_size("embed1", "auto", "auto"); check_size("embed2", 0, 0); check_size("embed3", 100, 100); -check_size("embed4", 240, 100); -check_size("embed5", 100, 200); +check_size("embed4", "auto", 100); +check_size("embed5", 100, "auto"); check_size("embed6", 100, 100); check_size("embed7", 100, 100); -check_size("embed8", 240, 100); +check_size("embed8", "auto", 100); check_size("embed9", 400, 100); check_size("embed10", 400, 100); check_size("embed11", 400, 400); -check_size("object1", 240, 200); +check_size("object1", "auto", "auto"); check_size("object2", 0, 0); check_size("object3", 100, 100); -check_size("object4", 240, 100); -check_size("object5", 100, 200); +check_size("object4", "auto", 100); +check_size("object5", 100, "auto"); check_size("object6", 100, 100); check_size("object7", 100, 100); -check_size("object8", 240, 100); +check_size("object8", "auto", 100); check_size("object9", 400, 100); check_size("object10", 400, 100); check_size("object11", 400, 400); diff --git a/dom/base/test/test_bug425013.html b/dom/base/test/test_bug425013.html deleted file mode 100644 index 66af670aa934..000000000000 --- a/dom/base/test/test_bug425013.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - Test for Bug 425013 - - - - - -Mozilla Bug 425013 -
- - - - - - - - - - - - - - - -

Alternate content

-
- - - - - -

Alternate content

-
- - - - - - - diff --git a/dom/events/EventStates.h b/dom/events/EventStates.h index fdc0dcf77c93..2672d2897d56 100644 --- a/dom/events/EventStates.h +++ b/dom/events/EventStates.h @@ -254,8 +254,6 @@ private: // Content is still loading such that there is nothing to show the // user (eg an image which hasn't started coming in yet). #define NS_EVENT_STATE_LOADING NS_DEFINE_EVENT_STATE_MACRO(23) -// Content is of a type that gecko can't handle. -#define NS_EVENT_STATE_TYPE_UNSUPPORTED NS_DEFINE_EVENT_STATE_MACRO(24) #define NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL NS_DEFINE_EVENT_STATE_MACRO(25) // Handler for the content has been blocked. #define NS_EVENT_STATE_HANDLER_BLOCKED NS_DEFINE_EVENT_STATE_MACRO(26) @@ -288,8 +286,6 @@ private: #define NS_EVENT_STATE_VULNERABLE_UPDATABLE NS_DEFINE_EVENT_STATE_MACRO(39) // Handler for click to play plugin (vulnerable w/no update) #define NS_EVENT_STATE_VULNERABLE_NO_UPDATE NS_DEFINE_EVENT_STATE_MACRO(40) -// Platform does not support plugin content (some mobile platforms) -#define NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM NS_DEFINE_EVENT_STATE_MACRO(41) // Element is ltr (for :dir pseudo-class) #define NS_EVENT_STATE_LTR NS_DEFINE_EVENT_STATE_MACRO(42) // Element is rtl (for :dir pseudo-class) diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index bd8ba27e5397..70157833833e 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -188,12 +188,6 @@ CSS_STATE_PSEUDO_CLASS(mozUserDisabled, ":-moz-user-disabled", CSS_STATE_PSEUDO_CLASS(mozSuppressed, ":-moz-suppressed", CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", NS_EVENT_STATE_SUPPRESSED) -CSS_STATE_PSEUDO_CLASS(mozTypeUnsupported, ":-moz-type-unsupported", - CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", - NS_EVENT_STATE_TYPE_UNSUPPORTED) -CSS_STATE_PSEUDO_CLASS(mozTypeUnsupportedPlatform, ":-moz-type-unsupported-platform", - CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", - NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM) CSS_STATE_PSEUDO_CLASS(mozHandlerClickToPlay, ":-moz-handler-clicktoplay", CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", NS_EVENT_STATE_TYPE_CLICK_TO_PLAY) From 3d548216f04e19620efcaaaea0bf1f4ad4572ca3 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Tue, 18 Oct 2016 09:16:03 -0400 Subject: [PATCH 68/80] Bug 1295093 - [mozharness] Allow excluding flavors when determining whether to use StructuredOutputParser, r=ahal This refactors the mozharness configs such that a specific flavor of an overall suite can still use the old DesktopUnittestOutputParser. This is necessary because mochitest-jetpack still does not use the structured logger. MozReview-Commit-ID: E8EpSLH4xt2 --HG-- extra : rebase_source : be18e3e4aa1f693b563a82b90fdda2f93201d9ee --- .../configs/unittests/linux_unittest.py | 11 ++++-- .../configs/unittests/mac_unittest.py | 9 ++++- .../configs/unittests/win_unittest.py | 9 ++++- .../mozharness/scripts/desktop_unittest.py | 36 ++++++++++++++----- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/testing/mozharness/configs/unittests/linux_unittest.py b/testing/mozharness/configs/unittests/linux_unittest.py index ee1d81cf5727..477aa380b844 100644 --- a/testing/mozharness/configs/unittests/linux_unittest.py +++ b/testing/mozharness/configs/unittests/linux_unittest.py @@ -210,7 +210,7 @@ config = { "tests": ["tests/reftest/tests/testing/crashtest/crashtests.list"] }, "jsreftest": { - "options":["--extra-profile-file=tests/jsreftest/tests/user.js", + "options": ["--extra-profile-file=tests/jsreftest/tests/user.js", "--suite=jstestbrowser"], "tests": ["tests/jsreftest/tests/jstests.list"] }, @@ -305,7 +305,14 @@ config = { "default_blob_upload_servers": [ "https://blobupload.elasticbeanstalk.com", ], - "structured_suites": ["reftest"], + "unstructured_flavors": {"mochitest": [], + "xpcshell": [], + "gtest": [], + "mozmill": [], + "cppunittest": [], + "jittest": [], + "mozbase": [], + }, "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"), "download_minidump_stackwalk": True, "minidump_stackwalk_path": MINIDUMP_STACKWALK_PATH, diff --git a/testing/mozharness/configs/unittests/mac_unittest.py b/testing/mozharness/configs/unittests/mac_unittest.py index 96e39c11facc..417bdfae1cc2 100644 --- a/testing/mozharness/configs/unittests/mac_unittest.py +++ b/testing/mozharness/configs/unittests/mac_unittest.py @@ -254,7 +254,14 @@ config = { "default_blob_upload_servers": [ "https://blobupload.elasticbeanstalk.com", ], - "structured_suites": ["reftest"], + "unstructured_flavors": {"mochitest": [], + "xpcshell": [], + "gtest": [], + "mozmill": [], + "cppunittest": [], + "jittest": [], + "mozbase": [], + }, "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"), "download_minidump_stackwalk": True, "minidump_stackwalk_path": "macosx64-minidump_stackwalk", diff --git a/testing/mozharness/configs/unittests/win_unittest.py b/testing/mozharness/configs/unittests/win_unittest.py index 2dc975cd579b..00e63b56ba39 100644 --- a/testing/mozharness/configs/unittests/win_unittest.py +++ b/testing/mozharness/configs/unittests/win_unittest.py @@ -274,7 +274,14 @@ config = { "default_blob_upload_servers": [ "https://blobupload.elasticbeanstalk.com", ], - "structured_suites": ["reftest"], + "unstructured_flavors": {"mochitest": [], + "xpcshell": [], + "gtest": [], + "mozmill": [], + "cppunittest": [], + "jittest": [], + "mozbase": [], + }, "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"), "download_minidump_stackwalk": True, "minidump_stackwalk_path": "win32-minidump_stackwalk.exe", diff --git a/testing/mozharness/scripts/desktop_unittest.py b/testing/mozharness/scripts/desktop_unittest.py index 304089e8d259..f1e354e2bb56 100755 --- a/testing/mozharness/scripts/desktop_unittest.py +++ b/testing/mozharness/scripts/desktop_unittest.py @@ -28,6 +28,8 @@ from mozharness.base.vcs.vcsbase import MercurialScript from mozharness.mozilla.blob_upload import BlobUploadMixin, blobupload_config_options from mozharness.mozilla.buildbot import TBPL_EXCEPTION from mozharness.mozilla.mozbase import MozbaseMixin +from mozharness.mozilla.structuredlog import StructuredOutputParser +from mozharness.mozilla.testing.unittest import DesktopUnittestOutputParser from mozharness.mozilla.testing.codecoverage import ( CodeCoverageMixin, code_coverage_config_options @@ -37,6 +39,7 @@ from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_opt SUITE_CATEGORIES = ['gtest', 'cppunittest', 'jittest', 'mochitest', 'reftest', 'xpcshell', 'mozbase', 'mozmill'] SUITE_DEFAULT_E10S = ['mochitest', 'reftest'] + # DesktopUnittest {{{1 class DesktopUnittest(TestingMixin, MercurialScript, BlobUploadMixin, MozbaseMixin, CodeCoverageMixin): config_options = [ @@ -405,7 +408,10 @@ class DesktopUnittest(TestingMixin, MercurialScript, BlobUploadMixin, MozbaseMix option = option % str_format_values if not option.endswith('None'): base_cmd.append(option) - if self.structured_output(suite_category): + if self.structured_output( + suite_category, + self._query_try_flavor(suite_category, suite) + ): base_cmd.append("--log-raw=-") return base_cmd else: @@ -415,12 +421,6 @@ class DesktopUnittest(TestingMixin, MercurialScript, BlobUploadMixin, MozbaseMix "config under %s_options" % (suite_category, suite_category)) - - for option in options: - option = option % str_format_values - if not option.endswith('None'): - base_cmd.append(option) - return base_cmd else: self.fatal("'binary_path' could not be determined.\n This should " @@ -462,7 +462,8 @@ class DesktopUnittest(TestingMixin, MercurialScript, BlobUploadMixin, MozbaseMix "mochitest": [("plain.*", "mochitest"), ("browser-chrome.*", "browser-chrome"), ("mochitest-devtools-chrome.*", "devtools-chrome"), - ("chrome", "chrome")], + ("chrome", "chrome"), + ("jetpack.*", "jetpack")], "xpcshell": [("xpcshell", "xpcshell")], "reftest": [("reftest", "reftest"), ("crashtest", "crashtest")] @@ -471,6 +472,23 @@ class DesktopUnittest(TestingMixin, MercurialScript, BlobUploadMixin, MozbaseMix if re.compile(suite_pattern).match(suite): return flavor + def structured_output(self, suite_category, flavor=None): + unstructured_flavors = self.config.get('unstructured_flavors') + if not unstructured_flavors: + return False + if suite_category not in unstructured_flavors: + return True + if not unstructured_flavors.get(suite_category) or flavor in unstructured_flavors.get(suite_category): + return False + return True + + def get_test_output_parser(self, suite_category, flavor=None, strict=False, + **kwargs): + if not self.structured_output(suite_category, flavor): + return DesktopUnittestOutputParser(suite_category=suite_category, **kwargs) + self.info("Structured output parser in use for %s." % suite_category) + return StructuredOutputParser(suite_category=suite_category, strict=strict, **kwargs) + # Actions {{{2 # clobber defined in BaseScript, deletes mozharness/build if exists @@ -605,7 +623,6 @@ class DesktopUnittest(TestingMixin, MercurialScript, BlobUploadMixin, MozbaseMix 'resources', module)) - # pull defined in VCSScript. # preflight_run_tests defined in TestingMixin. @@ -664,6 +681,7 @@ class DesktopUnittest(TestingMixin, MercurialScript, BlobUploadMixin, MozbaseMix 'level': ERROR, }] parser = self.get_test_output_parser(suite_category, + flavor=flavor, config=self.config, error_list=error_list, log_obj=self.log_obj) From 07b45aaa267984de9372d30d0d3f094d01eac578 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Tue, 18 Oct 2016 09:32:57 -0400 Subject: [PATCH 69/80] Fix merge conflict bustage for removed file from bug 1307445 part A. MozReview-Commit-ID: LQ5tZclbtb6 --HG-- extra : rebase_source : 01447300d41eaa074cf11658ceb8f135c7aeb27e --- toolkit/pluginproblem/jar.mn | 1 - 1 file changed, 1 deletion(-) diff --git a/toolkit/pluginproblem/jar.mn b/toolkit/pluginproblem/jar.mn index 7713e96e45b2..d0af1c82fb29 100644 --- a/toolkit/pluginproblem/jar.mn +++ b/toolkit/pluginproblem/jar.mn @@ -8,4 +8,3 @@ toolkit.jar: pluginproblem/pluginProblemContent.css (content/pluginProblemContent.css) pluginproblem/pluginProblemBinding.css (content/pluginProblemBinding.css) pluginproblem/pluginReplaceBinding.css (content/pluginReplaceBinding.css) - pluginproblem/pluginFinderBinding.css (content/pluginFinderBinding.css) From 225c6de9ba2b015bed565b8448154a1edce1b6e7 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 13 Oct 2016 15:08:53 -0400 Subject: [PATCH 70/80] Bug 1309867 - Part 1: Create FontFaceSet's ready promise lazily; r=heycam This should help avoid calling into the JS engine in the middle of painting. --- layout/style/FontFaceSet.cpp | 36 +++++++++++++++++++++--------------- layout/style/FontFaceSet.h | 3 +++ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp index 5eeebe72dbc4..01959d7bcdd0 100644 --- a/layout/style/FontFaceSet.cpp +++ b/layout/style/FontFaceSet.cpp @@ -97,6 +97,7 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument) : DOMEventTargetHelper(aWindow) , mDocument(aDocument) + , mResolveLazilyCreatedReadyPromise(false) , mStatus(FontFaceSetLoadStatus::Loaded) , mNonRuleFacesDirty(false) , mHasLoadingFontFaces(false) @@ -111,12 +112,7 @@ FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument) // be able to get to anyway) as it causes the window.FontFaceSet constructor // to be created. if (global && PrefEnabled()) { - ErrorResult rv; - mReady = Promise::Create(global, rv); - } - - if (mReady) { - mReady->MaybeResolve(this); + mResolveLazilyCreatedReadyPromise = true; } if (!mDocument->DidFireDOMContentLoaded()) { @@ -386,8 +382,16 @@ Promise* FontFaceSet::GetReady(ErrorResult& aRv) { if (!mReady) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; + nsCOMPtr global = GetParentObject(); + mReady = Promise::Create(global, aRv); + if (!mReady) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + if (mResolveLazilyCreatedReadyPromise) { + mReady->MaybeResolve(this); + mResolveLazilyCreatedReadyPromise = false; + } } FlushUserFontSet(); @@ -1505,14 +1509,14 @@ FontFaceSet::CheckLoadingStarted() false))->RunDOMEventWhenSafe(); if (PrefEnabled()) { - RefPtr ready; - if (GetParentObject()) { - ErrorResult rv; - ready = Promise::Create(GetParentObject(), rv); + if (mReady) { + if (GetParentObject()) { + ErrorResult rv; + mReady = Promise::Create(GetParentObject(), rv); + } } - - if (ready) { - mReady.swap(ready); + if (!mReady) { + mResolveLazilyCreatedReadyPromise = false; } } } @@ -1599,6 +1603,8 @@ FontFaceSet::CheckLoadingFinished() mStatus = FontFaceSetLoadStatus::Loaded; if (mReady) { mReady->MaybeResolve(this); + } else { + mResolveLazilyCreatedReadyPromise = true; } // Now dispatch the loadingdone/loadingerror events. diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h index 58e4982fe513..78fe512d5590 100644 --- a/layout/style/FontFaceSet.h +++ b/layout/style/FontFaceSet.h @@ -314,7 +314,10 @@ private: // any of those fonts failed to load. mReady is replaced with // a new Promise object whenever mReady is settled and another // FontFace in mRuleFaces or mNonRuleFaces starts to load. + // Note that mReady is created lazily when GetReady() is called. RefPtr mReady; + // Whether the ready promise must be resolved when it's created. + bool mResolveLazilyCreatedReadyPromise; // Set of all loaders pointing to us. These are not strong pointers, // but that's OK because nsFontFaceLoader always calls RemoveLoader on From 3697917fc42be803e4f3e56b76098243ce847c4e Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Tue, 18 Oct 2016 09:38:21 -0400 Subject: [PATCH 71/80] Bug 1309867 - Part 2: Lazily create FontFace::mLoaded; r=heycam --- layout/style/FontFace.cpp | 73 ++++++++++++++++++++++++--------------- layout/style/FontFace.h | 16 +++++++-- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/layout/style/FontFace.cpp b/layout/style/FontFace.cpp index 968b0eea0643..761e57285862 100644 --- a/layout/style/FontFace.cpp +++ b/layout/style/FontFace.cpp @@ -101,6 +101,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(FontFace) FontFace::FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet) : mParent(aParent) + , mLoadedRejection(NS_OK) , mStatus(FontFaceLoadStatus::Unloaded) , mSourceType(SourceType(0)) , mSourceBuffer(nullptr) @@ -109,16 +110,6 @@ FontFace::FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet) , mInFontFaceSet(false) { MOZ_COUNT_CTOR(FontFace); - - nsCOMPtr global = do_QueryInterface(aParent); - - // If the pref is not set, don't create the Promise (which the page wouldn't - // be able to get to anyway) as it causes the window.FontFace constructor - // to be created. - if (global && FontFaceSet::PrefEnabled()) { - ErrorResult rv; - mLoaded = Promise::Create(global, rv); - } } FontFace::~FontFace() @@ -198,13 +189,11 @@ FontFace::InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource) if (!ParseDescriptor(eCSSFontDesc_Src, aSource.GetAsString(), mDescriptors->mSrc)) { - if (mLoaded) { - // The SetStatus call we are about to do assumes that for - // FontFace objects with sources other than ArrayBuffer(View)s, that the - // mLoaded Promise is rejected with a network error. We get - // in here beforehand to set it to the required syntax error. - mLoaded->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); - } + // The SetStatus call we are about to do assumes that for + // FontFace objects with sources other than ArrayBuffer(View)s, that the + // mLoaded Promise is rejected with a network error. We get + // in here beforehand to set it to the required syntax error. + Reject(NS_ERROR_DOM_SYNTAX_ERR); SetStatus(FontFaceLoadStatus::Error); return; @@ -378,6 +367,8 @@ FontFace::Load(ErrorResult& aRv) { mFontFaceSet->FlushUserFontSet(); + EnsurePromise(); + if (!mLoaded) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -433,6 +424,8 @@ FontFace::GetLoaded(ErrorResult& aRv) { mFontFaceSet->FlushUserFontSet(); + EnsurePromise(); + if (!mLoaded) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -467,17 +460,15 @@ FontFace::SetStatus(FontFaceLoadStatus aStatus) otherSet->OnFontFaceStatusChanged(this); } - if (!mLoaded) { - return; - } - if (mStatus == FontFaceLoadStatus::Loaded) { - mLoaded->MaybeResolve(this); + if (mLoaded) { + mLoaded->MaybeResolve(this); + } } else if (mStatus == FontFaceLoadStatus::Error) { if (mSourceType == eSourceType_Buffer) { - mLoaded->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); + Reject(NS_ERROR_DOM_SYNTAX_ERR); } else { - mLoaded->MaybeReject(NS_ERROR_DOM_NETWORK_ERR); + Reject(NS_ERROR_DOM_NETWORK_ERR); } } } @@ -570,9 +561,7 @@ FontFace::SetDescriptors(const nsAString& aFamily, // on the FontFace. mDescriptors = new CSSFontFaceDescriptors; - if (mLoaded) { - mLoaded->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); - } + Reject(NS_ERROR_DOM_SYNTAX_ERR); SetStatus(FontFaceLoadStatus::Error); return false; @@ -748,6 +737,36 @@ FontFace::RemoveFontFaceSet(FontFaceSet* aFontFaceSet) } } +void +FontFace::Reject(nsresult aResult) +{ + if (mLoaded) { + mLoaded->MaybeReject(aResult); + } else { + mLoadedRejection = aResult; + } +} + +void +FontFace::EnsurePromise() +{ + nsCOMPtr global = do_QueryInterface(mParent); + + // If the pref is not set, don't create the Promise (which the page wouldn't + // be able to get to anyway) as it causes the window.FontFace constructor + // to be created. + if (global && FontFaceSet::PrefEnabled()) { + ErrorResult rv; + mLoaded = Promise::Create(global, rv); + + if (mStatus == FontFaceLoadStatus::Loaded) { + mLoaded->MaybeResolve(this); + } else if (mLoadedRejection != NS_OK) { + mLoaded->MaybeReject(mLoadedRejection); + } + } +} + // -- FontFace::Entry -------------------------------------------------------- /* virtual */ void diff --git a/layout/style/FontFace.h b/layout/style/FontFace.h index 8da40f53becf..29171a6da672 100644 --- a/layout/style/FontFace.h +++ b/layout/style/FontFace.h @@ -199,12 +199,24 @@ private: */ void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength); + // Acts like mLoaded->MaybeReject(aResult), except it doesn't create mLoaded + // if it doesn't already exist. + void Reject(nsresult aResult); + + // Creates mLoaded if it doesn't already exist. It may immediately resolve or + // reject mLoaded based on mStatus and mLoadedRejection. + void EnsurePromise(); + nsCOMPtr mParent; - // A Promise that is fulfilled once the font represented by this FontFace - // is loaded, and is rejected if the load fails. + // A Promise that is fulfilled once the font represented by this FontFace is + // loaded, and is rejected if the load fails. This promise is created lazily + // when JS asks for it. RefPtr mLoaded; + // Saves the rejection code for mLoaded if mLoaded hasn't been created yet. + nsresult mLoadedRejection; + // The @font-face rule this FontFace object is reflecting, if it is a // rule backed FontFace. RefPtr mRule; From f13c0113699121129263329be1a754a63545273b Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 17 Oct 2016 22:40:30 -0400 Subject: [PATCH 72/80] Bug 1310895 - Remove support for app default and manifest CSP enforcement; r=baku --- dom/apps/AppsService.js | 16 ----- dom/apps/AppsServiceChild.jsm | 10 --- dom/apps/AppsUtils.jsm | 37 ---------- dom/base/nsDocument.cpp | 70 ++----------------- dom/base/nsFrameLoader.cpp | 1 + .../test/test_app_permissions.html | 8 +-- .../test/test_fs_app_permissions.html | 8 +-- dom/interfaces/apps/nsIAppsService.idl | 10 --- dom/security/test/csp/file_bug768029.sjs | 29 -------- modules/libpref/init/all.js | 3 - 10 files changed, 8 insertions(+), 184 deletions(-) delete mode 100644 dom/security/test/csp/file_bug768029.sjs diff --git a/dom/apps/AppsService.js b/dom/apps/AppsService.js index 1100ad6c495d..e0a7ea041c36 100644 --- a/dom/apps/AppsService.js +++ b/dom/apps/AppsService.js @@ -37,22 +37,6 @@ AppsService.prototype = { localId == Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID); }, - getManifestCSPByLocalId: function getCSPByLocalId(localId) { - debug("GetManifestCSPByLocalId( " + localId + " )"); - if (this.isInvalidId(localId)) { - return null; - } - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - getDefaultCSPByLocalId: function getCSPByLocalId(localId) { - debug("GetDefaultCSPByLocalId( " + localId + " )"); - if (this.isInvalidId(localId)) { - return null; - } - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - getAppByManifestURL: function getAppByManifestURL(aManifestURL) { debug("GetAppByManifestURL( " + aManifestURL + " )"); throw Cr.NS_ERROR_NOT_IMPLEMENTED; diff --git a/dom/apps/AppsServiceChild.jsm b/dom/apps/AppsServiceChild.jsm index e8adfe4ce2a9..e570c2ebbacc 100644 --- a/dom/apps/AppsServiceChild.jsm +++ b/dom/apps/AppsServiceChild.jsm @@ -371,16 +371,6 @@ this.DOMApplicationRegistry = { return AppsUtils.getAppLocalIdByManifestURL(this.webapps, aManifestURL); }, - getManifestCSPByLocalId: function(aLocalId) { - debug("getManifestCSPByLocalId:" + aLocalId); - return AppsUtils.getManifestCSPByLocalId(this.webapps, aLocalId); - }, - - getDefaultCSPByLocalId: function(aLocalId) { - debug("getDefaultCSPByLocalId:" + aLocalId); - return AppsUtils.getDefaultCSPByLocalId(this.webapps, aLocalId); - }, - getAppLocalIdByStoreId: function(aStoreId) { debug("getAppLocalIdByStoreId:" + aStoreId); return AppsUtils.getAppLocalIdByStoreId(this.webapps, aStoreId); diff --git a/dom/apps/AppsUtils.jsm b/dom/apps/AppsUtils.jsm index 6df9bbc84a0e..3ca3f8552a22 100644 --- a/dom/apps/AppsUtils.jsm +++ b/dom/apps/AppsUtils.jsm @@ -262,43 +262,6 @@ this.AppsUtils = { return Ci.nsIScriptSecurityManager.NO_APP_ID; }, - getManifestCSPByLocalId: function getManifestCSPByLocalId(aApps, aLocalId) { - debug("getManifestCSPByLocalId " + aLocalId); - for (let id in aApps) { - let app = aApps[id]; - if (app.localId == aLocalId) { - return ( app.csp || "" ); - } - } - - return ""; - }, - - getDefaultCSPByLocalId: function(aApps, aLocalId) { - debug("getDefaultCSPByLocalId " + aLocalId); - for (let id in aApps) { - let app = aApps[id]; - if (app.localId == aLocalId) { - // Use the app status to choose the right default CSP. - try { - switch (app.appStatus) { - case Ci.nsIPrincipal.APP_STATUS_CERTIFIED: - return Services.prefs.getCharPref("security.apps.certified.CSP.default"); - break; - case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED: - return Services.prefs.getCharPref("security.apps.privileged.CSP.default"); - break; - case Ci.nsIPrincipal.APP_STATUS_INSTALLED: - return ""; - break; - } - } catch(e) {} - } - } - - return "default-src 'self'; object-src 'none'"; - }, - getAppByLocalId: function getAppByLocalId(aApps, aLocalId) { debug("getAppByLocalId " + aLocalId); for (let id in aApps) { diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 99cf667c359b..8c8a67cfb58f 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -203,7 +203,6 @@ #include "nsWrapperCacheInlines.h" #include "nsSandboxFlags.h" #include "nsIAddonPolicyService.h" -#include "nsIAppsService.h" #include "mozilla/dom/AnimatableBinding.h" #include "mozilla/dom/AnonymousContent.h" #include "mozilla/dom/BindingUtils.h" @@ -2544,32 +2543,9 @@ nsDocument::InitCSP(nsIChannel* aChannel) NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue); NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue); - // Figure out if we need to apply an app default CSP or a CSP from an app manifest - nsCOMPtr principal = NodePrincipal(); - - uint16_t appStatus = principal->GetAppStatus(); - bool applyAppDefaultCSP = false; - bool applyAppManifestCSP = false; - - nsAutoString appManifestCSP; - nsAutoString appDefaultCSP; - if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) { - nsCOMPtr appsService = do_GetService(APPS_SERVICE_CONTRACTID); - if (appsService) { - uint32_t appId = principal->GetAppId(); - appsService->GetManifestCSPByLocalId(appId, appManifestCSP); - if (!appManifestCSP.IsEmpty()) { - applyAppManifestCSP = true; - } - appsService->GetDefaultCSPByLocalId(appId, appDefaultCSP); - if (!appDefaultCSP.IsEmpty()) { - applyAppDefaultCSP = true; - } - } - } - // Check if this is a document from a WebExtension. nsString addonId; + nsCOMPtr principal = NodePrincipal(); principal->GetAddonId(addonId); bool applyAddonCSP = !addonId.IsEmpty(); @@ -2581,9 +2557,7 @@ nsDocument::InitCSP(nsIChannel* aChannel) } // If there's no CSP to apply, go ahead and return early - if (!applyAppDefaultCSP && - !applyAppManifestCSP && - !applyAddonCSP && + if (!applyAddonCSP && !applySignedContentCSP && cspHeaderValue.IsEmpty() && cspROHeaderValue.IsEmpty()) { @@ -2593,53 +2567,19 @@ nsDocument::InitCSP(nsIChannel* aChannel) nsAutoCString aspec; chanURI->GetAsciiSpec(aspec); MOZ_LOG(gCspPRLog, LogLevel::Debug, - ("no CSP for document, %s, %s", - aspec.get(), - applyAppDefaultCSP ? "is app" : "not an app")); + ("no CSP for document, %s", + aspec.get())); } return NS_OK; } - MOZ_LOG(gCspPRLog, LogLevel::Debug, ("Document is an app or CSP header specified %p", this)); - - // If Document is an app check to see if we already set CSP and return early - // if that is indeed the case. - // - // In general (see bug 947831), we should not be setting CSP on a principal - // that aliases another document. For non-app code this is not a problem - // since we only share the underlying principal with nested browsing - // contexts for which a header cannot be set (e.g., about:blank and - // about:srcodoc iframes) and thus won't try to set the CSP again. This - // check ensures that we do not try to set CSP for an app. - if (applyAppDefaultCSP || applyAppManifestCSP) { - nsCOMPtr csp; - rv = principal->GetCsp(getter_AddRefs(csp)); - NS_ENSURE_SUCCESS(rv, rv); - - if (csp) { - MOZ_LOG(gCspPRLog, LogLevel::Debug, ("%s %s %s", - "This document is sharing principal with another document.", - "Since the document is an app, CSP was already set.", - "Skipping attempt to set CSP.")); - return NS_OK; - } - } + MOZ_LOG(gCspPRLog, LogLevel::Debug, ("Document is an add-on or CSP header specified %p", this)); nsCOMPtr csp; rv = principal->EnsureCSP(this, getter_AddRefs(csp)); NS_ENSURE_SUCCESS(rv, rv); - // ----- if the doc is an app and we want a default CSP, apply it. - if (applyAppDefaultCSP) { - csp->AppendPolicy(appDefaultCSP, false, false); - } - - // ----- if the doc is an app and specifies a CSP in its manifest, apply it. - if (applyAppManifestCSP) { - csp->AppendPolicy(appManifestCSP, false, false); - } - // ----- if the doc is an addon, apply its CSP. if (applyAddonCSP) { nsCOMPtr aps = do_GetService("@mozilla.org/addons/policy-service;1"); diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index b1a44e7905fe..b2c83fdef4bc 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -15,6 +15,7 @@ #include "mozIApplication.h" #include "nsDocShell.h" +#include "nsIAppsService.h" #include "nsIDOMHTMLIFrameElement.h" #include "nsIDOMHTMLFrameElement.h" #include "nsIDOMMozBrowserFrame.h" diff --git a/dom/devicestorage/test/test_app_permissions.html b/dom/devicestorage/test/test_app_permissions.html index 234d576ee3c4..9d6ecad1900f 100644 --- a/dom/devicestorage/test/test_app_permissions.html +++ b/dom/devicestorage/test/test_app_permissions.html @@ -643,16 +643,10 @@ createTestFile('.txt'); var gTestRunner = runTest(); SpecialPowers.addPermission("browser", true, gTestUri); -// We are more permissive with CSP in our testing environment.... -const DEFAULT_CSP_PRIV = "default-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'"; -const DEFAULT_CSP_CERT = "default-src *; script-src 'self'; style-src 'self'; object-src 'none'"; - SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true], ["device.storage.enabled", true], ["device.storage.testing", true], - ["device.storage.prompt.testing", false], - ["security.apps.privileged.CSP.default", DEFAULT_CSP_PRIV], - ["security.apps.certified.CSP.default", DEFAULT_CSP_CERT]]}, + ["device.storage.prompt.testing", false]]}, function() { gTestRunner.next(); }); diff --git a/dom/devicestorage/test/test_fs_app_permissions.html b/dom/devicestorage/test/test_fs_app_permissions.html index 76ee0fcbbc78..103b9b6cf71a 100644 --- a/dom/devicestorage/test/test_fs_app_permissions.html +++ b/dom/devicestorage/test/test_fs_app_permissions.html @@ -818,16 +818,10 @@ function createTestFile(extension) { let gTestRunner = runTest(); SpecialPowers.addPermission("browser", true, gTestUri); -// We are more permissive with CSP in our testing environment.... -const DEFAULT_CSP_PRIV = "default-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'"; -const DEFAULT_CSP_CERT = "default-src *; script-src 'self'; style-src 'self'; object-src 'none'"; - SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true], ["device.storage.enabled", true], ["device.storage.testing", true], - ["device.storage.prompt.testing", false], - ["security.apps.privileged.CSP.default", DEFAULT_CSP_PRIV], - ["security.apps.certified.CSP.default", DEFAULT_CSP_CERT]]}, + ["device.storage.prompt.testing", false]]}, function() { gTestRunner.next(); }); diff --git a/dom/interfaces/apps/nsIAppsService.idl b/dom/interfaces/apps/nsIAppsService.idl index f388438fe0cd..4e91a50ecadc 100644 --- a/dom/interfaces/apps/nsIAppsService.idl +++ b/dom/interfaces/apps/nsIAppsService.idl @@ -46,16 +46,6 @@ interface nsIAppsService : nsISupports */ DOMString getManifestURLByLocalId(in unsigned long localId); - /** - * Returns the manifest CSP associated to this localId. - */ - DOMString getManifestCSPByLocalId(in unsigned long localId); - - /** - * Returns the default CSP associated to this localId. - */ - DOMString getDefaultCSPByLocalId(in unsigned long localId); - /** * Returns the basepath for core apps */ diff --git a/dom/security/test/csp/file_bug768029.sjs b/dom/security/test/csp/file_bug768029.sjs deleted file mode 100644 index 9ae353055ef9..000000000000 --- a/dom/security/test/csp/file_bug768029.sjs +++ /dev/null @@ -1,29 +0,0 @@ -function handleRequest(request, response) { - - var query = {}; - - request.queryString.split('&').forEach(function(val) { - var [name, value] = val.split('='); - query[name] = unescape(value); - }); - response.setHeader("Cache-Control", "no-cache", false); - - if ("type" in query) { - switch (query.type) { - case "script": - response.setHeader("Content-Type", "application/javascript"); - response.write("\n\ndocument.write('
script loaded\\n
');\n\n"); - return; - case "style": - response.setHeader("Content-Type", "text/css"); - response.write("\n\n.cspfoo { color:red; }\n\n"); - return; - case "img": - response.setHeader("Content-Type", "image/png"); - return; - } - } - - response.setHeader("Content-Type", "text/plain"); - response.write("ohnoes!"); -} diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index e900b7639103..bb398deb821a 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2139,9 +2139,6 @@ pref("security.notification_enable_delay", 500); pref("security.csp.enable", true); pref("security.csp.experimentalEnabled", false); -// Default Content Security Policy to apply to privileged apps. -pref("security.apps.privileged.CSP.default", "default-src * data: blob:; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'"); - // Default Content Security Policy to apply to signed contents. pref("security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"); From ee0b46b80e4c23bf0b35aaa8462326faba9eacea Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 18 Oct 2016 00:01:16 -0400 Subject: [PATCH 73/80] Bug 1310905 - Remove some b2g specific code from DOM notifications; r=baku --- dom/notification/DesktopNotification.cpp | 29 ------ dom/notification/Notification.cpp | 92 +---------------- dom/notification/NotificationStorage.js | 4 - .../test/unit/common_test_notificationdb.js | 2 +- .../test/unit/test_notificationdb.js | 98 ------------------- 5 files changed, 4 insertions(+), 221 deletions(-) diff --git a/dom/notification/DesktopNotification.cpp b/dom/notification/DesktopNotification.cpp index c3e01247d522..ed290074592c 100644 --- a/dom/notification/DesktopNotification.cpp +++ b/dom/notification/DesktopNotification.cpp @@ -14,7 +14,6 @@ #include "nsIDOMDesktopNotification.h" #include "mozilla/Preferences.h" #include "nsGlobalWindow.h" -#include "nsIAppsService.h" #include "nsIScriptSecurityManager.h" #include "nsServiceManagerUtils.h" #include "PermissionMessageUtils.h" @@ -73,34 +72,6 @@ DesktopNotification::PostDesktopNotification() mObserver = new AlertServiceObserver(this); } -#ifdef MOZ_B2G - nsCOMPtr appNotifier = - do_GetService("@mozilla.org/system-alerts-service;1"); - if (appNotifier) { - nsCOMPtr window = GetOwner(); - uint32_t appId = window ? window->GetDoc()->NodePrincipal()->GetAppId() - : nsIScriptSecurityManager::UNKNOWN_APP_ID; - - if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) { - nsCOMPtr appsService = do_GetService("@mozilla.org/AppsService;1"); - nsString manifestUrl = EmptyString(); - appsService->GetManifestURLByLocalId(appId, manifestUrl); - mozilla::AutoSafeJSContext cx; - JS::Rooted val(cx); - AppNotificationServiceOptions ops; - ops.mTextClickable = true; - ops.mManifestURL = manifestUrl; - - if (!ToJSValue(cx, ops, &val)) { - return NS_ERROR_FAILURE; - } - - return appNotifier->ShowAppNotification(mIconURL, mTitle, mDescription, - mObserver, val); - } - } -#endif - nsCOMPtr alerts = do_GetService("@mozilla.org/alerts-service;1"); if (!alerts) { return NS_ERROR_NOT_IMPLEMENTED; diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index d57a03340f9a..80e96e2f9343 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -31,7 +31,6 @@ #include "nsDOMJSUtils.h" #include "nsGlobalWindow.h" #include "nsIAlertsService.h" -#include "nsIAppsService.h" #include "nsIContentPermissionPrompt.h" #include "nsIDocument.h" #include "nsILoadContext.h" @@ -1506,15 +1505,6 @@ MainThreadNotificationObserver::Observe(nsISupports* aSubject, const char* aTopi } } } else if (!strcmp("alertfinished", aTopic)) { - // In b2g-desktop, if the app is closed, closing a notification still - // triggers the observer which might be alive even though the owner window - // was closed. Keeping this until we remove the close event (Bug 1139363) - // from implementation. - nsCOMPtr window = notification->GetOwner(); - if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) { - return NS_ERROR_FAILURE; - } - notification->UnpersistNotification(); notification->mIsClosed = true; notification->DispatchTrustedEvent(NS_LITERAL_STRING("close")); @@ -1784,52 +1774,6 @@ Notification::ShowInternal() IsInPrivateBrowsing()); -#ifdef MOZ_B2G - nsCOMPtr appNotifier = - do_GetService("@mozilla.org/system-alerts-service;1"); - if (appNotifier) { - uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID; - if (mWorkerPrivate) { - appId = mWorkerPrivate->GetPrincipal()->GetAppId(); - } else { - nsCOMPtr window = GetOwner(); - if (window) { - appId = window->GetDoc()->NodePrincipal()->GetAppId(); - } - } - - if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) { - nsCOMPtr appsService = do_GetService("@mozilla.org/AppsService;1"); - nsString manifestUrl = EmptyString(); - nsresult rv = appsService->GetManifestURLByLocalId(appId, manifestUrl); - if (NS_SUCCEEDED(rv)) { - mozilla::AutoSafeJSContext cx; - JS::Rooted val(cx); - AppNotificationServiceOptions ops; - ops.mTextClickable = true; - ops.mManifestURL = manifestUrl; - GetAlertName(ops.mId); - ops.mDbId = mID; - ops.mDir = DirectionToString(mDir); - ops.mLang = mLang; - ops.mTag = mTag; - ops.mData = mDataAsBase64; - ops.mMozbehavior = mBehavior; - ops.mMozbehavior.mSoundFile = soundUrl; - - if (!ToJSValue(cx, ops, &val)) { - NS_WARNING("Converting dict to object failed!"); - return; - } - - appNotifier->ShowAppNotification(iconUrl, mTitle, mBody, - alertObserver, val); - return; - } - } - } -#endif - // In the case of IPC, the parent process uses the cookie to map to // nsIObserver. Thus the cookie must be unique to differentiate observers. nsString uniqueCookie = NS_LITERAL_STRING("notification:"); @@ -2364,23 +2308,8 @@ Notification::GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin) return NS_ERROR_FAILURE; } - uint16_t appStatus = aPrincipal->GetAppStatus(); - uint32_t appId = aPrincipal->GetAppId(); - - nsresult rv; - if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED || - appId == nsIScriptSecurityManager::NO_APP_ID || - appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) { - rv = nsContentUtils::GetUTFOrigin(aPrincipal, aOrigin); - NS_ENSURE_SUCCESS(rv, rv); - } else { - // If we are in "app code", use manifest URL as unique origin since - // multiple apps can share the same origin but not same notifications. - nsCOMPtr appsService = - do_GetService("@mozilla.org/AppsService;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - appsService->GetManifestURLByLocalId(appId, aOrigin); - } + nsresult rv = nsContentUtils::GetUTFOrigin(aPrincipal, aOrigin); + NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -2827,22 +2756,7 @@ Notification::Observe(nsISupports* aSubject, const char* aTopic, obs->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC); } - uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED; - uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID; - - nsCOMPtr doc = window ? window->GetExtantDoc() : nullptr; - nsCOMPtr nodePrincipal = doc ? doc->NodePrincipal() : - nullptr; - if (nodePrincipal) { - appStatus = nodePrincipal->GetAppStatus(); - appId = nodePrincipal->GetAppId(); - } - - if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED || - appId == nsIScriptSecurityManager::NO_APP_ID || - appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) { - CloseInternal(); - } + CloseInternal(); } } diff --git a/dom/notification/NotificationStorage.js b/dom/notification/NotificationStorage.js index 636974d68dba..8186d61b88d1 100644 --- a/dom/notification/NotificationStorage.js +++ b/dom/notification/NotificationStorage.js @@ -23,10 +23,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", "nsIMessageSender"); -XPCOMUtils.defineLazyServiceGetter(this, "appsService", - "@mozilla.org/AppsService;1", - "nsIAppsService"); - const kMessageNotificationGetAllOk = "Notification:GetAll:Return:OK"; const kMessageNotificationGetAllKo = "Notification:GetAll:Return:KO"; const kMessageNotificationSaveKo = "Notification:Save:Return:KO"; diff --git a/dom/notification/test/unit/common_test_notificationdb.js b/dom/notification/test/unit/common_test_notificationdb.js index 83888edf4526..116c7836ad01 100644 --- a/dom/notification/test/unit/common_test_notificationdb.js +++ b/dom/notification/test/unit/common_test_notificationdb.js @@ -11,7 +11,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm", function getNotificationObject(app, id, tag) { return { - origin: "app://" + app + ".gaiamobile.org/manifest.webapp", + origin: "https://" + app + ".gaiamobile.org/", id: id, title: app + "Notification:" + Date.now(), dir: "auto", diff --git a/dom/notification/test/unit/test_notificationdb.js b/dom/notification/test/unit/test_notificationdb.js index 392df8b030e7..d631bd034558 100644 --- a/dom/notification/test/unit/test_notificationdb.js +++ b/dom/notification/test/unit/test_notificationdb.js @@ -308,101 +308,3 @@ add_test(function test_delete_previous() { requestID: requestID }); }); - -// Store two notifications, one without alertName and one with -add_test(function test_send_two_alertName() { - let requestID = 30; - let notifications = [ - { - origin: "app://system.gaiamobile.org/manifest.webapp", - id: "{27ead857-4f43-457f-a770-93b82fbfc223}", - title: "Notification title", - dir: "auto", - lang: "", - body: "Notification body", - tag: "", - icon: "icon.png", - timestamp: new Date().getTime() - }, { - origin: "app://system.gaiamobile.org/manifest.webapp", - id: "{40275e04-58d0-47be-8cc7-540578f793a4}", - title: "Notification title", - dir: "auto", - lang: "", - body: "Notification body", - tag: "", - icon: "icon.png", - alertName: "alertName", - timestamp: new Date().getTime() - } - ]; - let origin = notifications[0].origin; - - let msgGetCrossOriginReply = "Notification:GetAllCrossOrigin:Return:OK"; - let msgGetCrossOriginHandler = { - receiveMessage: function(message) { - if (message.name === msgGetCrossOriginReply) { - cpmm.removeMessageListener( - msgGetCrossOriginReply, msgGetCrossOriginHandler); - - let gotNotifications = message.data.notifications; - - // we expect to have one notification - do_check_eq(1, gotNotifications.length); - - // compare the only notification we should have got back - compareNotification(gotNotifications[0], notifications[1]); - - run_next_test(); - } - } - }; - cpmm.addMessageListener(msgGetCrossOriginReply, msgGetCrossOriginHandler); - - let msgGetReply = "Notification:GetAll:Return:OK"; - let msgGetHandler = { - receiveMessage: function(message) { - if (message.name === msgGetReply) { - cpmm.removeMessageListener(msgGetReply, msgGetHandler); - - let gotNotifications = message.data.notifications; - - // we expect to have two notifications - do_check_eq(2, gotNotifications.length); - - // compare each notification - for (let i = 0; i < gotNotifications.length; i++) { - compareNotification(gotNotifications[i], notifications[i]); - } - - run_next_test(); - } - } - }; - cpmm.addMessageListener(msgGetReply, msgGetHandler); - - let msgSaveReply = "Notification:Save:Return:OK"; - let msgSaveCalls = 0; - let msgSaveHandler = { - receiveMessage: function(message) { - if (message.name === msgSaveReply) { - msgSaveCalls++; - if (msgSaveCalls === 2) { - cpmm.removeMessageListener(msgSaveReply, msgSaveHandler); - // Trigger getall - cpmm.sendAsyncMessage("Notification:GetAll", { - origin: origin - }); - } - } - } - }; - cpmm.addMessageListener(msgSaveReply, msgSaveHandler); - - notifications.forEach(function(n) { - cpmm.sendAsyncMessage("Notification:Save", { - origin: origin, - notification: n - }); - }); -}); From e3186e29a75db1b4decfb0643298d9106442c95f Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 18 Oct 2016 00:23:44 -0400 Subject: [PATCH 74/80] Bug 1310910 - Remove ChromeNotifications; r=baku This partially backs out bug 967475. --- b2g/components/AlertsHelper.jsm | 61 --------- b2g/components/AlertsService.js | 63 +-------- b2g/installer/package-manifest.in | 2 - browser/installer/package-manifest.in | 2 - dom/interfaces/notification/moz.build | 1 - .../nsIDOMDesktopNotification.idl | 20 --- dom/notification/ChromeNotifications.js | 126 ------------------ dom/notification/ChromeNotifications.manifest | 3 - dom/notification/DesktopNotification.cpp | 1 - dom/notification/Notification.cpp | 4 - dom/notification/NotificationDB.jsm | 45 +------ dom/notification/moz.build | 2 - .../mochitest/notification/MockServices.js | 22 +-- dom/tests/mochitest/notification/chrome.ini | 8 -- .../notification_common.js | 9 +- .../test_notification_noresend.html | 88 ------------ dom/tests/moz.build | 1 - dom/webidl/ChromeNotifications.webidl | 14 -- dom/webidl/moz.build | 1 - mobile/android/installer/package-manifest.in | 2 - 20 files changed, 4 insertions(+), 471 deletions(-) delete mode 100644 dom/interfaces/notification/nsIDOMDesktopNotification.idl delete mode 100644 dom/notification/ChromeNotifications.js delete mode 100644 dom/notification/ChromeNotifications.manifest delete mode 100644 dom/tests/mochitest/notification/chrome.ini delete mode 100644 dom/tests/mochitest/notification/test_notification_noresend.html delete mode 100644 dom/webidl/ChromeNotifications.webidl diff --git a/b2g/components/AlertsHelper.jsm b/b2g/components/AlertsHelper.jsm index bfca82894b31..820f2406c50d 100644 --- a/b2g/components/AlertsHelper.jsm +++ b/b2g/components/AlertsHelper.jsm @@ -56,13 +56,10 @@ const kTopicAlertFinished = "alertfinished"; const kMozChromeNotificationEvent = "mozChromeNotificationEvent"; const kMozContentNotificationEvent = "mozContentNotificationEvent"; -const kMessageAppNotificationSend = "app-notification-send"; -const kMessageAppNotificationReturn = "app-notification-return"; const kMessageAlertNotificationSend = "alert-notification-send"; const kMessageAlertNotificationClose = "alert-notification-close"; const kMessages = [ - kMessageAppNotificationSend, kMessageAlertNotificationSend, kMessageAlertNotificationClose ]; @@ -132,35 +129,6 @@ var AlertsHelper = { listener.observer.observe(null, topic, listener.cookie); } catch (e) { } } else { - try { - listener.mm.sendAsyncMessage(kMessageAppNotificationReturn, { - uid: uid, - topic: topic, - target: listener.target - }); - } catch (e) { - // we get an exception if the app is not launched yet - if (detail.type !== kDesktopNotificationShow) { - // excluding the 'show' event: there is no reason a unlaunched app - // would want to be notified that a notification is shown. This - // happens when a notification is still displayed at reboot time. - gSystemMessenger.sendMessage(kNotificationSystemMessageName, { - clicked: (detail.type === kDesktopNotificationClick), - title: listener.title, - body: listener.text, - imageURL: listener.imageURL, - lang: listener.lang, - dir: listener.dir, - id: listener.id, - tag: listener.tag, - timestamp: listener.timestamp, - data: listener.dataObj - }, - Services.io.newURI(listener.target, null, null), - Services.io.newURI(listener.manifestURL, null, null) - ); - } - } if (detail.type === kDesktopNotificationClose && listener.dbId) { notificationStorage.delete(listener.manifestURL, listener.dbId); } @@ -280,31 +248,6 @@ var AlertsHelper = { data.lang, dataObj, null, data.inPrivateBrowsing); }, - showAppNotification: function(aMessage) { - let data = aMessage.data; - let details = data.details; - let dataObject = this.deserializeStructuredClone(details.data); - let listener = { - mm: aMessage.target, - title: data.title, - text: data.text, - manifestURL: details.manifestURL, - imageURL: data.imageURL, - lang: details.lang || undefined, - id: details.id || undefined, - dbId: details.dbId || undefined, - dir: details.dir || undefined, - tag: details.tag || undefined, - timestamp: details.timestamp || undefined, - dataObj: dataObject || undefined - }; - this.registerAppListener(data.uid, listener); - this.showNotification(data.imageURL, data.title, data.text, - details.textClickable, null, data.uid, details.dir, - details.lang, dataObject, details.manifestURL, - details.timestamp, details.mozbehavior); - }, - closeAlert: function(name) { SystemAppProxy._sendCustomEvent(kMozChromeNotificationEvent, { type: kDesktopNotificationClose, @@ -321,10 +264,6 @@ var AlertsHelper = { } switch(aMessage.name) { - case kMessageAppNotificationSend: - this.showAppNotification(aMessage); - break; - case kMessageAlertNotificationSend: this.showAlertNotification(aMessage); break; diff --git a/b2g/components/AlertsService.js b/b2g/components/AlertsService.js index fadfb4a8603d..19a164f0ed61 100644 --- a/b2g/components/AlertsService.js +++ b/b2g/components/AlertsService.js @@ -36,8 +36,6 @@ function debug(str) { const kNotificationSystemMessageName = "notification"; -const kMessageAppNotificationSend = "app-notification-send"; -const kMessageAppNotificationReturn = "app-notification-return"; const kMessageAlertNotificationSend = "alert-notification-send"; const kMessageAlertNotificationClose = "alert-notification-close"; @@ -47,20 +45,17 @@ const kTopicAlertClickCallback = "alertclickcallback"; function AlertsService() { Services.obs.addObserver(this, "xpcom-shutdown", false); - cpmm.addMessageListener(kMessageAppNotificationReturn, this); } AlertsService.prototype = { classID: Components.ID("{fe33c107-82a4-41d6-8c64-5353267e04c9}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService, - Ci.nsIAppNotificationService, Ci.nsIObserver]), observe: function(aSubject, aTopic, aData) { switch (aTopic) { case "xpcom-shutdown": Services.obs.removeObserver(this, "xpcom-shutdown"); - cpmm.removeMessageListener(kMessageAppNotificationReturn, this); break; } }, @@ -104,44 +99,13 @@ AlertsService.prototype = { }); }, - // nsIAppNotificationService - showAppNotification: function(aImageURL, aTitle, aText, aAlertListener, - aDetails) { - let uid = (aDetails.id == "") ? - "app-notif-" + uuidGenerator.generateUUID() : aDetails.id; - - let dataObj = this.deserializeStructuredClone(aDetails.data); - this._listeners[uid] = { - observer: aAlertListener, - title: aTitle, - text: aText, - manifestURL: aDetails.manifestURL, - imageURL: aImageURL, - lang: aDetails.lang || undefined, - id: aDetails.id || undefined, - dbId: aDetails.dbId || undefined, - dir: aDetails.dir || undefined, - tag: aDetails.tag || undefined, - timestamp: aDetails.timestamp || undefined, - dataObj: dataObj || undefined - }; - - cpmm.sendAsyncMessage(kMessageAppNotificationSend, { - imageURL: aImageURL, - title: aTitle, - text: aText, - uid: uid, - details: aDetails - }); - }, - // AlertsService.js custom implementation _listeners: [], receiveMessage: function(aMessage) { let data = aMessage.data; let listener = this._listeners[data.uid]; - if (aMessage.name !== kMessageAppNotificationReturn || !listener) { + if (!listener) { return; } @@ -150,31 +114,6 @@ AlertsService.prototype = { try { listener.observer.observe(null, topic, null); } catch (e) { - // It seems like there is no callbacks anymore, forward the click on - // notification via a system message containing the title/text/icon of - // the notification so the app get a change to react. - if (data.target) { - if (topic !== kTopicAlertShow) { - // excluding the 'show' event: there is no reason a unlaunched app - // would want to be notified that a notification is shown. This - // happens when a notification is still displayed at reboot time. - gSystemMessenger.sendMessage(kNotificationSystemMessageName, { - clicked: (topic === kTopicAlertClickCallback), - title: listener.title, - body: listener.text, - imageURL: listener.imageURL, - lang: listener.lang, - dir: listener.dir, - id: listener.id, - tag: listener.tag, - timestamp: listener.timestamp, - data: listener.dataObj || undefined, - }, - Services.io.newURI(data.target, null, null), - Services.io.newURI(listener.manifestURL, null, null) - ); - } - } if (topic === kTopicAlertFinished && listener.dbId) { notificationStorage.delete(listener.manifestURL, listener.dbId); } diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 8f1bab749a6b..65b2c3d41879 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -340,8 +340,6 @@ @RESPATH@/components/zipwriter.xpt ; JavaScript components -@RESPATH@/components/ChromeNotifications.js -@RESPATH@/components/ChromeNotifications.manifest @RESPATH@/components/ConsoleAPI.manifest @RESPATH@/components/ConsoleAPIStorage.js @RESPATH@/components/BrowserElementParent.manifest diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 1c9f1f4001e4..1acf4cf505b2 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -345,8 +345,6 @@ @RESPATH@/components/telemetry.xpt ; JavaScript components -@RESPATH@/components/ChromeNotifications.js -@RESPATH@/components/ChromeNotifications.manifest @RESPATH@/components/ConsoleAPI.manifest @RESPATH@/components/ConsoleAPIStorage.js @RESPATH@/components/BrowserElementParent.manifest diff --git a/dom/interfaces/notification/moz.build b/dom/interfaces/notification/moz.build index 2c1eda7de5e7..9e263b9f7afe 100644 --- a/dom/interfaces/notification/moz.build +++ b/dom/interfaces/notification/moz.build @@ -5,7 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. XPIDL_SOURCES += [ - 'nsIDOMDesktopNotification.idl', 'nsINotificationStorage.idl', ] diff --git a/dom/interfaces/notification/nsIDOMDesktopNotification.idl b/dom/interfaces/notification/nsIDOMDesktopNotification.idl deleted file mode 100644 index 308849684a01..000000000000 --- a/dom/interfaces/notification/nsIDOMDesktopNotification.idl +++ /dev/null @@ -1,20 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "domstubs.idl" - -interface nsIObserver; - -// Notification service that also provides the manifest URL -[scriptable, uuid(50cb17d2-dc8a-4aa6-bcd3-94d76af14e20)] -interface nsIAppNotificationService : nsISupports -{ - void showAppNotification(in AString imageUrl, - in AString title, - in AString text, - in nsIObserver alertListener, - // details should be a WebIDL - // AppNotificationServiceOptions Dictionary object - in jsval details); -}; diff --git a/dom/notification/ChromeNotifications.js b/dom/notification/ChromeNotifications.js deleted file mode 100644 index cf7ee9e61557..000000000000 --- a/dom/notification/ChromeNotifications.js +++ /dev/null @@ -1,126 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const DEBUG = false; - -function debug(s) { - dump("-*- ChromeNotifications.js: " + s + "\n"); -} - -const Ci = Components.interfaces; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "cpmm", - "@mozilla.org/childprocessmessagemanager;1", - "nsIMessageSender"); - -XPCOMUtils.defineLazyServiceGetter(this, "appNotifier", - "@mozilla.org/system-alerts-service;1", - "nsIAppNotificationService"); - -const CHROMENOTIFICATIONS_CID = "{74f94093-8b37-497e-824f-c3b250a911da}"; -const CHROMENOTIFICATIONS_CONTRACTID = "@mozilla.org/mozChromeNotifications;1"; - -function ChromeNotifications() { - this.innerWindowID = null; - this.resendCallback = null; -} - -ChromeNotifications.prototype = { - - init: function(aWindow) { - let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - this.innerWindowID = util.currentInnerWindowID; - Services.obs.addObserver(this, "inner-window-destroyed", false); - cpmm.addMessageListener("Notification:GetAllCrossOrigin:Return:OK", this); - }, - - performResend: function(notifications) { - let resentNotifications = 0; - - notifications.forEach(function(notification) { - let behavior; - try { - behavior = JSON.parse(notification.mozbehavior); - } catch(e) { - behavior = undefined; - } - - if (behavior && behavior.showOnlyOnce === true) { - return; - } - - appNotifier.showAppNotification( - notification.icon, - notification.title, - notification.body, - null, - { - manifestURL: notification.origin, - id: notification.alertName, - dir: notification.dir, - lang: notification.lang, - tag: notification.tag, - dbId: notification.id, - timestamp: notification.timestamp, - data: notification.data, - mozbehavior: behavior - } - ); - resentNotifications++; - }); - - try { - this.resendCallback && this.resendCallback(resentNotifications); - } catch (ex) { - if (DEBUG) debug("Content sent exception: " + ex); - } - }, - - mozResendAllNotifications: function(resendCallback) { - this.resendCallback = resendCallback; - cpmm.sendAsyncMessage("Notification:GetAllCrossOrigin", {}); - }, - - receiveMessage: function(message) { - switch (message.name) { - case "Notification:GetAllCrossOrigin:Return:OK": - this.performResend(message.data.notifications); - break; - - default: - if (DEBUG) { debug("Unrecognized message: " + message.name); } - break; - } - }, - - observe: function(aSubject, aTopic, aData) { - if (DEBUG) debug("Topic: " + aTopic); - if (aTopic == "inner-window-destroyed") { - let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; - if (wId != this.innerWindowID) { - return; - } - Services.obs.removeObserver(this, "inner-window-destroyed"); - cpmm.removeMessageListener("Notification:GetAllCrossOrigin:Return:OK", this); - } - }, - - classID : Components.ID(CHROMENOTIFICATIONS_CID), - contractID : CHROMENOTIFICATIONS_CONTRACTID, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIChromeNotifications, - Ci.nsIDOMGlobalPropertyInitializer, - Ci.nsIObserver, - Ci.nsIMessageListener]), -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeNotifications]); diff --git a/dom/notification/ChromeNotifications.manifest b/dom/notification/ChromeNotifications.manifest deleted file mode 100644 index 74e93107ffd7..000000000000 --- a/dom/notification/ChromeNotifications.manifest +++ /dev/null @@ -1,3 +0,0 @@ -# ChromeNotifications.js -component {74f94093-8b37-497e-824f-c3b250a911da} ChromeNotifications.js -contract @mozilla.org/mozChromeNotifications;1 {74f94093-8b37-497e-824f-c3b250a911da} diff --git a/dom/notification/DesktopNotification.cpp b/dom/notification/DesktopNotification.cpp index ed290074592c..76f1c5afb030 100644 --- a/dom/notification/DesktopNotification.cpp +++ b/dom/notification/DesktopNotification.cpp @@ -11,7 +11,6 @@ #include "nsContentPermissionHelper.h" #include "nsXULAppAPI.h" #include "mozilla/dom/PBrowserChild.h" -#include "nsIDOMDesktopNotification.h" #include "mozilla/Preferences.h" #include "nsGlobalWindow.h" #include "nsIScriptSecurityManager.h" diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 80e96e2f9343..06708b67833b 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -55,10 +55,6 @@ #include "WorkerRunnable.h" #include "WorkerScope.h" -#ifdef MOZ_B2G -#include "nsIDOMDesktopNotification.h" -#endif - namespace mozilla { namespace dom { diff --git a/dom/notification/NotificationDB.jsm b/dom/notification/NotificationDB.jsm index 10b3bad23407..b78b3f31df7b 100644 --- a/dom/notification/NotificationDB.jsm +++ b/dom/notification/NotificationDB.jsm @@ -35,8 +35,7 @@ const NOTIFICATION_STORE_PATH = const kMessages = [ "Notification:Save", "Notification:Delete", - "Notification:GetAll", - "Notification:GetAllCrossOrigin" + "Notification:GetAll" ]; var NotificationDB = { @@ -197,19 +196,6 @@ var NotificationDB = { }); break; - case "Notification:GetAllCrossOrigin": - this.queueTask("getallaccrossorigin", message.data).then( - function(notifications) { - returnMessage("Notification:GetAllCrossOrigin:Return:OK", { - notifications: notifications - }); - }).catch(function(error) { - returnMessage("Notification:GetAllCrossOrigin:Return:KO", { - errorMsg: error - }); - }); - break; - case "Notification:Save": this.queueTask("save", message.data).then(function() { returnMessage("Notification:Save:Return:OK", { @@ -286,10 +272,6 @@ var NotificationDB = { return this.taskGetAll(task.data); break; - case "getallaccrossorigin": - return this.taskGetAllCrossOrigin(); - break; - case "save": return this.taskSave(task.data); break; @@ -330,31 +312,6 @@ var NotificationDB = { return Promise.resolve(notifications); }, - taskGetAllCrossOrigin: function() { - if (DEBUG) { debug("Task, getting all whatever origin"); } - var notifications = []; - for (var origin in this.notifications) { - if (!this.notifications[origin]) { - continue; - } - - for (var i in this.notifications[origin]) { - var notification = this.notifications[origin][i]; - - // Notifications without the alertName field cannot be resent by - // mozResendAllNotifications, so we just skip them. They will - // still be available to applications via Notification.get() - if (!('alertName' in notification)) { - continue; - } - - notification.origin = origin; - notifications.push(notification); - } - } - return Promise.resolve(notifications); - }, - taskSave: function(data) { if (DEBUG) { debug("Task, saving"); } var origin = data.origin; diff --git a/dom/notification/moz.build b/dom/notification/moz.build index 6b53ce51d389..d966b160dfd6 100644 --- a/dom/notification/moz.build +++ b/dom/notification/moz.build @@ -5,8 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXTRA_COMPONENTS += [ - 'ChromeNotifications.js', - 'ChromeNotifications.manifest', 'NotificationStorage.js', 'NotificationStorage.manifest', ] diff --git a/dom/tests/mochitest/notification/MockServices.js b/dom/tests/mochitest/notification/MockServices.js index 2d43cac041b1..f809b24381d5 100644 --- a/dom/tests/mochitest/notification/MockServices.js +++ b/dom/tests/mochitest/notification/MockServices.js @@ -61,25 +61,6 @@ var MockServices = (function () { }, alertListener); }, - showAppNotification: function(aImageUrl, aTitle, aText, aAlertListener, aDetails) { - var listener = aAlertListener || (activeAlertNotifications[aDetails.id] ? activeAlertNotifications[aDetails.id].listener : undefined); - activeAppNotifications[aDetails.id] = { - observer: listener, - title: aTitle, - text: aText, - manifestURL: aDetails.manifestURL, - imageURL: aImageUrl, - lang: aDetails.lang || undefined, - id: aDetails.id || undefined, - dbId: aDetails.dbId || undefined, - dir: aDetails.dir || undefined, - tag: aDetails.tag || undefined, - timestamp: aDetails.timestamp || undefined, - data: aDetails.data || undefined - }; - this.showAlertNotification(aImageUrl, aTitle, aText, true, "", listener, aDetails.id); - }, - closeAlert: function(name) { var alertNotification = activeAlertNotifications[name]; if (alertNotification) { @@ -97,8 +78,7 @@ var MockServices = (function () { QueryInterface: function(aIID) { if (SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) || - SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService) || - SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAppNotificationService)) { + SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService)) { return this; } throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE; diff --git a/dom/tests/mochitest/notification/chrome.ini b/dom/tests/mochitest/notification/chrome.ini deleted file mode 100644 index 171745999a7a..000000000000 --- a/dom/tests/mochitest/notification/chrome.ini +++ /dev/null @@ -1,8 +0,0 @@ -[DEFAULT] -skip-if = toolkit == 'android' # Bug 1287455: takes too long to complete on Android -support-files = - MockServices.js - NotificationTest.js - -[test_notification_noresend.html] -skip-if = (toolkit == 'gonk') # Mochitest on Gonk registers an app manifest that messes with the logic diff --git a/dom/tests/mochitest/notification/desktop-notification/notification_common.js b/dom/tests/mochitest/notification/desktop-notification/notification_common.js index 3aae3cef4b91..921b1e753cd8 100644 --- a/dom/tests/mochitest/notification/desktop-notification/notification_common.js +++ b/dom/tests/mochitest/notification/desktop-notification/notification_common.js @@ -27,16 +27,9 @@ var mockAlertsService = { }, alertListener); }, - showAppNotification: function(imageUrl, title, text, alertListener, details) { - this.showAlertNotification(imageUrl, title, text, details.textClickable, "", - alertListener, details.name, details.dir, - details.lang, details.data); - }, - QueryInterface: function(aIID) { if (SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) || - SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService) || - SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAppNotificationService)) { + SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService)) { return this; } throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE; diff --git a/dom/tests/mochitest/notification/test_notification_noresend.html b/dom/tests/mochitest/notification/test_notification_noresend.html deleted file mode 100644 index ce78d8a921ab..000000000000 --- a/dom/tests/mochitest/notification/test_notification_noresend.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - Testing mozResendAllNotifications() resend behavior for Pages - - - - - - -Bug 1159128 -

- -

-
-
-
diff --git a/dom/tests/moz.build b/dom/tests/moz.build
index 572da0e3ec3c..f7c3e2437c16 100644
--- a/dom/tests/moz.build
+++ b/dom/tests/moz.build
@@ -36,7 +36,6 @@ MOCHITEST_CHROME_MANIFESTS += [
     'mochitest/general/chrome.ini',
     'mochitest/geolocation/chrome.ini',
     'mochitest/localstorage/chrome.ini',
-    'mochitest/notification/chrome.ini',
     'mochitest/sessionstorage/chrome.ini',
     'mochitest/whatwg/chrome.ini',
 ]
diff --git a/dom/webidl/ChromeNotifications.webidl b/dom/webidl/ChromeNotifications.webidl
deleted file mode 100644
index 5283e23027e5..000000000000
--- a/dom/webidl/ChromeNotifications.webidl
+++ /dev/null
@@ -1,14 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-[JSImplementation="@mozilla.org/mozChromeNotifications;1",
- NavigatorProperty="mozChromeNotifications",
- ChromeOnly]
-interface ChromeNotifications {
-  void mozResendAllNotifications(ResendCallback resendCallback);
-};
-
-callback ResendCallback = void (long count);
diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build
index 8019c4339b2d..f8d7641b3111 100644
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -74,7 +74,6 @@ WEBIDL_FILES = [
     'CheckerboardReportService.webidl',
     'ChildNode.webidl',
     'ChromeNodeList.webidl',
-    'ChromeNotifications.webidl',
     'ChromeUtils.webidl',
     'Client.webidl',
     'Clients.webidl',
diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in
index c93f43c1ba10..bdad13175b94 100644
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -262,8 +262,6 @@
 @BINPATH@/components/zipwriter.xpt
 
 ; JavaScript components
-@BINPATH@/components/ChromeNotifications.js
-@BINPATH@/components/ChromeNotifications.manifest
 @BINPATH@/components/ConsoleAPI.manifest
 @BINPATH@/components/ConsoleAPIStorage.js
 @BINPATH@/components/ContactManager.js

From 3c4317ab39e5505b4de70dab7c15e2f7f5936567 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Tue, 18 Oct 2016 09:56:01 -0400
Subject: [PATCH 75/80] Bug 1307491 follow-up: Remove two remaining
 declarations of NS_IsAppOffline

---
 netwerk/base/nsNetUtil.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h
index 0425732b52e5..bd89ca8aeb37 100644
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -771,10 +771,6 @@ NS_NewNotificationCallbacksAggregation(nsIInterfaceRequestor  *callbacks,
  */
 bool NS_IsOffline();
 
-bool NS_IsAppOffline(uint32_t appId);
-
-bool NS_IsAppOffline(nsIPrincipal *principal);
-
 /**
  * Helper functions for implementing nsINestedURI::innermostURI.
  *

From b01eb310835ef8a7eb7dd478169a201514bb08db Mon Sep 17 00:00:00 2001
From: Luke Wagner 
Date: Tue, 18 Oct 2016 09:46:27 -0500
Subject: [PATCH 76/80] Bug 1311019 - Baldr: use currentOffset() instead of
 size() for current offset (r=bbouvier)

MozReview-Commit-ID: 9SSCWfua4zj

--HG--
extra : rebase_source : 03f822b7e4d1f3d2bf16666ea06e30dac3308772
---
 js/src/jit-test/tests/wasm/regress/bug1311019.js | 11 +++++++++++
 js/src/jit/MacroAssembler.cpp                    | 11 +++++------
 2 files changed, 16 insertions(+), 6 deletions(-)
 create mode 100644 js/src/jit-test/tests/wasm/regress/bug1311019.js

diff --git a/js/src/jit-test/tests/wasm/regress/bug1311019.js b/js/src/jit-test/tests/wasm/regress/bug1311019.js
new file mode 100644
index 000000000000..db977d108493
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/bug1311019.js
@@ -0,0 +1,11 @@
+load(libdir + "wasm.js");
+
+new WebAssembly.Module(wasmTextToBinary(`(module
+    (memory 1)
+    (func
+        (i64.trunc_s/f32 (f32.const 6.96875))
+        (i32.load8_s (i32.const 0))
+        (f32.const -7.66028056e-31)
+        (unreachable)
+    )
+)`));
diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp
index 454b7757da8f..afb1945ffc76 100644
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -2851,7 +2851,7 @@ MacroAssembler::wasmEmitTrapOutOfLineCode()
             break;
           }
           case wasm::TrapSite::MemoryAccess: {
-            append(wasm::MemoryAccess(site.codeOffset, size()));
+            append(wasm::MemoryAccess(site.codeOffset, currentOffset()));
             break;
           }
         }
@@ -2874,8 +2874,7 @@ MacroAssembler::wasmEmitTrapOutOfLineCode()
             // by the wasm::CallSite to allow unwinding this frame.
             setFramePushed(site.framePushed);
 
-            // Align the stack for a nullary call. The call does not return so
-            // there's no need to emit a corresponding increment.
+            // Align the stack for a nullary call.
             size_t alreadyPushed = sizeof(AsmJSFrame) + framePushed();
             size_t toPush = ABIArgGenerator().stackBytesConsumedSoFar();
             if (size_t dec = StackDecrementForCall(ABIStackAlignment, alreadyPushed, toPush))
@@ -2889,12 +2888,12 @@ MacroAssembler::wasmEmitTrapOutOfLineCode()
             // the trapping instruction.
             wasm::CallSiteDesc desc(site.bytecodeOffset, wasm::CallSiteDesc::TrapExit);
             call(desc, site.trap);
+        }
 
 #ifdef DEBUG
-            // Traps do not return.
-            breakpoint();
+        // Traps do not return, so no need to freeStack().
+        breakpoint();
 #endif
-        }
     }
 
     // Ensure that the return address of the last emitted call above is always

From 6b23604d39ea45f9873d21de3eadad64d1c6e768 Mon Sep 17 00:00:00 2001
From: Jan de Mooij 
Date: Tue, 18 Oct 2016 17:08:47 +0200
Subject: [PATCH 77/80] Bug 1309174 - Handle lazy self-hosted functions in
 optimization tracking debug spew. r=shu

---
 js/src/jit/OptimizationTracking.cpp | 44 +++++++++++++----------------
 1 file changed, 19 insertions(+), 25 deletions(-)

diff --git a/js/src/jit/OptimizationTracking.cpp b/js/src/jit/OptimizationTracking.cpp
index b611a813a2df..029b3c082be1 100644
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -849,6 +849,22 @@ MaybeConstructorFromType(TypeSet::Type ty)
     return newScript ? newScript->function() : nullptr;
 }
 
+static void
+InterpretedFunctionFilenameAndLineNumber(JSFunction* fun, const char** filename,
+                                         Maybe* lineno)
+{
+    if (fun->hasScript()) {
+        *filename = fun->nonLazyScript()->maybeForwardedScriptSource()->filename();
+        *lineno = Some((unsigned) fun->nonLazyScript()->lineno());
+    } else if (fun->lazyScriptOrNull()) {
+        *filename = fun->lazyScript()->maybeForwardedScriptSource()->filename();
+        *lineno = Some((unsigned) fun->lazyScript()->lineno());
+    } else {
+        *filename = "(self-hosted builtin)";
+        *lineno = Nothing();
+    }
+}
+
 static void
 SpewConstructor(TypeSet::Type ty, JSFunction* constructor)
 {
@@ -866,17 +882,11 @@ SpewConstructor(TypeSet::Type ty, JSFunction* constructor)
         snprintf(buf, mozilla::ArrayLength(buf), "??");
 
     const char* filename;
-    size_t lineno;
-    if (constructor->hasScript()) {
-        filename = constructor->nonLazyScript()->filename();
-        lineno = constructor->nonLazyScript()->lineno();
-    } else {
-        filename = constructor->lazyScript()->filename();
-        lineno = constructor->lazyScript()->lineno();
-    }
+    Maybe lineno;
+    InterpretedFunctionFilenameAndLineNumber(constructor, &filename, &lineno);
 
     JitSpew(JitSpew_OptimizationTracking, "   Unique type %s has constructor %s (%s:%" PRIuSIZE ")",
-            TypeSet::TypeString(ty), buf, filename, lineno);
+            TypeSet::TypeString(ty), buf, filename, lineno.isSome() ? *lineno : 0);
 #endif
 }
 
@@ -1154,22 +1164,6 @@ IonBuilder::trackInlineSuccessUnchecked(InliningStatus status)
         trackOptimizationOutcome(TrackedOutcome::Inlined);
 }
 
-static void
-InterpretedFunctionFilenameAndLineNumber(JSFunction* fun, const char** filename,
-                                         Maybe* lineno)
-{
-    if (fun->hasScript()) {
-        *filename = fun->nonLazyScript()->maybeForwardedScriptSource()->filename();
-        *lineno = Some((unsigned) fun->nonLazyScript()->lineno());
-    } else if (fun->lazyScriptOrNull()) {
-        *filename = fun->lazyScript()->maybeForwardedScriptSource()->filename();
-        *lineno = Some((unsigned) fun->lazyScript()->lineno());
-    } else {
-        *filename = "(self-hosted builtin)";
-        *lineno = Nothing();
-    }
-}
-
 static JSFunction*
 FunctionFromTrackedType(const IonTrackedTypeWithAddendum& tracked)
 {

From c31db088643d61f622ede6b473eae0e73a4656d7 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Tue, 18 Oct 2016 11:33:14 -0400
Subject: [PATCH 78/80] Backout bug 1309867 for test failures

---
 layout/style/FontFace.cpp    | 73 +++++++++++++-----------------------
 layout/style/FontFace.h      | 16 +-------
 layout/style/FontFaceSet.cpp | 36 ++++++++----------
 layout/style/FontFaceSet.h   |  3 --
 4 files changed, 44 insertions(+), 84 deletions(-)

diff --git a/layout/style/FontFace.cpp b/layout/style/FontFace.cpp
index 761e57285862..968b0eea0643 100644
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -101,7 +101,6 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(FontFace)
 
 FontFace::FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet)
   : mParent(aParent)
-  , mLoadedRejection(NS_OK)
   , mStatus(FontFaceLoadStatus::Unloaded)
   , mSourceType(SourceType(0))
   , mSourceBuffer(nullptr)
@@ -110,6 +109,16 @@ FontFace::FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet)
   , mInFontFaceSet(false)
 {
   MOZ_COUNT_CTOR(FontFace);
+
+  nsCOMPtr global = do_QueryInterface(aParent);
+
+  // If the pref is not set, don't create the Promise (which the page wouldn't
+  // be able to get to anyway) as it causes the window.FontFace constructor
+  // to be created.
+  if (global && FontFaceSet::PrefEnabled()) {
+    ErrorResult rv;
+    mLoaded = Promise::Create(global, rv);
+  }
 }
 
 FontFace::~FontFace()
@@ -189,11 +198,13 @@ FontFace::InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource)
     if (!ParseDescriptor(eCSSFontDesc_Src,
                          aSource.GetAsString(),
                          mDescriptors->mSrc)) {
-      // The SetStatus call we are about to do assumes that for
-      // FontFace objects with sources other than ArrayBuffer(View)s, that the
-      // mLoaded Promise is rejected with a network error.  We get
-      // in here beforehand to set it to the required syntax error.
-      Reject(NS_ERROR_DOM_SYNTAX_ERR);
+      if (mLoaded) {
+        // The SetStatus call we are about to do assumes that for
+        // FontFace objects with sources other than ArrayBuffer(View)s, that the
+        // mLoaded Promise is rejected with a network error.  We get
+        // in here beforehand to set it to the required syntax error.
+        mLoaded->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR);
+      }
 
       SetStatus(FontFaceLoadStatus::Error);
       return;
@@ -367,8 +378,6 @@ FontFace::Load(ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
 
-  EnsurePromise();
-
   if (!mLoaded) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -424,8 +433,6 @@ FontFace::GetLoaded(ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
 
-  EnsurePromise();
-
   if (!mLoaded) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -460,15 +467,17 @@ FontFace::SetStatus(FontFaceLoadStatus aStatus)
     otherSet->OnFontFaceStatusChanged(this);
   }
 
+  if (!mLoaded) {
+    return;
+  }
+
   if (mStatus == FontFaceLoadStatus::Loaded) {
-    if (mLoaded) {
-      mLoaded->MaybeResolve(this);
-    }
+    mLoaded->MaybeResolve(this);
   } else if (mStatus == FontFaceLoadStatus::Error) {
     if (mSourceType == eSourceType_Buffer) {
-      Reject(NS_ERROR_DOM_SYNTAX_ERR);
+      mLoaded->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR);
     } else {
-      Reject(NS_ERROR_DOM_NETWORK_ERR);
+      mLoaded->MaybeReject(NS_ERROR_DOM_NETWORK_ERR);
     }
   }
 }
@@ -561,7 +570,9 @@ FontFace::SetDescriptors(const nsAString& aFamily,
     // on the FontFace.
     mDescriptors = new CSSFontFaceDescriptors;
 
-    Reject(NS_ERROR_DOM_SYNTAX_ERR);
+    if (mLoaded) {
+      mLoaded->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR);
+    }
 
     SetStatus(FontFaceLoadStatus::Error);
     return false;
@@ -737,36 +748,6 @@ FontFace::RemoveFontFaceSet(FontFaceSet* aFontFaceSet)
   }
 }
 
-void
-FontFace::Reject(nsresult aResult)
-{
-  if (mLoaded) {
-    mLoaded->MaybeReject(aResult);
-  } else {
-    mLoadedRejection = aResult;
-  }
-}
-
-void
-FontFace::EnsurePromise()
-{
-  nsCOMPtr global = do_QueryInterface(mParent);
-
-  // If the pref is not set, don't create the Promise (which the page wouldn't
-  // be able to get to anyway) as it causes the window.FontFace constructor
-  // to be created.
-  if (global && FontFaceSet::PrefEnabled()) {
-    ErrorResult rv;
-    mLoaded = Promise::Create(global, rv);
-
-    if (mStatus == FontFaceLoadStatus::Loaded) {
-      mLoaded->MaybeResolve(this);
-    } else if (mLoadedRejection != NS_OK) {
-      mLoaded->MaybeReject(mLoadedRejection);
-    }
-  }
-}
-
 // -- FontFace::Entry --------------------------------------------------------
 
 /* virtual */ void
diff --git a/layout/style/FontFace.h b/layout/style/FontFace.h
index 29171a6da672..8da40f53becf 100644
--- a/layout/style/FontFace.h
+++ b/layout/style/FontFace.h
@@ -199,24 +199,12 @@ private:
    */
   void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength);
 
-  // Acts like mLoaded->MaybeReject(aResult), except it doesn't create mLoaded
-  // if it doesn't already exist.
-  void Reject(nsresult aResult);
-
-  // Creates mLoaded if it doesn't already exist. It may immediately resolve or
-  // reject mLoaded based on mStatus and mLoadedRejection.
-  void EnsurePromise();
-
   nsCOMPtr mParent;
 
-  // A Promise that is fulfilled once the font represented by this FontFace is
-  // loaded, and is rejected if the load fails. This promise is created lazily
-  // when JS asks for it.
+  // A Promise that is fulfilled once the font represented by this FontFace
+  // is loaded, and is rejected if the load fails.
   RefPtr mLoaded;
 
-  // Saves the rejection code for mLoaded if mLoaded hasn't been created yet.
-  nsresult mLoadedRejection;
-
   // The @font-face rule this FontFace object is reflecting, if it is a
   // rule backed FontFace.
   RefPtr mRule;
diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp
index 01959d7bcdd0..5eeebe72dbc4 100644
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -97,7 +97,6 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument)
   : DOMEventTargetHelper(aWindow)
   , mDocument(aDocument)
-  , mResolveLazilyCreatedReadyPromise(false)
   , mStatus(FontFaceSetLoadStatus::Loaded)
   , mNonRuleFacesDirty(false)
   , mHasLoadingFontFaces(false)
@@ -112,7 +111,12 @@ FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument)
   // be able to get to anyway) as it causes the window.FontFaceSet constructor
   // to be created.
   if (global && PrefEnabled()) {
-    mResolveLazilyCreatedReadyPromise = true;
+    ErrorResult rv;
+    mReady = Promise::Create(global, rv);
+  }
+
+  if (mReady) {
+    mReady->MaybeResolve(this);
   }
 
   if (!mDocument->DidFireDOMContentLoaded()) {
@@ -382,16 +386,8 @@ Promise*
 FontFaceSet::GetReady(ErrorResult& aRv)
 {
   if (!mReady) {
-    nsCOMPtr global = GetParentObject();
-    mReady = Promise::Create(global, aRv);
-    if (!mReady) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
-    }
-    if (mResolveLazilyCreatedReadyPromise) {
-      mReady->MaybeResolve(this);
-      mResolveLazilyCreatedReadyPromise = false;
-    }
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
   }
 
   FlushUserFontSet();
@@ -1509,14 +1505,14 @@ FontFaceSet::CheckLoadingStarted()
                             false))->RunDOMEventWhenSafe();
 
   if (PrefEnabled()) {
-    if (mReady) {
-      if (GetParentObject()) {
-        ErrorResult rv;
-        mReady = Promise::Create(GetParentObject(), rv);
-      }
+    RefPtr ready;
+    if (GetParentObject()) {
+      ErrorResult rv;
+      ready = Promise::Create(GetParentObject(), rv);
     }
-    if (!mReady) {
-      mResolveLazilyCreatedReadyPromise = false;
+
+    if (ready) {
+      mReady.swap(ready);
     }
   }
 }
@@ -1603,8 +1599,6 @@ FontFaceSet::CheckLoadingFinished()
   mStatus = FontFaceSetLoadStatus::Loaded;
   if (mReady) {
     mReady->MaybeResolve(this);
-  } else {
-    mResolveLazilyCreatedReadyPromise = true;
   }
 
   // Now dispatch the loadingdone/loadingerror events.
diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h
index 78fe512d5590..58e4982fe513 100644
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -314,10 +314,7 @@ private:
   // any of those fonts failed to load.  mReady is replaced with
   // a new Promise object whenever mReady is settled and another
   // FontFace in mRuleFaces or mNonRuleFaces starts to load.
-  // Note that mReady is created lazily when GetReady() is called.
   RefPtr mReady;
-  // Whether the ready promise must be resolved when it's created.
-  bool mResolveLazilyCreatedReadyPromise;
 
   // Set of all loaders pointing to us. These are not strong pointers,
   // but that's OK because nsFontFaceLoader always calls RemoveLoader on

From 0427462aa8e09a9299609ebef4e126eba685a354 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Wed, 5 Oct 2016 23:49:34 -0400
Subject: [PATCH 79/80] Bug 1308075 - Remove support for accessing the Cache
 API from app:// URLs and also for storing requests/responses with app:// URLs
 within it; r=bkelly

This is backing out bug 1243849.
---
 dom/cache/CacheStorage.cpp | 1 -
 dom/cache/TypeUtils.cpp    | 3 +--
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/dom/cache/CacheStorage.cpp b/dom/cache/CacheStorage.cpp
index 0104240c1fc1..8c08b4884300 100644
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -119,7 +119,6 @@ IsTrusted(const PrincipalInfo& aPrincipalInfo, bool aTestingPrefEnabled)
 
   nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen));
   if (scheme.LowerCaseEqualsLiteral("https") ||
-      scheme.LowerCaseEqualsLiteral("app") ||
       scheme.LowerCaseEqualsLiteral("file")) {
     return true;
   }
diff --git a/dom/cache/TypeUtils.cpp b/dom/cache/TypeUtils.cpp
index be5fbca874be..77d84010c61d 100644
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -400,8 +400,7 @@ TypeUtils::ProcessURL(nsACString& aUrl, bool* aSchemeValidOut,
   if (aSchemeValidOut) {
     nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen));
     *aSchemeValidOut = scheme.LowerCaseEqualsLiteral("http") ||
-                       scheme.LowerCaseEqualsLiteral("https") ||
-                       scheme.LowerCaseEqualsLiteral("app");
+                       scheme.LowerCaseEqualsLiteral("https");
   }
 
   uint32_t queryPos;

From a00dbb24ab849932c928a245269aeba9d9d29c9b Mon Sep 17 00:00:00 2001
From: Jan de Mooij 
Date: Tue, 18 Oct 2016 17:52:56 +0200
Subject: [PATCH 80/80] Bug 1309903 - Fix Ion regalloc to require call
 instruction uses to be atStart. r=bhackett

---
 js/src/jit/BacktrackingAllocator.cpp    | 22 +++++++-------
 js/src/jit/Lowering.cpp                 | 38 ++++++++++++-------------
 js/src/jit/Lowering.h                   |  4 +++
 js/src/jit/shared/Lowering-shared-inl.h |  6 ++++
 js/src/jit/shared/Lowering-shared.h     |  1 +
 5 files changed, 40 insertions(+), 31 deletions(-)

diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp
index 9ffbe1a6dd26..8d2bca22bf67 100644
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -661,17 +661,20 @@ BacktrackingAllocator::buildLivenessInfo()
                 if (inputAlloc->isUse()) {
                     LUse* use = inputAlloc->toUse();
 
-                    // Call uses should always be at-start or fixed, since
-                    // calls use all registers.
+                    // Call uses should always be at-start, since calls use all
+                    // registers.
                     MOZ_ASSERT_IF(ins->isCall() && !inputAlloc.isSnapshotInput(),
-                                  use->isFixedRegister() || use->usedAtStart());
+                                  use->usedAtStart());
 
 #ifdef DEBUG
                     // Don't allow at-start call uses if there are temps of the same kind,
-                    // so that we don't assign the same register.
+                    // so that we don't assign the same register. Only allow this when the
+                    // use and temp are fixed registers, as they can't alias.
                     if (ins->isCall() && use->usedAtStart()) {
-                        for (size_t i = 0; i < ins->numTemps(); i++)
-                            MOZ_ASSERT(vreg(ins->getTemp(i)).type() != vreg(use).type());
+                        for (size_t i = 0; i < ins->numTemps(); i++) {
+                            MOZ_ASSERT(vreg(ins->getTemp(i)).type() != vreg(use).type() ||
+                                       (use->isFixedRegister() && ins->getTemp(i)->isFixed()));
+                        }
                     }
 
                     // If there are both useRegisterAtStart(x) and useRegister(y)
@@ -692,12 +695,7 @@ BacktrackingAllocator::buildLivenessInfo()
                     if (use->policy() == LUse::RECOVERED_INPUT)
                         continue;
 
-                    // Fixed uses on calls are specially overridden to happen
-                    // at the input position.
-                    CodePosition to =
-                        (use->usedAtStart() || (ins->isCall() && use->isFixedRegister()))
-                        ? inputOf(*ins)
-                        : outputOf(*ins);
+                    CodePosition to = use->usedAtStart() ? inputOf(*ins) : outputOf(*ins);
                     if (use->isFixedRegister()) {
                         LAllocation reg(AnyRegister::FromCode(use->registerCode()));
                         for (size_t i = 0; i < ins->numDefs(); i++) {
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index 92fa8af57c85..f1ab5fe2472a 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -399,7 +399,7 @@ LIRGenerator::visitCreateThis(MCreateThis* ins)
 void
 LIRGenerator::visitCreateArgumentsObject(MCreateArgumentsObject* ins)
 {
-    LAllocation callObj = useFixed(ins->getCallObject(), CallTempReg0);
+    LAllocation callObj = useFixedAtStart(ins->getCallObject(), CallTempReg0);
     LCreateArgumentsObject* lir = new(alloc()) LCreateArgumentsObject(callObj, tempFixed(CallTempReg1),
                                                                       tempFixed(CallTempReg2),
                                                                       tempFixed(CallTempReg3));
@@ -539,12 +539,12 @@ LIRGenerator::visitCall(MCall* call)
             lir = new(alloc()) LCallNative(tempFixed(cxReg), tempFixed(numReg),
                                            tempFixed(vpReg), tempFixed(tmpReg));
         } else {
-            lir = new(alloc()) LCallKnown(useFixed(call->getFunction(), CallTempReg0),
+            lir = new(alloc()) LCallKnown(useFixedAtStart(call->getFunction(), CallTempReg0),
                                           tempFixed(CallTempReg2));
         }
     } else {
         // Call anything, using the most generic code.
-        lir = new(alloc()) LCallGeneric(useFixed(call->getFunction(), CallTempReg0),
+        lir = new(alloc()) LCallGeneric(useFixedAtStart(call->getFunction(), CallTempReg0),
                                         tempFixed(ArgumentsRectifierReg),
                                         tempFixed(CallTempReg2));
     }
@@ -566,9 +566,9 @@ LIRGenerator::visitApplyArgs(MApplyArgs* apply)
     MOZ_ASSERT(CallTempReg2 != JSReturnReg_Data);
 
     LApplyArgsGeneric* lir = new(alloc()) LApplyArgsGeneric(
-        useFixed(apply->getFunction(), CallTempReg3),
-        useFixed(apply->getArgc(), CallTempReg0),
-        useBoxFixed(apply->getThis(), CallTempReg4, CallTempReg5),
+        useFixedAtStart(apply->getFunction(), CallTempReg3),
+        useFixedAtStart(apply->getArgc(), CallTempReg0),
+        useBoxFixedAtStart(apply->getThis(), CallTempReg4, CallTempReg5),
         tempFixed(CallTempReg1),  // object register
         tempFixed(CallTempReg2)); // stack counter register
 
@@ -596,7 +596,7 @@ LIRGenerator::visitApplyArray(MApplyArray* apply)
     LApplyArrayGeneric* lir = new(alloc()) LApplyArrayGeneric(
         useFixedAtStart(apply->getFunction(), CallTempReg3),
         useFixedAtStart(apply->getElements(), CallTempReg0),
-        useBoxFixed(apply->getThis(), CallTempReg4, CallTempReg5),
+        useBoxFixedAtStart(apply->getThis(), CallTempReg4, CallTempReg5),
         tempFixed(CallTempReg1),  // object register
         tempFixed(CallTempReg2)); // stack counter register
 
@@ -670,8 +670,8 @@ LIRGenerator::visitGetDynamicName(MGetDynamicName* ins)
     MDefinition* name = ins->getName();
     MOZ_ASSERT(name->type() == MIRType::String);
 
-    LGetDynamicName* lir = new(alloc()) LGetDynamicName(useFixed(envChain, CallTempReg0),
-                                                        useFixed(name, CallTempReg1),
+    LGetDynamicName* lir = new(alloc()) LGetDynamicName(useFixedAtStart(envChain, CallTempReg0),
+                                                        useFixedAtStart(name, CallTempReg1),
                                                         tempFixed(CallTempReg2),
                                                         tempFixed(CallTempReg3),
                                                         tempFixed(CallTempReg4));
@@ -1562,7 +1562,7 @@ LIRGenerator::visitPow(MPow* ins)
     if (power->type() == MIRType::Int32) {
         // Note: useRegisterAtStart here is safe, the temp is a GP register so
         // it will never get the same register.
-        lir = new(alloc()) LPowI(useRegisterAtStart(input), useFixed(power, CallTempReg1),
+        lir = new(alloc()) LPowI(useRegisterAtStart(input), useFixedAtStart(power, CallTempReg1),
                                  tempFixed(CallTempReg0));
     } else {
         lir = new(alloc()) LPowD(useRegisterAtStart(input), useRegisterAtStart(power),
@@ -3302,9 +3302,9 @@ LIRGenerator::visitArraySlice(MArraySlice* ins)
     MOZ_ASSERT(ins->begin()->type() == MIRType::Int32);
     MOZ_ASSERT(ins->end()->type() == MIRType::Int32);
 
-    LArraySlice* lir = new(alloc()) LArraySlice(useFixed(ins->object(), CallTempReg0),
-                                                useFixed(ins->begin(), CallTempReg1),
-                                                useFixed(ins->end(), CallTempReg2),
+    LArraySlice* lir = new(alloc()) LArraySlice(useFixedAtStart(ins->object(), CallTempReg0),
+                                                useFixedAtStart(ins->begin(), CallTempReg1),
+                                                useFixedAtStart(ins->end(), CallTempReg2),
                                                 tempFixed(CallTempReg3),
                                                 tempFixed(CallTempReg4));
     defineReturn(lir, ins);
@@ -4020,7 +4020,7 @@ LIRGenerator::visitRest(MRest* ins)
 {
     MOZ_ASSERT(ins->numActuals()->type() == MIRType::Int32);
 
-    LRest* lir = new(alloc()) LRest(useFixed(ins->numActuals(), CallTempReg0),
+    LRest* lir = new(alloc()) LRest(useFixedAtStart(ins->numActuals(), CallTempReg0),
                                     tempFixed(CallTempReg1),
                                     tempFixed(CallTempReg2),
                                     tempFixed(CallTempReg3));
@@ -4291,10 +4291,10 @@ LIRGenerator::visitWasmCall(MWasmCall* ins)
     }
 
     for (unsigned i = 0; i < ins->numArgs(); i++)
-        args[i] = useFixed(ins->getOperand(i), ins->registerForArg(i));
+        args[i] = useFixedAtStart(ins->getOperand(i), ins->registerForArg(i));
 
     if (ins->callee().isTable())
-        args[ins->numArgs()] = useFixed(ins->getOperand(ins->numArgs()), WasmTableCallIndexReg);
+        args[ins->numArgs()] = useFixedAtStart(ins->getOperand(ins->numArgs()), WasmTableCallIndexReg);
 
     LInstruction* lir;
     if (ins->type() == MIRType::Int64)
@@ -4327,8 +4327,8 @@ LIRGenerator::visitSetDOMProperty(MSetDOMProperty* ins)
     MOZ_ASSERT(ok, "How can we not have six temp registers?");
 
     LSetDOMProperty* lir = new(alloc()) LSetDOMProperty(tempFixed(cxReg),
-                                                        useFixed(ins->object(), objReg),
-                                                        useBoxFixed(val, tempReg1, tempReg2),
+                                                        useFixedAtStart(ins->object(), objReg),
+                                                        useBoxFixedAtStart(val, tempReg1, tempReg2),
                                                         tempFixed(privReg),
                                                         tempFixed(valueReg));
     add(lir, ins);
@@ -4345,7 +4345,7 @@ LIRGenerator::visitGetDOMProperty(MGetDOMProperty* ins)
     mozilla::DebugOnly ok = GetTempRegForIntArg(3, 0, &valueReg);
     MOZ_ASSERT(ok, "How can we not have four temp registers?");
     LGetDOMProperty* lir = new(alloc()) LGetDOMProperty(tempFixed(cxReg),
-                                                        useFixed(ins->object(), objReg),
+                                                        useFixedAtStart(ins->object(), objReg),
                                                         tempFixed(privReg),
                                                         tempFixed(valueReg));
 
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index 1da16b6da2e6..583bd9f83dfa 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -49,6 +49,10 @@ class LIRGenerator : public LIRGeneratorSpecific
     MOZ_MUST_USE bool generate();
 
   private:
+    LBoxAllocation useBoxFixedAtStart(MDefinition* mir, Register reg1, Register reg2) {
+        return useBoxFixed(mir, reg1, reg2, /* useAtStart = */ true);
+    }
+
     LBoxAllocation useBoxFixedAtStart(MDefinition* mir, ValueOperand op);
     LBoxAllocation useBoxAtStart(MDefinition* mir, LUse::Policy policy = LUse::REGISTER);
 
diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h
index c7a10dcf4439..abdd0910e975 100644
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -587,6 +587,12 @@ LIRGeneratorShared::useFixed(MDefinition* mir, AnyRegister reg)
     return reg.isFloat() ? use(mir, LUse(reg.fpu())) : use(mir, LUse(reg.gpr()));
 }
 
+LUse
+LIRGeneratorShared::useFixedAtStart(MDefinition* mir, AnyRegister reg)
+{
+    return reg.isFloat() ? use(mir, LUse(reg.fpu(), true)) : use(mir, LUse(reg.gpr(), true));
+}
+
 LDefinition
 LIRGeneratorShared::temp(LDefinition::Type type, LDefinition::Policy policy)
 {
diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h
index 4cd11a92c4bb..687cc293e5ba 100644
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -92,6 +92,7 @@ class LIRGeneratorShared : public MDefinitionVisitor
     inline LUse useFixed(MDefinition* mir, FloatRegister reg);
     inline LUse useFixed(MDefinition* mir, AnyRegister reg);
     inline LUse useFixedAtStart(MDefinition* mir, Register reg);
+    inline LUse useFixedAtStart(MDefinition* mir, AnyRegister reg);
     inline LAllocation useOrConstant(MDefinition* mir);
     inline LAllocation useOrConstantAtStart(MDefinition* mir);
     // "Any" is architecture dependent, and will include registers and stack