diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp index 84e9d0c0802e..e52462ab7c37 100644 --- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -800,7 +800,11 @@ getParentCB(AtkObject *aAtkObj) atkParent = parent ? AccessibleWrap::GetAtkObject(parent) : nullptr; } else if (ProxyAccessible* proxy = GetProxy(aAtkObj)) { ProxyAccessible* parent = proxy->Parent(); - atkParent = parent ? GetWrapperFor(parent) : nullptr; + if (parent) + atkParent = GetWrapperFor(parent); + + // Otherwise this should be the proxy for the tab's top level document. + atkParent = AccessibleWrap::GetAtkObject(proxy->OuterDocOfRemoteBrowser()); } if (atkParent) @@ -859,6 +863,9 @@ getIndexInParentCB(AtkObject* aAtkObj) if (ProxyAccessible* parent = proxy->Parent()) return parent->IndexOfEmbeddedChild(proxy); + if (proxy->OuterDocOfRemoteBrowser()) + return 0; + return -1; } diff --git a/accessible/ipc/ProxyAccessible.cpp b/accessible/ipc/ProxyAccessible.cpp index 877536601daf..6a16197a652f 100644 --- a/accessible/ipc/ProxyAccessible.cpp +++ b/accessible/ipc/ProxyAccessible.cpp @@ -6,6 +6,10 @@ #include "ProxyAccessible.h" #include "DocAccessibleParent.h" +#include "DocAccessible.h" +#include "mozilla/a11y/DocManager.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/TabParent.h" #include "mozilla/unused.h" #include "mozilla/a11y/Platform.h" #include "RelationType.h" @@ -963,5 +967,19 @@ ProxyAccessible::URLDocTypeMimeType(nsString& aURL, nsString& aDocType, unused << mDoc->SendURLDocTypeMimeType(mID, &aURL, &aDocType, &aMimeType); } +Accessible* +ProxyAccessible::OuterDocOfRemoteBrowser() const +{ + auto tab = static_cast(mDoc->Manager()); + dom::Element* frame = tab->GetOwnerElement(); + NS_ASSERTION(frame, "why isn't the tab in a frame!"); + if (!frame) + return nullptr; + + DocAccessible* chromeDoc = GetExistingDocAccessible(frame->OwnerDoc()); + NS_ASSERTION(chromeDoc, "accessible tab in not accessible chromeDocument"); + + return chromeDoc ? chromeDoc->GetAccessible(frame) : nullptr; +} } } diff --git a/accessible/ipc/ProxyAccessible.h b/accessible/ipc/ProxyAccessible.h index d53e48cbcfdd..e36c113fe043 100644 --- a/accessible/ipc/ProxyAccessible.h +++ b/accessible/ipc/ProxyAccessible.h @@ -18,6 +18,7 @@ namespace mozilla { namespace a11y { +class Accessible; class Attribute; class DocAccessibleParent; enum class RelationType; @@ -65,6 +66,8 @@ public: */ ProxyAccessible* Parent() const { return mParent; } + Accessible* OuterDocOfRemoteBrowser() const; + /** * Get the role of the accessible we're proxying. */ diff --git a/accessible/mac/AccessibleWrap.h b/accessible/mac/AccessibleWrap.h index 7871947f7c71..3a8ab84a0a36 100644 --- a/accessible/mac/AccessibleWrap.h +++ b/accessible/mac/AccessibleWrap.h @@ -59,10 +59,7 @@ public: // construction, destruction * for it. */ bool IsIgnored(); - - inline bool HasPopup () - { return (NativeState() & mozilla::a11y::states::HASPOPUP); } - + /** * Returns this accessible's all children, adhering to "flat" accessibles by * not returning their children. @@ -108,6 +105,8 @@ private: bool mNativeInited; }; +Class GetTypeFromRole(roles::Role aRole); + } // namespace a11y } // namespace mozilla diff --git a/accessible/mac/AccessibleWrap.mm b/accessible/mac/AccessibleWrap.mm index 16a38d7f18f4..10fded7ed969 100644 --- a/accessible/mac/AccessibleWrap.mm +++ b/accessible/mac/AccessibleWrap.mm @@ -15,6 +15,7 @@ #import "mozHTMLAccessible.h" #import "mozTextAccessible.h" +using namespace mozilla; using namespace mozilla::a11y; AccessibleWrap:: @@ -33,8 +34,10 @@ AccessibleWrap::GetNativeObject() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - if (!mNativeInited && !mNativeObject && !IsDefunct() && !AncestorIsFlat()) - mNativeObject = [[GetNativeType() alloc] initWithAccessible:this]; + if (!mNativeInited && !mNativeObject && !IsDefunct() && !AncestorIsFlat()) { + uintptr_t accWrap = reinterpret_cast(this); + mNativeObject = [[GetNativeType() alloc] initWithAccessible:accWrap]; + } mNativeInited = true; @@ -59,51 +62,7 @@ AccessibleWrap::GetNativeType () if (IsXULTabpanels()) return [mozPaneAccessible class]; - roles::Role role = Role(); - switch (role) { - case roles::PUSHBUTTON: - case roles::SPLITBUTTON: - case roles::TOGGLE_BUTTON: - { - // if this button may show a popup, let's make it of the popupbutton type. - return HasPopup() ? [mozPopupButtonAccessible class] : - [mozButtonAccessible class]; - } - - case roles::PAGETAB: - return [mozButtonAccessible class]; - - case roles::CHECKBUTTON: - return [mozCheckboxAccessible class]; - - case roles::HEADING: - return [mozHeadingAccessible class]; - - case roles::PAGETABLIST: - return [mozTabsAccessible class]; - - case roles::ENTRY: - case roles::STATICTEXT: - case roles::CAPTION: - case roles::ACCEL_LABEL: - case roles::PASSWORD_TEXT: - // normal textfield (static or editable) - return [mozTextAccessible class]; - - case roles::TEXT_LEAF: - return [mozTextLeafAccessible class]; - - case roles::LINK: - return [mozLinkAccessible class]; - - case roles::COMBOBOX: - return [mozPopupButtonAccessible class]; - - default: - return [mozAccessible class]; - } - - return nil; + return GetTypeFromRole(Role()); NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } @@ -274,3 +233,52 @@ AccessibleWrap::AncestorIsFlat() // no parent was flat return false; } + +Class +a11y::GetTypeFromRole(roles::Role aRole) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; + + switch (aRole) { + case roles::COMBOBOX: + case roles::PUSHBUTTON: + case roles::SPLITBUTTON: + case roles::TOGGLE_BUTTON: + { + return [mozButtonAccessible class]; + } + + case roles::PAGETAB: + return [mozButtonAccessible class]; + + case roles::CHECKBUTTON: + return [mozCheckboxAccessible class]; + + case roles::HEADING: + return [mozHeadingAccessible class]; + + case roles::PAGETABLIST: + return [mozTabsAccessible class]; + + case roles::ENTRY: + case roles::STATICTEXT: + case roles::CAPTION: + case roles::ACCEL_LABEL: + case roles::PASSWORD_TEXT: + // normal textfield (static or editable) + return [mozTextAccessible class]; + + case roles::TEXT_LEAF: + return [mozTextLeafAccessible class]; + + case roles::LINK: + return [mozLinkAccessible class]; + + default: + return [mozAccessible class]; + } + + return nil; + + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; +} diff --git a/accessible/mac/Platform.mm b/accessible/mac/Platform.mm index 8503159818c4..8c8423dd88e4 100644 --- a/accessible/mac/Platform.mm +++ b/accessible/mac/Platform.mm @@ -7,6 +7,7 @@ #import #include "Platform.h" +#include "ProxyAccessible.h" #include "nsAppShell.h" @@ -34,13 +35,23 @@ PlatformShutdown() } void -ProxyCreated(ProxyAccessible*, uint32_t) +ProxyCreated(ProxyAccessible* aProxy, uint32_t) { + // Pass in dummy state for now as retrieving proxy state requires IPC. + Class type = GetTypeFromRole(aProxy->Role()); + uintptr_t accWrap = reinterpret_cast(aProxy) | IS_PROXY; + mozAccessible* mozWrapper = [[type alloc] initWithAccessible:accWrap]; + aProxy->SetWrapper(reinterpret_cast(mozWrapper)); } void -ProxyDestroyed(ProxyAccessible*) +ProxyDestroyed(ProxyAccessible* aProxy) { + mozAccessible* wrapper = + reinterpret_cast(aProxy->GetWrapper()); + [wrapper expire]; + [wrapper release]; + aProxy->SetWrapper(0); } void diff --git a/accessible/mac/moz.build b/accessible/mac/moz.build index d5c6dce1ac22..2e582cad9762 100644 --- a/accessible/mac/moz.build +++ b/accessible/mac/moz.build @@ -30,6 +30,7 @@ LOCAL_INCLUDES += [ '/accessible/base', '/accessible/generic', '/accessible/html', + '/accessible/ipc', '/accessible/xul', '/layout/generic', '/layout/xul', diff --git a/accessible/mac/mozAccessible.h b/accessible/mac/mozAccessible.h index e24bde0fb7ba..b3989f60b163 100644 --- a/accessible/mac/mozAccessible.h +++ b/accessible/mac/mozAccessible.h @@ -57,11 +57,14 @@ static const uintptr_t IS_PROXY = 1; mozilla::a11y::role mRole; } -// return the Accessible for this mozAccessible. -- (mozilla::a11y::AccessibleWrap*) getGeckoAccessible; +// return the Accessible for this mozAccessible if it exists. +- (mozilla::a11y::AccessibleWrap*)getGeckoAccessible; + +// return the ProxyAccessible for this mozAccessible if it exists. +- (mozilla::a11y::ProxyAccessible*)getProxyAccessible; // inits with the gecko owner. -- (id)initWithAccessible:(mozilla::a11y::AccessibleWrap*)geckoParent; +- (id)initWithAccessible:(uintptr_t)aGeckoObj; // our accessible parent (AXParent) - (id )parent; diff --git a/accessible/mac/mozAccessible.mm b/accessible/mac/mozAccessible.mm index a6391faf0987..7b0632199369 100644 --- a/accessible/mac/mozAccessible.mm +++ b/accessible/mac/mozAccessible.mm @@ -60,15 +60,18 @@ GetClosestInterestingAccessible(id anObject) @implementation mozAccessible -- (id)initWithAccessible:(AccessibleWrap*)geckoAccessible +- (id)initWithAccessible:(uintptr_t)aGeckoAccessible { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; if ((self = [super init])) { - mGeckoAccessible = reinterpret_cast(geckoAccessible); - mRole = geckoAccessible->Role(); + mGeckoAccessible = aGeckoAccessible; + if (aGeckoAccessible & IS_PROXY) + mRole = [self getProxyAccessible]->Role(); + else + mRole = [self getGeckoAccessible]->Role(); } - + return self; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; @@ -92,6 +95,15 @@ GetClosestInterestingAccessible(id anObject) return reinterpret_cast(mGeckoAccessible); } + +- (mozilla::a11y::ProxyAccessible*)getProxyAccessible +{ + // Check if mGeckoAccessible points at a proxy + if (!(mGeckoAccessible & IS_PROXY)) + return nil; + + return reinterpret_cast(mGeckoAccessible & ~IS_PROXY); +} #pragma mark - diff --git a/accessible/mac/mozActionElements.h b/accessible/mac/mozActionElements.h index d3769bbc7e40..a325921eb84f 100644 --- a/accessible/mac/mozActionElements.h +++ b/accessible/mac/mozActionElements.h @@ -9,6 +9,9 @@ /* Simple subclasses for things like checkboxes, buttons, etc. */ @interface mozButtonAccessible : mozAccessible + { + } +- (BOOL)hasPopup; - (void)click; - (BOOL)isTab; @end @@ -18,10 +21,6 @@ - (int)isChecked; @end -/* Used for buttons that may pop up a menu. */ -@interface mozPopupButtonAccessible : mozButtonAccessible -@end - /* Class for tabs - not individual tabs */ @interface mozTabsAccessible : mozAccessible { diff --git a/accessible/mac/mozActionElements.mm b/accessible/mac/mozActionElements.mm index b5593b62229e..98a15330a350 100644 --- a/accessible/mac/mozActionElements.mm +++ b/accessible/mac/mozActionElements.mm @@ -42,6 +42,7 @@ enum CheckboxValue { NSAccessibilityEnabledAttribute, // required NSAccessibilityFocusedAttribute, // required NSAccessibilityTitleAttribute, // required + NSAccessibilityChildrenAttribute, NSAccessibilityDescriptionAttribute, #if DEBUG @"AXMozDescription", @@ -57,15 +58,19 @@ enum CheckboxValue { { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) + if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { + if ([self hasPopup]) + return [self children]; return nil; + } + if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { if ([self isTab]) return utils::LocalizedString(NS_LITERAL_STRING("tab")); - + return NSAccessibilityRoleDescription([self role], nil); } - + return [super accessibilityAttributeValue:attribute]; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; @@ -80,36 +85,49 @@ enum CheckboxValue { { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - if ([self isEnabled]) + if ([self isEnabled]) { + if ([self hasPopup]) + return [NSArray arrayWithObjects:NSAccessibilityPressAction, + NSAccessibilityShowMenuAction, + nil]; return [NSArray arrayWithObject:NSAccessibilityPressAction]; - + } return nil; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } -- (NSString*)accessibilityActionDescription:(NSString*)action +- (NSString*)accessibilityActionDescription:(NSString*)action { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; if ([action isEqualToString:NSAccessibilityPressAction]) { if ([self isTab]) return utils::LocalizedString(NS_LITERAL_STRING("switch")); - + return @"press button"; // XXX: localize this later? } - + + if ([self hasPopup]) { + if ([action isEqualToString:NSAccessibilityShowMenuAction]) + return @"show menu"; + } + return nil; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } -- (void)accessibilityPerformAction:(NSString*)action +- (void)accessibilityPerformAction:(NSString*)action { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - if ([action isEqualToString:NSAccessibilityPressAction]) + if ([self isEnabled] && [action isEqualToString:NSAccessibilityPressAction]) { + // TODO: this should bring up the menu, but currently doesn't. + // once msaa and atk have merged better, they will implement + // the action needed to show the menu. [self click]; + } NS_OBJC_END_TRY_ABORT_BLOCK; } @@ -127,6 +145,12 @@ enum CheckboxValue { return (accWrap && (accWrap->Role() == roles::PAGETAB)); } +- (BOOL)hasPopup +{ + AccessibleWrap* accWrap = [self getGeckoAccessible]; + return accWrap && (accWrap->NativeState() & mozilla::a11y::states::HASPOPUP); +} + @end @implementation mozCheckboxAccessible @@ -170,91 +194,6 @@ enum CheckboxValue { @end -@implementation mozPopupButtonAccessible - -- (NSArray *)accessibilityAttributeNames -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - static NSArray *attributes = nil; - - if (!attributes) { - attributes = [[NSArray alloc] initWithObjects:NSAccessibilityParentAttribute, // required - NSAccessibilityPositionAttribute, // required - NSAccessibilityRoleAttribute, // required - NSAccessibilitySizeAttribute, // required - NSAccessibilityWindowAttribute, // required - NSAccessibilityTopLevelUIElementAttribute, // required - NSAccessibilityHelpAttribute, - NSAccessibilityEnabledAttribute, // required - NSAccessibilityFocusedAttribute, // required - NSAccessibilityTitleAttribute, // required for popupmenus, and for menubuttons with a title - NSAccessibilityChildrenAttribute, // required - NSAccessibilityDescriptionAttribute, // required if it has no title attr -#if DEBUG - @"AXMozDescription", -#endif - nil]; - } - return attributes; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (id)accessibilityAttributeValue:(NSString *)attribute -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { - return [super children]; - } - return [super accessibilityAttributeValue:attribute]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (NSArray *)accessibilityActionNames -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if ([self isEnabled]) { - return [NSArray arrayWithObjects:NSAccessibilityPressAction, - NSAccessibilityShowMenuAction, - nil]; - } - return nil; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (NSString *)accessibilityActionDescription:(NSString *)action -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if ([action isEqualToString:NSAccessibilityShowMenuAction]) - return @"show menu"; - return [super accessibilityActionDescription:action]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void)accessibilityPerformAction:(NSString *)action -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // both the ShowMenu and Click action do the same thing. - if ([self isEnabled]) { - // TODO: this should bring up the menu, but currently doesn't. - // once msaa and atk have merged better, they will implement - // the action needed to show the menu. - [super click]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@end - @implementation mozTabsAccessible - (void)dealloc diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index b2ed23e7515d..c57530d07138 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -37,6 +37,7 @@ SEARCH_PATHS = [ 'python/which', 'python/pystache', 'python/pyyaml/lib', + 'python/requests', 'build', 'build/pymake', 'config', diff --git a/build/mobile/sutagent/android/fencp/FileCursor.java b/build/mobile/sutagent/android/fencp/FileCursor.java index d04870c92cce..036f36a0f9b7 100644 --- a/build/mobile/sutagent/android/fencp/FileCursor.java +++ b/build/mobile/sutagent/android/fencp/FileCursor.java @@ -65,7 +65,6 @@ public class FileCursor extends AbstractWindowedCursor { nCount = 1; } - mRowIdColumnIndex = 0; } } } diff --git a/build/mobile/sutagent/android/ffxcp/FileCursor.java b/build/mobile/sutagent/android/ffxcp/FileCursor.java index af0e0eb4456a..52b15e6aa10e 100644 --- a/build/mobile/sutagent/android/ffxcp/FileCursor.java +++ b/build/mobile/sutagent/android/ffxcp/FileCursor.java @@ -66,7 +66,6 @@ public class FileCursor extends AbstractWindowedCursor { nCount = 1; } - mRowIdColumnIndex = 0; } } } diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index fae2251523c0..a83d1995b3fa 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2228,7 +2228,7 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescrip if (sPostGCEventsToConsole) { NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) "); nsString prefix, gcstats; - gcstats.Adopt(aDesc.formatMessage(aRt)); + gcstats.Adopt(aDesc.formatSummaryMessage(aRt)); prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC)); nsString msg = prefix + gcstats; @@ -2304,6 +2304,15 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescrip nsCycleCollector_dispatchDeferredDeletion(); } + if (sPostGCEventsToConsole) { + nsString gcstats; + gcstats.Adopt(aDesc.formatSliceMessage(aRt)); + nsCOMPtr cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (cs) { + cs->LogStringMessage(gcstats.get()); + } + } + break; default: diff --git a/dom/base/nsXMLHttpRequest.cpp b/dom/base/nsXMLHttpRequest.cpp index 1548a6f07bc6..2912ff4fd626 100644 --- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -10,6 +10,7 @@ #include #endif #include "mozilla/ArrayUtils.h" +#include "mozilla/CheckedInt.h" #include "mozilla/dom/BlobSet.h" #include "mozilla/dom/File.h" #include "mozilla/dom/XMLHttpRequestUploadBinding.h" @@ -3993,26 +3994,30 @@ ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen, { MOZ_ASSERT(!mMapPtr); + CheckedUint32 neededCapacity = mLength; + neededCapacity += aDataLen; + if (!neededCapacity.isValid()) { + return false; + } if (mLength + aDataLen > mCapacity) { - uint32_t newcap; + CheckedUint32 newcap = mCapacity; // Double while under aMaxGrowth or if not specified. if (!aMaxGrowth || mCapacity < aMaxGrowth) { - newcap = mCapacity * 2; + newcap *= 2; } else { - newcap = mCapacity + aMaxGrowth; + newcap += aMaxGrowth; } - // But make sure there's always enough to satisfy our request. - if (newcap < mLength + aDataLen) { - newcap = mLength + aDataLen; - } - - // Did we overflow? - if (newcap < mCapacity) { + if (!newcap.isValid()) { return false; } - if (!setCapacity(newcap)) { + // But make sure there's always enough to satisfy our request. + if (newcap.value() < neededCapacity.value()) { + newcap = neededCapacity; + } + + if (!setCapacity(newcap.value())) { return false; } } diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 597276cbbdc9..e58e817406ab 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -3454,17 +3454,21 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess const gfxTextRun::DetailedGlyph *d = mTextRun->GetDetailedGlyphs(i); - if (glyphs[i].IsMissing() && d->mAdvance > 0) { - newGlyph.mIndex = 0; - if (rtl) { - inlinePos = baselineOriginInline - advanceSum - - d->mAdvance * devUnitsPerAppUnit; - } else { - inlinePos = baselineOriginInline + advanceSum; + if (glyphs[i].IsMissing()) { + if (d->mAdvance > 0) { + // Perhaps we should render a hexbox here, but for now + // we just draw the font's .notdef glyph. (See bug 808288.) + newGlyph.mIndex = 0; + if (rtl) { + inlinePos = baselineOriginInline - advanceSum - + d->mAdvance * devUnitsPerAppUnit; + } else { + inlinePos = baselineOriginInline + advanceSum; + } + blockPos = baselineOriginBlock; + advanceSum += d->mAdvance * devUnitsPerAppUnit; + glyphBuf.push_back(newGlyph); } - blockPos = baselineOriginBlock; - advanceSum += d->mAdvance * devUnitsPerAppUnit; - glyphBuf.push_back(newGlyph); continue; } diff --git a/dom/smil/nsSMILAnimationFunction.cpp b/dom/smil/nsSMILAnimationFunction.cpp index c7d19db56b88..276a85f48b1d 100644 --- a/dom/smil/nsSMILAnimationFunction.cpp +++ b/dom/smil/nsSMILAnimationFunction.cpp @@ -4,8 +4,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/. */ -#include "mozilla/dom/SVGAnimationElement.h" #include "nsSMILAnimationFunction.h" + +#include "mozilla/dom/SVGAnimationElement.h" +#include "mozilla/Move.h" #include "nsISMILAttr.h" #include "nsSMILParserUtils.h" #include "nsSMILNullType.h" @@ -267,9 +269,7 @@ nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr, // If additive animation isn't required or isn't supported, set the value. if (!isAdditive || NS_FAILED(aResult.SandwichAdd(result))) { - aResult.Swap(result); - // Note: The old value of aResult is now in |result|, and it will get - // cleaned up when |result| goes out of scope, when this function returns. + aResult = Move(result); } } diff --git a/dom/smil/nsSMILCSSProperty.cpp b/dom/smil/nsSMILCSSProperty.cpp index 5d42c8bf5df0..b0e6bc9e0ba7 100644 --- a/dom/smil/nsSMILCSSProperty.cpp +++ b/dom/smil/nsSMILCSSProperty.cpp @@ -7,11 +7,13 @@ /* representation of a SMIL-animatable CSS property on an element */ #include "nsSMILCSSProperty.h" + +#include "mozilla/dom/Element.h" +#include "mozilla/Move.h" #include "nsSMILCSSValueType.h" #include "nsSMILValue.h" #include "nsComputedDOMStyle.h" #include "nsCSSProps.h" -#include "mozilla/dom/Element.h" #include "nsIDOMElement.h" #include "nsIDocument.h" @@ -81,7 +83,7 @@ nsSMILCSSProperty::GetBaseValue() const // In either case, just return a dummy value (initialized with the right // type, so as not to indicate failure). nsSMILValue tmpVal(&nsSMILCSSValueType::sSingleton); - baseValue.Swap(tmpVal); + Swap(baseValue, tmpVal); return baseValue; } diff --git a/dom/smil/nsSMILValue.cpp b/dom/smil/nsSMILValue.cpp index c701fcda4f16..cd881b0b016d 100644 --- a/dom/smil/nsSMILValue.cpp +++ b/dom/smil/nsSMILValue.cpp @@ -44,6 +44,35 @@ nsSMILValue::operator=(const nsSMILValue& aVal) return *this; } +// Move constructor / reassignment operator: +nsSMILValue::nsSMILValue(nsSMILValue&& aVal) + : mU(aVal.mU), // Copying union is only OK because we clear aVal.mType below. + mType(aVal.mType) +{ + // Leave aVal with a null type, so that it's safely destructible (and won't + // mess with anything referenced by its union, which we've copied). + aVal.mType = nsSMILNullType::Singleton(); +} + +nsSMILValue& +nsSMILValue::operator=(nsSMILValue&& aVal) +{ + if (!IsNull()) { + // Clean up any data we're currently tracking. + DestroyAndCheckPostcondition(); + } + + // Copy the union (which could include a pointer to external memory) & mType: + mU = aVal.mU; + mType = aVal.mType; + + // Leave aVal with a null type, so that it's safely destructible (and won't + // mess with anything referenced by its union, which we've now copied). + aVal.mType = nsSMILNullType::Singleton(); + + return *this; +} + bool nsSMILValue::operator==(const nsSMILValue& aVal) const { @@ -53,19 +82,6 @@ nsSMILValue::operator==(const nsSMILValue& aVal) const return mType == aVal.mType && mType->IsEqual(*this, aVal); } -void -nsSMILValue::Swap(nsSMILValue& aOther) -{ - nsSMILValue tmp; - memcpy(&tmp, &aOther, sizeof(nsSMILValue)); // tmp = aOther - memcpy(&aOther, this, sizeof(nsSMILValue)); // aOther = this - memcpy(this, &tmp, sizeof(nsSMILValue)); // this = tmp - - // |tmp| is about to die -- we need to clear its mType, so that its - // destructor doesn't muck with the data we just transferred out of it. - tmp.mType = nsSMILNullType::Singleton(); -} - nsresult nsSMILValue::Add(const nsSMILValue& aValueToAdd, uint32_t aCount) { diff --git a/dom/smil/nsSMILValue.h b/dom/smil/nsSMILValue.h index 1649eb85c5a9..c0998d61dbe8 100644 --- a/dom/smil/nsSMILValue.h +++ b/dom/smil/nsSMILValue.h @@ -33,6 +33,10 @@ public: const nsSMILValue& operator=(const nsSMILValue& aVal); + // Move constructor / reassignment operator: + nsSMILValue(nsSMILValue&& aVal); + nsSMILValue& operator=(nsSMILValue&& aVal); + // Equality operators. These are allowed to be conservative (return false // more than you'd expect) - see comment above nsISMILType::IsEqual. bool operator==(const nsSMILValue& aVal) const; @@ -45,9 +49,6 @@ public: return (mType == nsSMILNullType::Singleton()); } - // Swaps the member data (mU & mPtr) of |this| with |aOther| - void Swap(nsSMILValue& aOther); - nsresult Add(const nsSMILValue& aValueToAdd, uint32_t aCount = 1); nsresult SandwichAdd(const nsSMILValue& aValueToAdd); nsresult ComputeDistance(const nsSMILValue& aTo, double& aDistance) const; diff --git a/dom/svg/SVGAnimatedLengthList.cpp b/dom/svg/SVGAnimatedLengthList.cpp index a759329d8abc..91702a16b416 100644 --- a/dom/svg/SVGAnimatedLengthList.cpp +++ b/dom/svg/SVGAnimatedLengthList.cpp @@ -5,7 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SVGAnimatedLengthList.h" + #include "DOMSVGAnimatedLengthList.h" +#include "mozilla/Move.h" #include "nsSVGElement.h" #include "nsSVGAttrTearoffTable.h" #include "nsSMILValue.h" @@ -138,7 +140,7 @@ SVGAnimatedLengthList:: nsresult rv = llai->SetValueFromString(aStr); if (NS_SUCCEEDED(rv)) { llai->SetInfo(mElement, mAxis, mCanZeroPadList); - aValue.Swap(val); + aValue = Move(val); // If any of the lengths in the list depend on their context, then we must // prevent caching of the entire animation sandwich. This is because the @@ -181,7 +183,7 @@ SVGAnimatedLengthList::SMILAnimatedLengthList::GetBaseValue() const nsresult rv = llai->CopyFrom(mVal->mBaseVal); if (NS_SUCCEEDED(rv)) { llai->SetInfo(mElement, mAxis, mCanZeroPadList); - val.Swap(tmp); + val = Move(tmp); } return val; } diff --git a/dom/svg/SVGAnimatedNumberList.cpp b/dom/svg/SVGAnimatedNumberList.cpp index 7e566a0d33dd..9be7ffadd80b 100644 --- a/dom/svg/SVGAnimatedNumberList.cpp +++ b/dom/svg/SVGAnimatedNumberList.cpp @@ -5,7 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SVGAnimatedNumberList.h" + #include "DOMSVGAnimatedNumberList.h" +#include "mozilla/Move.h" #include "nsSVGElement.h" #include "nsSVGAttrTearoffTable.h" #include "nsSMILValue.h" @@ -138,7 +140,7 @@ SVGAnimatedNumberList:: nsresult rv = nlai->SetValueFromString(aStr); if (NS_SUCCEEDED(rv)) { nlai->SetInfo(mElement); - aValue.Swap(val); + aValue = Move(val); } aPreventCachingOfSandwich = false; return rv; @@ -157,7 +159,7 @@ SVGAnimatedNumberList::SMILAnimatedNumberList::GetBaseValue() const nsresult rv = nlai->CopyFrom(mVal->mBaseVal); if (NS_SUCCEEDED(rv)) { nlai->SetInfo(mElement); - val.Swap(tmp); + Swap(val, tmp); } return val; } diff --git a/dom/svg/SVGAnimatedPathSegList.cpp b/dom/svg/SVGAnimatedPathSegList.cpp index 5f3f7a0da3b0..4f07d20404d6 100644 --- a/dom/svg/SVGAnimatedPathSegList.cpp +++ b/dom/svg/SVGAnimatedPathSegList.cpp @@ -5,7 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SVGAnimatedPathSegList.h" + #include "DOMSVGPathSegList.h" +#include "mozilla/Move.h" #include "nsSVGElement.h" #include "nsSVGAttrTearoffTable.h" #include "nsSMILValue.h" @@ -160,7 +162,7 @@ SVGAnimatedPathSegList:: nsresult rv = list->SetValueFromString(aStr); if (NS_SUCCEEDED(rv)) { list->SetElement(mElement); - aValue.Swap(val); + aValue = Move(val); } aPreventCachingOfSandwich = false; return rv; @@ -179,7 +181,7 @@ SVGAnimatedPathSegList::SMILAnimatedPathSegList::GetBaseValue() const nsresult rv = list->CopyFrom(mVal->mBaseVal); if (NS_SUCCEEDED(rv)) { list->SetElement(mElement); - val.Swap(tmp); + val = Move(tmp); } return val; } diff --git a/dom/svg/SVGAnimatedPointList.cpp b/dom/svg/SVGAnimatedPointList.cpp index c411b53b891d..13031901d77a 100644 --- a/dom/svg/SVGAnimatedPointList.cpp +++ b/dom/svg/SVGAnimatedPointList.cpp @@ -5,7 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SVGAnimatedPointList.h" + #include "DOMSVGPointList.h" +#include "mozilla/Move.h" #include "nsSVGElement.h" #include "nsSVGAttrTearoffTable.h" #include "nsSMILValue.h" @@ -163,7 +165,7 @@ SVGAnimatedPointList:: nsresult rv = list->SetValueFromString(aStr); if (NS_SUCCEEDED(rv)) { list->SetInfo(mElement); - aValue.Swap(val); + aValue = Move(val); } aPreventCachingOfSandwich = false; return rv; @@ -182,7 +184,7 @@ SVGAnimatedPointList::SMILAnimatedPointList::GetBaseValue() const nsresult rv = list->CopyFrom(mVal->mBaseVal); if (NS_SUCCEEDED(rv)) { list->SetInfo(mElement); - val.Swap(tmp); + Swap(val, tmp); } return val; } diff --git a/dom/svg/nsSVGAngle.cpp b/dom/svg/nsSVGAngle.cpp index 15a9301ac15f..2c3c5f94cd36 100644 --- a/dom/svg/nsSVGAngle.cpp +++ b/dom/svg/nsSVGAngle.cpp @@ -4,10 +4,11 @@ * 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/ArrayUtils.h" - #include "nsSVGAngle.h" + +#include "mozilla/ArrayUtils.h" #include "mozilla/dom/SVGMarkerElement.h" +#include "mozilla/Move.h" #include "nsContentUtils.h" // NS_ENSURE_FINITE #include "nsSMILValue.h" #include "nsSVGAttrTearoffTable.h" @@ -383,7 +384,7 @@ nsSVGAngle::SMILOrient::ValueFromString(const nsAString& aStr, val.mU.mOrient.mUnit = unitType; val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_ANGLE; } - aValue.Swap(val); + aValue = Move(val); aPreventCachingOfSandwich = false; return NS_OK; diff --git a/dom/svg/nsSVGAnimatedTransformList.cpp b/dom/svg/nsSVGAnimatedTransformList.cpp index 1da21c0fc863..dd4d550e5f40 100644 --- a/dom/svg/nsSVGAnimatedTransformList.cpp +++ b/dom/svg/nsSVGAnimatedTransformList.cpp @@ -5,8 +5,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsSVGAnimatedTransformList.h" + #include "mozilla/dom/SVGAnimatedTransformList.h" #include "mozilla/dom/SVGAnimationElement.h" +#include "mozilla/Move.h" #include "nsCharSeparatedTokenizer.h" #include "nsSVGTransform.h" #include "nsSMILValue.h" @@ -248,7 +250,7 @@ nsSVGAnimatedTransformList::SMILAnimatedTransformList::ParseValue( } // Success! Populate our outparam with parsed value. - aResult.Swap(val); + aResult = Move(val); } int32_t diff --git a/dom/svg/nsSVGClass.cpp b/dom/svg/nsSVGClass.cpp index 826fa0d0886b..bb114b9a8629 100644 --- a/dom/svg/nsSVGClass.cpp +++ b/dom/svg/nsSVGClass.cpp @@ -5,10 +5,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsSVGClass.h" + +#include "mozilla/dom/SVGAnimatedString.h" +#include "mozilla/Move.h" #include "nsSVGElement.h" #include "nsSMILValue.h" #include "SMILStringType.h" -#include "mozilla/dom/SVGAnimatedString.h" using namespace mozilla; using namespace mozilla::dom; @@ -130,7 +132,7 @@ nsSVGClass::SMILString::ValueFromString(const nsAString& aStr, nsSMILValue val(SMILStringType::Singleton()); *static_cast(val.mU.mPtr) = aStr; - aValue.Swap(val); + aValue = Move(val); aPreventCachingOfSandwich = false; return NS_OK; } diff --git a/dom/svg/nsSVGString.cpp b/dom/svg/nsSVGString.cpp index 041e013de16e..7c3de686808d 100644 --- a/dom/svg/nsSVGString.cpp +++ b/dom/svg/nsSVGString.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsSVGString.h" + +#include "mozilla/Move.h" #include "nsSVGAttrTearoffTable.h" #include "nsSMILValue.h" #include "SMILStringType.h" @@ -110,7 +112,7 @@ nsSVGString::SMILString::ValueFromString(const nsAString& aStr, nsSMILValue val(SMILStringType::Singleton()); *static_cast(val.mU.mPtr) = aStr; - aValue.Swap(val); + aValue = Move(val); aPreventCachingOfSandwich = false; return NS_OK; } diff --git a/dom/svg/nsSVGViewBox.cpp b/dom/svg/nsSVGViewBox.cpp index ff11939b9bd6..61e345a37fec 100644 --- a/dom/svg/nsSVGViewBox.cpp +++ b/dom/svg/nsSVGViewBox.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsSVGViewBox.h" + +#include "mozilla/Move.h" #include "nsCharSeparatedTokenizer.h" #include "nsSMILValue.h" #include "nsTextFormatter.h" @@ -300,7 +302,7 @@ nsSVGViewBox::SMILViewBox } nsSMILValue val(&SVGViewBoxSMILType::sSingleton); *static_cast(val.mU.mPtr) = viewBox; - aValue.Swap(val); + aValue = Move(val); aPreventCachingOfSandwich = false; return NS_OK; diff --git a/dom/xul/templates/nsTemplateMap.h b/dom/xul/templates/nsTemplateMap.h index 71203f559746..25cc01481deb 100644 --- a/dom/xul/templates/nsTemplateMap.h +++ b/dom/xul/templates/nsTemplateMap.h @@ -16,21 +16,12 @@ protected: nsIContent* mTemplate; }; - PLDHashTable mTable; - - void - Init() - { - PL_DHashTableInit(&mTable, PL_DHashGetStubOps(), sizeof(Entry)); - } - - void - Finish() { PL_DHashTableFinish(&mTable); } + PLDHashTable2 mTable; public: - nsTemplateMap() { Init(); } + nsTemplateMap() : mTable(PL_DHashGetStubOps(), sizeof(Entry)) { } - ~nsTemplateMap() { Finish(); } + ~nsTemplateMap() { } void Put(nsIContent* aContent, nsIContent* aTemplate) { @@ -70,7 +61,7 @@ public: } void - Clear() { Finish(); Init(); } + Clear() { mTable.Clear(); } }; #endif // nsTemplateMap_h__ diff --git a/embedding/components/build/moz.build b/embedding/components/build/moz.build index 1108b5ceb57c..2412e7d1dd31 100644 --- a/embedding/components/build/moz.build +++ b/embedding/components/build/moz.build @@ -26,6 +26,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': '../printingui/win', ] elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + DEFINES['PROXY_PRINTING'] = 1 LOCAL_INCLUDES += [ '../printingui/mac', ] diff --git a/embedding/components/printingui/ipc/PPrintingTypes.ipdlh b/embedding/components/printingui/ipc/PPrintingTypes.ipdlh index 4540ef2fbf3a..a382bf9abbfc 100644 --- a/embedding/components/printingui/ipc/PPrintingTypes.ipdlh +++ b/embedding/components/printingui/ipc/PPrintingTypes.ipdlh @@ -72,6 +72,7 @@ struct PrintData { bool isInitializedFromPrinter; bool isInitializedFromPrefs; bool persistMarginBoxSettings; + int32_t optionFlags; /* Windows-specific things */ nsString driverName; @@ -91,9 +92,20 @@ struct PrintData { CStringKeyValue[] GTKPrintSettings; /** - * TODO: OS X specific things - specifically, an array of names for the - * document to be supplied by nsIWebBrowserPrint::enumerateDocumentNames + * OS X specific things. */ + nsString printJobName; + bool printAllPages; + bool mustCollate; + nsString disposition; + /** TODO: Is there an "unsigned short" primitive? **/ + short pagesAcross; + short pagesDown; + double printTime; + bool detailedErrorReporting; + nsString faxNumber; + bool addHeaderAndFooter; + bool fileNameExtensionHidden; }; } // namespace embedding diff --git a/embedding/components/printingui/ipc/PrintDataUtils.cpp b/embedding/components/printingui/ipc/PrintDataUtils.cpp index 8afd4d739567..3c121895d376 100644 --- a/embedding/components/printingui/ipc/PrintDataUtils.cpp +++ b/embedding/components/printingui/ipc/PrintDataUtils.cpp @@ -129,7 +129,23 @@ NS_IMETHODIMP MockWebBrowserPrint::EnumerateDocumentNames(uint32_t* aCount, char16_t*** aResult) { - return NS_ERROR_NOT_IMPLEMENTED; + *aCount = 0; + *aResult = nullptr; + + if (mData.printJobName().IsEmpty()) { + return NS_OK; + } + + // The only consumer that cares about this is the OS X printing + // dialog, and even then, it only cares about the first document + // name. That's why we only send a single document name through + // PrintData. + char16_t** array = (char16_t**) moz_xmalloc(sizeof(char16_t*)); + array[0] = ToNewUnicode(mData.printJobName()); + + *aCount = 1; + *aResult = array; + return NS_OK; } NS_IMETHODIMP diff --git a/gfx/2d/unittest/TestCairo.cpp b/gfx/2d/unittest/TestCairo.cpp index f73e1772d394..1ea4626b886d 100644 --- a/gfx/2d/unittest/TestCairo.cpp +++ b/gfx/2d/unittest/TestCairo.cpp @@ -49,5 +49,48 @@ TEST(Cairo, Bug825721) { TryCircle(0.0, 1.0, 5761126469220696064.0); } +TEST(Cairo, Bug1063486) { + + double x1, y1, x2, y2; + const double epsilon = .01; + + cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); + ASSERT_TRUE(surf != nullptr); + + cairo_t *cairo = cairo_create(surf); + ASSERT_TRUE(cairo != nullptr); + + printf("Path 1\n"); + cairo_move_to(cairo, -20, -10); + cairo_line_to(cairo, 20, -10); + cairo_line_to(cairo, 20, 10); + cairo_curve_to(cairo, 10,10, -10,10, -20,10); + cairo_curve_to(cairo, -30,10, -30,-10, -20,-10); + + cairo_path_extents(cairo, &x1, &y1, &x2, &y2); + + ASSERT_LT(std::abs(-27.5 - x1), epsilon); // the failing coordinate + ASSERT_LT(std::abs(-10 - y1), epsilon); + ASSERT_LT(std::abs(20 - x2), epsilon); + ASSERT_LT(std::abs(10 - y2), epsilon); + + printf("Path 2\n"); + cairo_new_path(cairo); + cairo_move_to(cairo, 10, 30); + cairo_line_to(cairo, 90, 30); + cairo_curve_to(cairo, 30,30, 30,30, 10,30); + cairo_curve_to(cairo, 0,30, 0,0, 30,5); + + cairo_path_extents(cairo, &x1, &y1, &x2, &y2); + + ASSERT_LT(std::abs(4.019531 - x1), epsilon); // the failing coordinate + ASSERT_LT(std::abs(4.437500 - y1), epsilon); + ASSERT_LT(std::abs(90. - x2), epsilon); + ASSERT_LT(std::abs(30. - y2), epsilon); + + cairo_surface_destroy(surf); + cairo_destroy(cairo); +} + } } diff --git a/gfx/cairo/cairo/src/cairo-path-bounds.c b/gfx/cairo/cairo/src/cairo-path-bounds.c index b7766494e316..8ca80fa137a6 100644 --- a/gfx/cairo/cairo/src/cairo-path-bounds.c +++ b/gfx/cairo/cairo/src/cairo-path-bounds.c @@ -131,6 +131,7 @@ _cairo_path_bounder_curve_to (void *closure, else { /* All control points are within the current extents. */ + bounder->current_point = *d; return CAIRO_STATUS_SUCCESS; } } diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index e8df05d23755..d46c76495981 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -733,6 +733,7 @@ struct ParamTraits WriteParam(aMsg, aParam.GetPageScrollAmount()); WriteParam(aMsg, aParam.AllowVerticalScrollWithWheel()); WriteParam(aMsg, aParam.mClipRect); + WriteParam(aMsg, aParam.mIsLayersIdRoot); WriteParam(aMsg, aParam.GetContentDescription()); } @@ -776,6 +777,7 @@ struct ParamTraits ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) && ReadParam(aMsg, aIter, &aResult->mAllowVerticalScrollWithWheel) && ReadParam(aMsg, aIter, &aResult->mClipRect) && + ReadParam(aMsg, aIter, &aResult->mIsLayersIdRoot) && ReadContentDescription(aMsg, aIter, aResult)); } }; diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index 319d0dfaac80..68696b84ddcc 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -67,6 +67,7 @@ public: , mLineScrollAmount(0, 0) , mPageScrollAmount(0, 0) , mAllowVerticalScrollWithWheel(false) + , mIsLayersIdRoot(false) { } @@ -100,7 +101,8 @@ public: mLineScrollAmount == aOther.mLineScrollAmount && mPageScrollAmount == aOther.mPageScrollAmount && mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel && - mClipRect == aOther.mClipRect; + mClipRect == aOther.mClipRect && + mIsLayersIdRoot == aOther.mIsLayersIdRoot; } bool operator!=(const FrameMetrics& aOther) const { @@ -525,6 +527,13 @@ public: return mClipRect.ref(); } + void SetIsLayersIdRoot(bool aValue) { + mIsLayersIdRoot = aValue; + } + bool IsLayersIdRoot() const { + return mIsLayersIdRoot; + } + private: // The pres-shell resolution that has been induced on the document containing @@ -699,6 +708,10 @@ private: // The clip rect to use when compositing a layer with this FrameMetrics. Maybe mClipRect; + // Whether these framemetrics are for the root scroll frame (root element if + // we don't have a root scroll frame) for its layers id. + bool mIsLayersIdRoot; + // WARNING!!!! // // When adding new fields to FrameMetrics, the following places should be diff --git a/gfx/layers/LayersLogging.cpp b/gfx/layers/LayersLogging.cpp index d7049de072b1..f8461e44f6bb 100644 --- a/gfx/layers/LayersLogging.cpp +++ b/gfx/layers/LayersLogging.cpp @@ -213,7 +213,7 @@ AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s, const char* pfx, const char* sfx) { aStream << pfx - << nsPrintfCString("{ l=%llu, p=%u, v=%llu }", s.mLayersId, s.mPresShellId, s.mScrollId).get() + << nsPrintfCString("{ l=%" PRIu64 ", p=%u, v=%" PRIu64 " }", s.mLayersId, s.mPresShellId, s.mScrollId).get() << sfx; } diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 20cf3bd9c2bb..054648e285ef 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -280,7 +280,8 @@ GetEventRegions(const LayerMetricsWrapper& aLayer) already_AddRefed APZCTreeManager::RecycleOrCreateNode(TreeBuildingState& aState, - AsyncPanZoomController* aApzc) + AsyncPanZoomController* aApzc, + uint64_t aLayersId) { // Find a node without an APZC and return it. Note that unless the layer tree // actually changes, this loop should generally do an early-return on the @@ -289,11 +290,11 @@ APZCTreeManager::RecycleOrCreateNode(TreeBuildingState& aState, nsRefPtr node = aState.mNodesToDestroy[i]; if (!node->IsPrimaryHolder()) { aState.mNodesToDestroy.RemoveElement(node); - node->RecycleWith(aApzc); + node->RecycleWith(aApzc, aLayersId); return node.forget(); } } - nsRefPtr node = new HitTestingTreeNode(aApzc, false); + nsRefPtr node = new HitTestingTreeNode(aApzc, false, aLayersId); return node.forget(); } @@ -333,7 +334,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, nsRefPtr node = nullptr; if (!needsApzc) { - node = RecycleOrCreateNode(aState, nullptr); + node = RecycleOrCreateNode(aState, nullptr, aLayersId); AttachNodeToTree(node, aParent, aNextSibling); node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), aLayer.GetClipRect() ? Some(ParentLayerIntRegion(*aLayer.GetClipRect())) : Nothing(), @@ -413,7 +414,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, apzc->ShareFrameMetricsAcrossProcesses(); } MOZ_ASSERT(node == nullptr); - node = new HitTestingTreeNode(apzc, true); + node = new HitTestingTreeNode(apzc, true, aLayersId); } else { // If we are re-using a node for this layer clear the tree pointers // so that it doesn't continue pointing to nodes that might no longer @@ -452,9 +453,9 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, // we are logging about APZCs is the scroll id, and otherwise we could // confuse APZCs from different layer trees with the same scroll id. if (aLayersId == aState.mOriginatingLayersId) { - if (apzc->IsRootForLayersId()) { + if (apzc->HasNoParentWithSameLayersId()) { aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), - "isRootForLayersId", true); + "hasNoParentWithSameLayersId", true); } else { MOZ_ASSERT(apzc->GetParent()); aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), @@ -463,7 +464,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, } if (newApzc) { - if (apzc->IsRootForLayersId()) { + if (apzc->HasNoParentWithSameLayersId()) { // If we just created a new apzc that is the root for its layers ID, then // we need to update its zoom constraints which might have arrived before this // was created @@ -487,7 +488,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, // now that will also be using that APZC. The hit-test region on the APZC needs // to be updated to deal with the new layer's hit region. - node = RecycleOrCreateNode(aState, apzc); + node = RecycleOrCreateNode(aState, apzc, aLayersId); AttachNodeToTree(node, aParent, aNextSibling); // Even though different layers associated with a given APZC may be at @@ -1039,7 +1040,7 @@ APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid, // For a given layers id, non-root APZCs inherit the zoom constraints // of their root. - if (node && node->GetApzc()->IsRootForLayersId()) { + if (node && node->GetApzc()->HasNoParentWithSameLayersId()) { UpdateZoomConstraintsRecursively(node.get(), aConstraints); } } @@ -1056,7 +1057,7 @@ APZCTreeManager::UpdateZoomConstraintsRecursively(HitTestingTreeNode* aNode, } for (HitTestingTreeNode* child = aNode->GetLastChild(); child; child = child->GetPrevSibling()) { // We can have subtrees with their own layers id - leave those alone. - if (child->GetApzc() && child->GetApzc()->IsRootForLayersId()) { + if (child->GetApzc() && child->GetApzc()->HasNoParentWithSameLayersId()) { continue; } UpdateZoomConstraintsRecursively(child, aConstraints); @@ -1343,7 +1344,7 @@ APZCTreeManager::BuildOverscrollHandoffChain(const nsRefPtrAdd(apzc); if (apzc->GetScrollHandoffParentId() == FrameMetrics::NULL_SCROLL_ID) { - if (!apzc->IsRootForLayersId()) { + if (!apzc->HasNoParentWithSameLayersId()) { // This probably indicates a bug or missed case in layout code NS_WARNING("Found a non-root APZ with no handoff parent"); } @@ -1361,7 +1362,7 @@ APZCTreeManager::BuildOverscrollHandoffChain(const nsRefPtrIsRootForLayersId()) { + while (!parent->HasNoParentWithSameLayersId()) { parent = parent->GetParent(); // While walking up to find the root of the subtree, if we encounter the // handoff parent, we don't actually need to do the search so we can @@ -1461,7 +1462,11 @@ APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode, node); HitTestResult hitResult = node->HitTest(aHitTestPoint); if (hitResult != HitTestResult::HitNothing) { - result = node->GetNearestContainingApzc(); + result = node->GetNearestContainingApzcWithSameLayersId(); + if (!result) { + result = FindRootApzcForLayersId(node->GetLayersId()); + MOZ_ASSERT(result); + } APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n", result, node, hitResult); MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion); @@ -1478,6 +1483,35 @@ APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode, return nullptr; } +AsyncPanZoomController* +APZCTreeManager::FindRootApzcForLayersId(uint64_t aLayersId) const +{ + mTreeLock.AssertCurrentThreadOwns(); + + if (!mRootNode) { + return nullptr; + } + std::deque queue; + queue.push_back(mRootNode); + while (!queue.empty()) { + const HitTestingTreeNode* node = queue.front(); + queue.pop_front(); + + AsyncPanZoomController* apzc = node->GetApzc(); + if (apzc && apzc->GetLayersId() == aLayersId && apzc->IsRootForLayersId()) { + return apzc; + } + + for (HitTestingTreeNode* child = node->GetLastChild(); + child; + child = child->GetPrevSibling()) { + queue.push_back(child); + } + } + + return nullptr; +} + /* The methods GetScreenToApzcTransform() and GetApzcToGeckoTransform() return some useful transformations that input events may need applied. This is best illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L @@ -1704,7 +1738,7 @@ APZCTreeManager::RootAPZCForLayersId(AsyncPanZoomController* aApzc) const { MonitorAutoLock lock(mTreeLock); nsRefPtr apzc = aApzc; - while (apzc && !apzc->IsRootForLayersId()) { + while (apzc && !apzc->HasNoParentWithSameLayersId()) { apzc = apzc->GetParent(); } return apzc.forget(); diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h index 61801b1d49cf..cd4707a23d93 100644 --- a/gfx/layers/apz/src/APZCTreeManager.h +++ b/gfx/layers/apz/src/APZCTreeManager.h @@ -412,6 +412,7 @@ private: AsyncPanZoomController* GetAPZCAtPoint(HitTestingTreeNode* aNode, const ParentLayerPoint& aHitTestPoint, HitTestResult* aOutHitResult); + AsyncPanZoomController* FindRootApzcForLayersId(uint64_t aLayersId) const; already_AddRefed GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const; already_AddRefed CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const; already_AddRefed RootAPZCForLayersId(AsyncPanZoomController* aApzc) const; @@ -433,7 +434,8 @@ private: void FlushRepaintsRecursively(HitTestingTreeNode* aNode); already_AddRefed RecycleOrCreateNode(TreeBuildingState& aState, - AsyncPanZoomController* aApzc); + AsyncPanZoomController* aApzc, + uint64_t aLayersId); HitTestingTreeNode* PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, const FrameMetrics& aMetrics, uint64_t aLayersId, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index df9ffbf0d869..d7d9d5852caa 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -2900,6 +2900,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount()); mFrameMetrics.SetPageScrollAmount(aLayerMetrics.GetPageScrollAmount()); mFrameMetrics.SetClipRect(aLayerMetrics.GetClipRect()); + mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot()); if (scrollOffsetUpdated) { APZC_LOG("%p updating scroll offset from %s to %s\n", this, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 19679b43b8ee..01ebb1a168f9 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -899,12 +899,18 @@ public: } /* Returns true if there is no APZC higher in the tree with the same - * layers id. + * layers id. Deprecated. New code shouldn't use this. Old code should be + * updated to not use this. */ - bool IsRootForLayersId() const { + bool HasNoParentWithSameLayersId() const { return !mParent || (mParent->mLayersId != mLayersId); } + bool IsRootForLayersId() const { + ReentrantMonitorAutoEnter lock(mMonitor); + return mFrameMetrics.IsLayersIdRoot(); + } + private: // This is a raw pointer to avoid introducing a reference cycle between // AsyncPanZoomController and APZCTreeManager. Since these objects don't @@ -1087,6 +1093,11 @@ public: return mAsyncTransformAppliedToContent; } + uint64_t GetLayersId() const + { + return mLayersId; + } + private: // Extra offset to add in SampleContentTransformForFrame for testing CSSPoint mTestAsyncScrollOffset; diff --git a/gfx/layers/apz/src/HitTestingTreeNode.cpp b/gfx/layers/apz/src/HitTestingTreeNode.cpp index 47ad0c3ab44f..9a8693d49fd8 100644 --- a/gfx/layers/apz/src/HitTestingTreeNode.cpp +++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp @@ -18,22 +18,28 @@ namespace mozilla { namespace layers { HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc, - bool aIsPrimaryHolder) + bool aIsPrimaryHolder, + uint64_t aLayersId) : mApzc(aApzc) , mIsPrimaryApzcHolder(aIsPrimaryHolder) + , mLayersId(aLayersId) , mOverride(EventRegionsOverride::NoOverride) { if (mIsPrimaryApzcHolder) { MOZ_ASSERT(mApzc); } + MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId); } void -HitTestingTreeNode::RecycleWith(AsyncPanZoomController* aApzc) +HitTestingTreeNode::RecycleWith(AsyncPanZoomController* aApzc, + uint64_t aLayersId) { MOZ_ASSERT(!mIsPrimaryApzcHolder); Destroy(); // clear out tree pointers mApzc = aApzc; + mLayersId = aLayersId; + MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId); // The caller is expected to call SetHitTestData to repopulate the hit-test // fields. } @@ -57,6 +63,8 @@ HitTestingTreeNode::Destroy() } mApzc = nullptr; } + + mLayersId = 0; } void @@ -147,12 +155,31 @@ HitTestingTreeNode::GetNearestContainingApzc() const return nullptr; } +AsyncPanZoomController* +HitTestingTreeNode::GetNearestContainingApzcWithSameLayersId() const +{ + for (const HitTestingTreeNode* n = this; + n && n->mLayersId == mLayersId; + n = n->GetParent()) { + if (n->GetApzc()) { + return n->GetApzc(); + } + } + return nullptr; +} + bool HitTestingTreeNode::IsPrimaryHolder() const { return mIsPrimaryApzcHolder; } +uint64_t +HitTestingTreeNode::GetLayersId() const +{ + return mLayersId; +} + void HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions, const gfx::Matrix4x4& aTransform, @@ -229,7 +256,8 @@ HitTestingTreeNode::Dump(const char* aPrefix) const mPrevSibling->Dump(aPrefix); } printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %s%sr=(%s) t=(%s) c=(%s)\n", - aPrefix, this, mApzc.get(), mApzc ? Stringify(mApzc->GetGuid()).c_str() : "", + aPrefix, this, mApzc.get(), + mApzc ? Stringify(mApzc->GetGuid()).c_str() : nsPrintfCString("l=%" PRIu64, mLayersId).get(), (mOverride & EventRegionsOverride::ForceDispatchToContent) ? "fdtc " : "", (mOverride & EventRegionsOverride::ForceEmptyHitRegion) ? "fehr " : "", Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(), diff --git a/gfx/layers/apz/src/HitTestingTreeNode.h b/gfx/layers/apz/src/HitTestingTreeNode.h index d5a157c9de61..08f1195681b5 100644 --- a/gfx/layers/apz/src/HitTestingTreeNode.h +++ b/gfx/layers/apz/src/HitTestingTreeNode.h @@ -53,8 +53,9 @@ class HitTestingTreeNode { private: ~HitTestingTreeNode(); public: - HitTestingTreeNode(AsyncPanZoomController* aApzc, bool aIsPrimaryHolder); - void RecycleWith(AsyncPanZoomController* aApzc); + HitTestingTreeNode(AsyncPanZoomController* aApzc, bool aIsPrimaryHolder, + uint64_t aLayersId); + void RecycleWith(AsyncPanZoomController* aApzc, uint64_t aLayersId); void Destroy(); /* Tree construction methods */ @@ -75,7 +76,9 @@ public: AsyncPanZoomController* GetApzc() const; AsyncPanZoomController* GetNearestContainingApzc() const; + AsyncPanZoomController* GetNearestContainingApzcWithSameLayersId() const; bool IsPrimaryHolder() const; + uint64_t GetLayersId() const; /* Hit test related methods */ @@ -106,6 +109,8 @@ private: nsRefPtr mApzc; bool mIsPrimaryApzcHolder; + uint64_t mLayersId; + /* Let {L,M} be the {layer, scrollable metrics} pair that this node * corresponds to in the layer tree. mEventRegions contains the event regions * from L, in the case where event-regions are enabled. If event-regions are diff --git a/gfx/layers/apz/test/apz_test_utils.js b/gfx/layers/apz/test/apz_test_utils.js index a4ae85ce5a8e..834afd8970e5 100644 --- a/gfx/layers/apz/test/apz_test_utils.js +++ b/gfx/layers/apz/test/apz_test_utils.js @@ -91,7 +91,7 @@ function buildApzcTree(paint) { // This 'root' does not correspond to an APZC. var root = makeNode(-1); for (var scrollId in paint) { - if ("isRootForLayersId" in paint[scrollId]) { + if ("hasNoParentWithSameLayersId" in paint[scrollId]) { addRoot(root, scrollId); } else if ("parentScrollId" in paint[scrollId]) { addLink(root, scrollId, paint[scrollId]["parentScrollId"]); diff --git a/gfx/tests/gtest/TestAsyncPanZoomController.cpp b/gfx/tests/gtest/TestAsyncPanZoomController.cpp index 7746fb22abf6..d53764a78dd8 100644 --- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp +++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp @@ -1894,6 +1894,10 @@ protected: CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1)) { FrameMetrics metrics; metrics.SetScrollId(aScrollId); + // By convention in this test file, START_SCROLL_ID is the root, so mark it as such. + if (aScrollId == FrameMetrics::START_SCROLL_ID) { + metrics.SetIsLayersIdRoot(true); + } IntRect layerBound = aLayer->GetVisibleRegion().GetBounds(); metrics.SetCompositionBounds(ParentLayerRect(layerBound.x, layerBound.y, layerBound.width, layerBound.height)); @@ -2864,9 +2868,10 @@ protected: void CreateBug1119497LayerTree() { const char* layerTreeSyntax = "c(tt)"; // LayerID 0 12 - // 0 is the root and doesn't have an APZC - // 1 is behind 2 and does have an APZC - // 2 entirely covers 1 and should take all the input events + // 0 is the root and has an APZC + // 1 is behind 2 and has an APZC + // 2 entirely covers 1 and should take all the input events, but has no APZC + // so hits to 2 should go to to the root APZC nsIntRegion layerVisibleRegions[] = { nsIntRegion(IntRect(0, 0, 100, 100)), nsIntRegion(IntRect(0, 0, 100, 100)), @@ -2874,6 +2879,7 @@ protected: }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers); + SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID); SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1); registration = MakeUnique(0, root, mcc); @@ -3006,8 +3012,8 @@ TEST_F(APZEventRegionsTester, Bug1119497) { HitTestResult result; nsRefPtr hit = manager->GetTargetAPZC(ScreenPoint(50, 50), &result); // We should hit layers[2], so |result| will be HitLayer but there's no - // actual APZC in that parent chain, so |hit| should be nullptr. - EXPECT_EQ(nullptr, hit.get()); + // actual APZC on layers[2], so it will be the APZC of the root layer. + EXPECT_EQ(ApzcOf(layers[0]), hit.get()); EXPECT_EQ(HitTestResult::HitLayer, result); } diff --git a/gfx/tests/gtest/moz.build b/gfx/tests/gtest/moz.build index f84118748afb..9295bb4b4820 100644 --- a/gfx/tests/gtest/moz.build +++ b/gfx/tests/gtest/moz.build @@ -32,6 +32,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows': UNIFIED_SOURCES += [ '/gfx/2d/unittest/%s' % p for p in [ 'TestBase.cpp', 'TestBugs.cpp', + 'TestCairo.cpp', 'TestPoint.cpp', 'TestScaling.cpp', ]] diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 64e44ec528de..d714266ea9ec 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2249,7 +2249,7 @@ gfxFont::Measure(gfxTextRun *aTextRun, const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); NS_ASSERTION(details != nullptr, - "detaiedGlyph record should not be missing!"); + "detailedGlyph record should not be missing!"); uint32_t j; for (j = 0; j < glyphCount; ++j, ++details) { uint32_t glyphIndex = details->mGlyphID; diff --git a/gfx/thebes/gfxGDIFont.cpp b/gfx/thebes/gfxGDIFont.cpp index 6ba3b592f6ba..2c33860469c9 100644 --- a/gfx/thebes/gfxGDIFont.cpp +++ b/gfx/thebes/gfxGDIFont.cpp @@ -337,26 +337,27 @@ gfxGDIFont::Initialize() } } - // Cache the width of a single space. - SIZE size; - GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size); - mMetrics->spaceWidth = ROUND(size.cx); - - // Cache the width of digit zero. - // XXX MSDN (http://msdn.microsoft.com/en-us/library/ms534223.aspx) - // does not say what the failure modes for GetTextExtentPoint32 are - - // is it safe to assume it will fail iff the font has no '0'? - if (GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size)) { - mMetrics->zeroOrAveCharWidth = ROUND(size.cx); - } else { - mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth; - } - WORD glyph; + SIZE size; DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS); if (ret != GDI_ERROR && glyph != 0xFFFF) { mSpaceGlyph = glyph; + // Cache the width of a single space. + GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size); + mMetrics->spaceWidth = ROUND(size.cx); + } else { + mMetrics->spaceWidth = mMetrics->aveCharWidth; + } + + // Cache the width of digit zero, if available. + ret = GetGlyphIndicesW(dc.GetDC(), L"0", 1, &glyph, + GGI_MARK_NONEXISTING_GLYPHS); + if (ret != GDI_ERROR && glyph != 0xFFFF) { + GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size); + mMetrics->zeroOrAveCharWidth = ROUND(size.cx); + } else { + mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth; } SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont); diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index 1790a405af83..453860041e8a 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -1791,10 +1791,18 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext, charGlyphs[baseCharIndex].SetSimpleGlyph(advance, ginfo[glyphStart].codepoint); } else { - // collect all glyphs in a list to be assigned to the first char; + // Collect all glyphs in a list to be assigned to the first char; // there must be at least one in the clump, and we already measured // its advance, hence the placement of the loop-exit test and the - // measurement of the next glyph + // measurement of the next glyph. + // For vertical orientation, we add a "base offset" to compensate + // for the positioning within the cluster being based on horizontal + // glyph origin/offset. + hb_position_t baseIOffset, baseBOffset; + if (aVertical) { + baseIOffset = 2 * (i_offset - i_advance); + baseBOffset = GetGlyphHAdvance(ginfo[glyphStart].codepoint); + } while (1) { gfxTextRun::DetailedGlyph* details = detailedGlyphs.AppendElement(); @@ -1817,9 +1825,9 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext, } if (aVertical) { - i_offset = posInfo[glyphStart].y_offset; + i_offset = baseIOffset - posInfo[glyphStart].y_offset; i_advance = posInfo[glyphStart].y_advance; - b_offset = posInfo[glyphStart].x_offset; + b_offset = baseBOffset - posInfo[glyphStart].x_offset; b_advance = posInfo[glyphStart].x_advance; } else { i_offset = posInfo[glyphStart].x_offset; diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 571888de5260..3482048d9098 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -336,7 +336,8 @@ struct JS_PUBLIC_API(GCDescription) { GCDescription(bool isCompartment, JSGCInvocationKind kind) : isCompartment_(isCompartment), invocationKind_(kind) {} - char16_t* formatMessage(JSRuntime* rt) const; + char16_t* formatSliceMessage(JSRuntime* rt) const; + char16_t* formatSummaryMessage(JSRuntime* rt) const; char16_t* formatJSON(JSRuntime* rt, uint64_t timestamp) const; JS::dbg::GarbageCollectionEvent::Ptr toGCEvent(JSRuntime* rt) const; diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index acf48f8e9ab2..85d29e2460a8 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -76,7 +76,7 @@ * - MutableHandle is a non-const reference to Rooted. It is used in the * same way as Handle and includes a |set(const T& v)| method to allow * updating the value of the referenced Rooted. A MutableHandle can be - * created from a Rooted by using |Rooted::operator&()|. + * created with an implicit cast from a Rooted*. * * In some cases the small performance overhead of exact rooting (measured to * be a few nanoseconds on desktop) is too much. In these cases, try the @@ -1185,7 +1185,5 @@ CallTraceCallbackOnNonHeap(T* v, const TraceCallbacks& aCallbacks, const char* a } /* namespace js */ #undef DELETE_ASSIGNMENT_OPS -#undef DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS -#undef DECLARE_NONPOINTER_ACCESSOR_METHODS #endif /* js_RootingAPI_h */ diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index f00e71c8b650..539454ab3345 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -33,210 +33,6 @@ using mozilla::PodZero; /* Except for the first and last, slices of less than 10ms are not reported. */ static const int64_t SLICE_MIN_REPORT_TIME = 10 * PRMJ_USEC_PER_MSEC; -class gcstats::StatisticsSerializer -{ - typedef Vector CharBuffer; - CharBuffer buf_; - bool asJSON_; - bool needComma_; - bool oom_; - - static const int MaxFieldValueLength = 128; - - public: - enum Mode { - AsJSON = true, - AsText = false - }; - - explicit StatisticsSerializer(Mode asJSON) - : buf_(), asJSON_(asJSON), needComma_(false), oom_(false) - {} - - bool isJSON() { return asJSON_; } - - bool isOOM() { return oom_; } - - void endLine() { - if (!asJSON_) { - p("\n"); - needComma_ = false; - } - } - - void extra(const char* str) { - if (!asJSON_) { - needComma_ = false; - p(str); - } - } - - void appendString(const char* name, const char* value) { - put(name, value, "", true); - } - - void appendNumber(const char* name, const char* vfmt, const char* units, ...) { - va_list va; - va_start(va, units); - append(name, vfmt, va, units); - va_end(va); - } - - void appendDecimal(const char* name, const char* units, double d) { - if (d < 0) - d = 0; - if (asJSON_) - appendNumber(name, "%d.%03d", units, (int)d, (int)(d * 1000.) % 1000); - else - appendNumber(name, "%.1f", units, d); - } - - void appendIfNonzeroMS(const char* name, double v) { - if (asJSON_ || v >= 0.1) - appendDecimal(name, "ms", v); - } - - void beginObject(const char* name) { - if (needComma_) - pJSON(", "); - if (asJSON_ && name) { - putKey(name); - pJSON(": "); - } - pJSON("{"); - needComma_ = false; - } - - void endObject() { - needComma_ = false; - pJSON("}"); - needComma_ = true; - } - - void beginArray(const char* name) { - if (needComma_) - pJSON(", "); - if (asJSON_) - putKey(name); - pJSON(": ["); - needComma_ = false; - } - - void endArray() { - needComma_ = false; - pJSON("]"); - needComma_ = true; - } - - char16_t* finishJSString() { - char* buf = finishCString(); - if (!buf) - return nullptr; - - size_t nchars = strlen(buf); - char16_t* out = js_pod_malloc(nchars + 1); - if (!out) { - oom_ = true; - js_free(buf); - return nullptr; - } - - CopyAndInflateChars(out, buf, nchars); - js_free(buf); - - out[nchars] = 0; - return out; - } - - char* finishCString() { - if (oom_) - return nullptr; - - buf_.append('\0'); - - char* buf = buf_.extractRawBuffer(); - if (!buf) - oom_ = true; - - return buf; - } - - private: - void append(const char* name, const char* vfmt, - va_list va, const char* units) - { - char val[MaxFieldValueLength]; - JS_vsnprintf(val, MaxFieldValueLength, vfmt, va); - put(name, val, units, false); - } - - void p(const char* cstr) { - if (oom_) - return; - - if (!buf_.append(cstr, strlen(cstr))) - oom_ = true; - } - - void p(const char c) { - if (oom_) - return; - - if (!buf_.append(c)) - oom_ = true; - } - - void pJSON(const char* str) { - if (asJSON_) - p(str); - } - - void put(const char* name, const char* val, const char* units, bool valueIsQuoted) { - if (needComma_) - p(", "); - needComma_ = true; - - putKey(name); - p(": "); - if (valueIsQuoted) - putQuoted(val); - else - p(val); - if (!asJSON_) - p(units); - } - - void putQuoted(const char* str) { - pJSON("\""); - p(str); - pJSON("\""); - } - - void putKey(const char* str) { - if (!asJSON_) { - p(str); - return; - } - - p("\""); - const char* c = str; - while (*c) { - if (*c == ' ' || *c == '\t') - p('_'); - else if (isupper(*c)) - p(tolower(*c)); - else if (*c == '+') - p("added_"); - else if (*c == '-') - p("removed_"); - else if (*c != '(' && *c != ')') - p(*c); - c++; - } - p("\""); - } -}; - /* * If this fails, then you can either delete this assertion and allow all * larger-numbered reasons to pile up in the last telemetry bucket, or switch @@ -443,25 +239,11 @@ struct AllPhaseIterator { } }; -static void -FormatPhaseTimes(StatisticsSerializer& ss, const char* name, Statistics::PhaseTimeTable times) -{ - ss.beginObject(name); - - for (AllPhaseIterator iter(times); !iter.done(); iter.advance()) { - Phase phase; - size_t dagSlot; - iter.get(&phase, &dagSlot); - ss.appendIfNonzeroMS(phases[phase].name, t(times[dagSlot][phase])); - } - ss.endObject(); -} - void -Statistics::gcDuration(int64_t* total, int64_t* maxPause) +Statistics::gcDuration(int64_t* total, int64_t* maxPause) const { *total = *maxPause = 0; - for (SliceData* slice = slices.begin(); slice != slices.end(); slice++) { + for (const SliceData* slice = slices.begin(); slice != slices.end(); slice++) { *total += slice->duration(); if (slice->duration() > *maxPause) *maxPause = slice->duration(); @@ -480,90 +262,6 @@ Statistics::sccDurations(int64_t* total, int64_t* maxPause) } } -bool -Statistics::formatData(StatisticsSerializer& ss, uint64_t timestamp) -{ - MOZ_ASSERT(!aborted); - - int64_t total, longest; - gcDuration(&total, &longest); - - int64_t sccTotal, sccLongest; - sccDurations(&sccTotal, &sccLongest); - - double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC); - double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); - - ss.beginObject(nullptr); - if (ss.isJSON()) - ss.appendNumber("Timestamp", "%llu", "", (unsigned long long)timestamp); - if (slices.length() > 1 || ss.isJSON()) - ss.appendDecimal("Max Pause", "ms", t(longest)); - else - ss.appendString("Reason", ExplainReason(slices[0].reason)); - ss.appendDecimal("Total Time", "ms", t(total)); - ss.appendNumber("Zones Collected", "%d", "", zoneStats.collectedZoneCount); - ss.appendNumber("Total Zones", "%d", "", zoneStats.zoneCount); - ss.appendNumber("Total Compartments", "%d", "", zoneStats.compartmentCount); - ss.appendNumber("Minor GCs", "%d", "", counts[STAT_MINOR_GC]); - ss.appendNumber("Store Buffer Overflows", "%d", "", counts[STAT_STOREBUFFER_OVERFLOW]); - ss.appendNumber("MMU (20ms)", "%d", "%", int(mmu20 * 100)); - ss.appendNumber("MMU (50ms)", "%d", "%", int(mmu50 * 100)); - ss.appendDecimal("SCC Sweep Total", "ms", t(sccTotal)); - ss.appendDecimal("SCC Sweep Max Pause", "ms", t(sccLongest)); - if (nonincrementalReason_ || ss.isJSON()) { - ss.appendString("Nonincremental Reason", - nonincrementalReason_ ? nonincrementalReason_ : "none"); - } - ss.appendNumber("Allocated", "%u", "MB", unsigned(preBytes / 1024 / 1024)); - ss.appendNumber("+Chunks", "%d", "", counts[STAT_NEW_CHUNK]); - ss.appendNumber("-Chunks", "%d", "", counts[STAT_DESTROY_CHUNK]); - ss.endLine(); - - if (slices.length() > 1 || ss.isJSON()) { - ss.beginArray("Slices"); - for (size_t i = 0; i < slices.length(); i++) { - int64_t width = slices[i].duration(); - if (i != 0 && i != slices.length() - 1 && width < SLICE_MIN_REPORT_TIME && - !slices[i].resetReason && !ss.isJSON()) - { - continue; - } - - char budgetDescription[200]; - slices[i].budget.describe(budgetDescription, sizeof(budgetDescription) - 1); - - ss.beginObject(nullptr); - ss.extra(" "); - ss.appendNumber("Slice", "%d", "", i); - ss.appendDecimal("Pause", "", t(width)); - ss.extra(" ("); - ss.appendDecimal("When", "ms", t(slices[i].start - slices[0].start)); - ss.appendString("Reason", ExplainReason(slices[i].reason)); - ss.appendString("Budget", budgetDescription); - if (ss.isJSON()) { - ss.appendDecimal("Page Faults", "", - double(slices[i].endFaults - slices[i].startFaults)); - - ss.appendNumber("Start Timestamp", "%llu", "", (unsigned long long)slices[i].start); - ss.appendNumber("End Timestamp", "%llu", "", (unsigned long long)slices[i].end); - } - if (slices[i].resetReason) - ss.appendString("Reset", slices[i].resetReason); - ss.extra("): "); - FormatPhaseTimes(ss, "Times", slices[i].phaseTimes); - ss.endLine(); - ss.endObject(); - } - ss.endArray(); - } - ss.extra(" Totals: "); - FormatPhaseTimes(ss, "Totals", phaseTimes); - ss.endObject(); - - return !ss.isOOM(); -} - typedef Vector FragmentVector; static UniqueChars @@ -617,6 +315,119 @@ SumChildTimes(size_t phaseSlot, Phase phase, Statistics::PhaseTimeTable phaseTim return total; } +UniqueChars +Statistics::formatCompactSliceMessage() const +{ + // Skip if we OOM'ed. + if (slices.length() == 0) + return UniqueChars(nullptr); + + const size_t index = slices.length() - 1; + const SliceData& slice = slices[index]; + + char budgetDescription[200]; + slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1); + + const char* format = + "GC Slice %u - Pause: %.3fms of %s budget (@ %.3fms); Reason: %s; Reset: %s%s; Times: "; + char buffer[1024]; + memset(buffer, 0, sizeof(buffer)); + JS_snprintf(buffer, sizeof(buffer), format, index, + t(slice.duration()), budgetDescription, t(slice.start - slices[0].start), + ExplainReason(slice.reason), + slice.resetReason ? "yes - " : "no", slice.resetReason ? slice.resetReason : ""); + + FragmentVector fragments; + if (!fragments.append(make_string_copy(buffer)) || + !fragments.append(formatCompactSlicePhaseTimes(slices[index].phaseTimes))) + { + return UniqueChars(nullptr); + } + return Join(fragments); +} + +UniqueChars +Statistics::formatCompactSummaryMessage() const +{ + const double bytesPerMiB = 1024 * 1024; + + FragmentVector fragments; + if (!fragments.append(make_string_copy("Summary - "))) + return UniqueChars(nullptr); + + int64_t total, longest; + gcDuration(&total, &longest); + + const double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC); + const double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); + + char buffer[1024]; + if (!nonincrementalReason_) { + JS_snprintf(buffer, sizeof(buffer), + "Max Pause: %.3fms; MMU 20ms: %.1f%%; MMU 50ms: %.1f%%; Total: %.3fms; ", + t(longest), mmu20 * 100., mmu50 * 100., t(total)); + } else { + JS_snprintf(buffer, sizeof(buffer), "Non-Incremental: %.3fms; ", t(total)); + } + if (!fragments.append(make_string_copy(buffer))) + return UniqueChars(nullptr); + + JS_snprintf(buffer, sizeof(buffer), + "Zones: %d of %d; Compartments: %d of %d; HeapSize: %.3f MiB; "\ + "HeapChange (abs): %+d (%d); ", + zoneStats.collectedZoneCount, zoneStats.zoneCount, + zoneStats.collectedCompartmentCount, zoneStats.compartmentCount, + double(preBytes) / bytesPerMiB, + counts[STAT_NEW_CHUNK] - counts[STAT_DESTROY_CHUNK], + counts[STAT_NEW_CHUNK] + counts[STAT_DESTROY_CHUNK]); + if (!fragments.append(make_string_copy(buffer))) + return UniqueChars(nullptr); + + MOZ_ASSERT_IF(counts[STAT_ARENA_RELOCATED], gckind == GC_SHRINK); + if (gckind == GC_SHRINK) { + JS_snprintf(buffer, sizeof(buffer), + "Kind: %s; Relocated: %.3f MiB; ", + ExplainInvocationKind(gckind), + double(ArenaSize * counts[STAT_ARENA_RELOCATED]) / bytesPerMiB); + if (!fragments.append(make_string_copy(buffer))) + return UniqueChars(nullptr); + } + + return Join(fragments); +} + +UniqueChars +Statistics::formatCompactSlicePhaseTimes(PhaseTimeTable phaseTimes) const +{ + static const int64_t MaxUnaccountedTimeUS = 100; + + FragmentVector fragments; + char buffer[128]; + for (AllPhaseIterator iter(phaseTimes); !iter.done(); iter.advance()) { + Phase phase; + size_t dagSlot; + size_t level; + iter.get(&phase, &dagSlot, &level); + MOZ_ASSERT(level < 4); + + int64_t ownTime = phaseTimes[dagSlot][phase]; + int64_t childTime = SumChildTimes(dagSlot, phase, phaseTimes); + if (ownTime > MaxUnaccountedTimeUS) { + JS_snprintf(buffer, sizeof(buffer), "%s: %.3fms", phases[phase].name, t(ownTime)); + if (!fragments.append(make_string_copy(buffer))) + return UniqueChars(nullptr); + + if (childTime && (ownTime - childTime) > MaxUnaccountedTimeUS) { + MOZ_ASSERT(level < 3); + JS_snprintf(buffer, sizeof(buffer), "%s: %.3fms", "Other", t(ownTime - childTime)); + if (!fragments.append(make_string_copy(buffer))) + return UniqueChars(nullptr); + } + } + } + return Join(fragments, ", "); +} + UniqueChars Statistics::formatDetailedMessage() { @@ -919,14 +730,6 @@ Statistics::formatJsonPhaseTimes(PhaseTimeTable phaseTimes) return Join(fragments, ","); } -char16_t* -Statistics::formatMessage() -{ - StatisticsSerializer ss(StatisticsSerializer::AsText); - formatData(ss, 0); - return ss.finishJSString(); -} - Statistics::Statistics(JSRuntime* rt) : runtime(rt), startupTime(PRMJ_Now()), @@ -1012,18 +815,8 @@ Statistics::Statistics(JSRuntime* rt) Statistics::~Statistics() { - if (fp) { - StatisticsSerializer ss(StatisticsSerializer::AsText); - FormatPhaseTimes(ss, "", phaseTotals); - char* msg = ss.finishCString(); - if (msg) { - fprintf(fp, "TOTALS\n%s\n\n-------\n", msg); - js_free(msg); - } - - if (fp != stdout && fp != stderr) - fclose(fp); - } + if (fp && fp != stdout && fp != stderr) + fclose(fp); } JS::GCSliceCallback @@ -1314,7 +1107,7 @@ Statistics::endSCC(unsigned scc, int64_t start) * as long as the total time it spends is at most 10ms. */ double -Statistics::computeMMU(int64_t window) +Statistics::computeMMU(int64_t window) const { MOZ_ASSERT(!slices.empty()); diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 8357ab244151..c97d3bf64559 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -101,8 +101,6 @@ enum Stat { STAT_LIMIT }; -class StatisticsSerializer; - struct ZoneGCStats { /* Number of zones collected in this GC. */ @@ -186,7 +184,8 @@ struct Statistics int64_t beginSCC(); void endSCC(unsigned scc, int64_t start); - char16_t* formatMessage(); + UniqueChars formatCompactSliceMessage() const; + UniqueChars formatCompactSummaryMessage() const; UniqueChars formatJsonMessage(uint64_t timestamp); UniqueChars formatDetailedMessage(); @@ -233,7 +232,7 @@ struct Statistics size_t slicesLength() const { return slices.length(); } /* Create a convenient typedef for referring tables of phase times. */ - typedef int64_t (*PhaseTimeTable)[PHASE_LIMIT]; + typedef int64_t const (*PhaseTimeTable)[PHASE_LIMIT]; private: JSRuntime* runtime; @@ -277,7 +276,7 @@ struct Statistics size_t preBytes; /* Records the maximum GC pause in an API-controlled interval (in us). */ - int64_t maxPauseInInterval; + mutable int64_t maxPauseInInterval; /* Phases that are currently on stack. */ Phase phaseNesting[MAX_NESTING]; @@ -309,10 +308,11 @@ struct Statistics void recordPhaseEnd(Phase phase); - void gcDuration(int64_t* total, int64_t* maxPause); + void gcDuration(int64_t* total, int64_t* maxPause) const; void sccDurations(int64_t* total, int64_t* maxPause); void printStats(); - bool formatData(StatisticsSerializer& ss, uint64_t timestamp); + + UniqueChars formatCompactSlicePhaseTimes(PhaseTimeTable phaseTimes) const; UniqueChars formatDetailedDescription(); UniqueChars formatDetailedSliceDescription(unsigned i, const SliceData& slice); @@ -323,7 +323,7 @@ struct Statistics UniqueChars formatJsonSliceDescription(unsigned i, const SliceData& slice); UniqueChars formatJsonPhaseTimes(PhaseTimeTable phaseTimes); - double computeMMU(int64_t resolution); + double computeMMU(int64_t resolution) const; }; struct AutoGCSlice diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index a8d490e549cf..78cb94106cf0 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -3808,7 +3808,7 @@ IsOptimizableElementPropertyName(JSContext* cx, HandleValue key, MutableHandleId static bool TryAttachNativeGetValueElemStub(JSContext* cx, HandleScript script, jsbytecode* pc, ICGetElem_Fallback* stub, HandleNativeObject obj, - HandleValue key) + HandleValue key, bool* attached) { RootedId id(cx); if (!IsOptimizableElementPropertyName(cx, key, &id)) @@ -3858,6 +3858,7 @@ TryAttachNativeGetValueElemStub(JSContext* cx, HandleScript script, jsbytecode* return false; stub->addNewStub(newStub); + *attached = true; } return true; } @@ -3865,7 +3866,7 @@ TryAttachNativeGetValueElemStub(JSContext* cx, HandleScript script, jsbytecode* static bool TryAttachNativeGetAccessorElemStub(JSContext* cx, HandleScript script, jsbytecode* pc, ICGetElem_Fallback* stub, HandleNativeObject obj, - HandleValue key, bool* attached) + HandleValue key, bool* attached, bool* isTemporarilyUnoptimizable) { MOZ_ASSERT(!*attached); @@ -3881,15 +3882,15 @@ TryAttachNativeGetAccessorElemStub(JSContext* cx, HandleScript script, jsbytecod RootedObject baseHolder(cx); if (!EffectlesslyLookupProperty(cx, obj, propName, &baseHolder, &shape)) return false; - if(!baseHolder || baseHolder->isNative()) + if (!baseHolder || baseHolder->isNative()) return true; HandleNativeObject holder = baseHolder.as(); bool getterIsScripted = false; - bool isTemporarilyUnoptimizable = false; if (IsCacheableGetPropCall(cx, obj, baseHolder, shape, &getterIsScripted, - &isTemporarilyUnoptimizable, /*isDOMProxy=*/false)) { + isTemporarilyUnoptimizable, /*isDOMProxy=*/false)) + { RootedFunction getter(cx, &shape->getterObject()->as()); #if JS_HAS_NO_SUCH_METHOD @@ -4003,7 +4004,7 @@ IsNativeOrUnboxedDenseElementAccess(HandleObject obj, HandleValue key) static bool TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_Fallback* stub, - HandleValue lhs, HandleValue rhs, HandleValue res) + HandleValue lhs, HandleValue rhs, HandleValue res, bool* attached) { bool isCallElem = (JSOp(*pc) == JSOP_CALLELEM); @@ -4020,6 +4021,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ return false; stub->addNewStub(stringStub); + *attached = true; return true; } @@ -4038,6 +4040,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ return false; stub->addNewStub(argsStub); + *attached = true; return true; } @@ -4060,6 +4063,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ return false; stub->addNewStub(argsStub); + *attached = true; return true; } } @@ -4074,6 +4078,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ return false; stub->addNewStub(denseStub); + *attached = true; return true; } @@ -4081,10 +4086,12 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ if (obj->isNative() && rhs.isString()) { RootedScript rootedScript(cx, script); if (!TryAttachNativeGetValueElemStub(cx, rootedScript, pc, stub, - obj.as(), rhs)) + obj.as(), rhs, attached)) { return false; } + if (*attached) + return true; script = rootedScript; } @@ -4098,6 +4105,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ return false; stub->addNewStub(unboxedStub); + *attached = true; return true; } @@ -4131,6 +4139,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ return false; stub->addNewStub(typedArrayStub); + *attached = true; return true; } @@ -4179,15 +4188,20 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_ if (stub->numOptimizedStubs() >= ICGetElem_Fallback::MAX_OPTIMIZED_STUBS) { // TODO: Discard all stubs in this IC and replace with inert megamorphic stub. // But for now we just bail. + stub->noteUnoptimizableAccess(); attached = true; } // Try to attach an optimized getter stub. + bool isTemporarilyUnoptimizable = false; if (!attached && lhs.isObject() && lhs.toObject().isNative() && rhs.isString()){ RootedScript rootedScript(cx, frame->script()); RootedNativeObject obj(cx, &lhs.toObject().as()); - if (!TryAttachNativeGetAccessorElemStub(cx, rootedScript, pc, stub, obj, rhs, &attached)) + if (!TryAttachNativeGetAccessorElemStub(cx, rootedScript, pc, stub, obj, rhs, &attached, + &isTemporarilyUnoptimizable)) + { return false; + } script = rootedScript; } @@ -4209,11 +4223,11 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_ return true; // Try to attach an optimized stub. - if (!TryAttachGetElemStub(cx, frame->script(), pc, stub, lhs, rhs, res)) + if (!TryAttachGetElemStub(cx, frame->script(), pc, stub, lhs, rhs, res, &attached)) return false; - // If we ever add a way to note unoptimizable accesses here, propagate the - // isTemporarilyUnoptimizable state from TryAttachNativeGetElemStub to here. + if (!attached && !isTemporarilyUnoptimizable) + stub->noteUnoptimizableAccess(); return true; } @@ -9948,7 +9962,7 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc, return true; } - if (native == js::array_concat) { + if (native == js::array_concat || native == js::array_slice) { if (args.thisv().isObject() && !args.thisv().toObject().isSingleton()) { res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0, TenuredObject, /* forceAnalyze = */ true)); @@ -12682,11 +12696,15 @@ ICGetIntrinsic_Constant::~ICGetIntrinsic_Constant() { } ICGetProp_Primitive::ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub, - Shape* protoShape, uint32_t offset) + JSValueType primitiveType, Shape* protoShape, + uint32_t offset) : ICMonitoredStub(GetProp_Primitive, stubCode, firstMonitorStub), protoShape_(protoShape), offset_(offset) -{ } +{ + extra_ = uint16_t(primitiveType); + MOZ_ASSERT(JSValueType(extra_) == primitiveType); +} ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index b570c07de6bd..7894a44b5b82 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -2638,6 +2638,7 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub static const uint16_t EXTRA_NON_NATIVE = 0x1; static const uint16_t EXTRA_NEGATIVE_INDEX = 0x2; + static const uint16_t EXTRA_UNOPTIMIZABLE_ACCESS = 0x4; public: static const uint32_t MAX_OPTIMIZED_STUBS = 16; @@ -2655,6 +2656,12 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub bool hasNegativeIndex() const { return extra_ & EXTRA_NEGATIVE_INDEX; } + void noteUnoptimizableAccess() { + extra_ |= EXTRA_UNOPTIMIZABLE_ACCESS; + } + bool hadUnoptimizableAccess() const { + return extra_ & EXTRA_UNOPTIMIZABLE_ACCESS; + } // Compiler for this stub kind. class Compiler : public ICStubCompiler { @@ -4109,13 +4116,17 @@ class ICGetProp_Primitive : public ICMonitoredStub // Fixed or dynamic slot offset. uint32_t offset_; - ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub, + ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub, JSValueType primitiveType, Shape* protoShape, uint32_t offset); public: HeapPtrShape& protoShape() { return protoShape_; } + JSValueType primitiveType() const { + return JSValueType(extra_); + } + static size_t offsetOfProtoShape() { return offsetof(ICGetProp_Primitive, protoShape_); } @@ -4155,7 +4166,7 @@ class ICGetProp_Primitive : public ICMonitoredStub ICStub* getStub(ICStubSpace* space) { RootedShape protoShape(cx, prototype_->as().lastProperty()); return newStub(space, getStubCode(), firstMonitorStub_, - protoShape, offset_); + primitiveType_, protoShape, offset_); } }; }; diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index d743beac0e32..95b93faa165e 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -702,6 +702,80 @@ BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shap return true; } +MIRType +BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc) +{ + if (!hasBaselineScript()) + return MIRType_Value; + + const ICEntry& entry = icEntryFromPC(pc); + MIRType type = MIRType_None; + + for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { + MIRType stubType; + switch (stub->kind()) { + case ICStub::GetProp_Fallback: + if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) + return MIRType_Value; + continue; + + case ICStub::GetElem_Fallback: + if (stub->toGetElem_Fallback()->hadUnoptimizableAccess()) + return MIRType_Value; + continue; + + case ICStub::GetProp_Generic: + return MIRType_Value; + + case ICStub::GetProp_ArgumentsLength: + case ICStub::GetElem_Arguments: + // Either an object or magic arguments. + return MIRType_Value; + + case ICStub::GetProp_ArrayLength: + case ICStub::GetProp_Native: + case ICStub::GetProp_NativeDoesNotExist: + case ICStub::GetProp_NativePrototype: + case ICStub::GetProp_Unboxed: + case ICStub::GetProp_TypedObject: + case ICStub::GetProp_CallScripted: + case ICStub::GetProp_CallNative: + case ICStub::GetProp_CallDOMProxyNative: + case ICStub::GetProp_CallDOMProxyWithGenerationNative: + case ICStub::GetProp_DOMProxyShadowed: + case ICStub::GetElem_NativeSlot: + case ICStub::GetElem_NativePrototypeSlot: + case ICStub::GetElem_NativePrototypeCallNative: + case ICStub::GetElem_NativePrototypeCallScripted: + case ICStub::GetElem_String: + case ICStub::GetElem_Dense: + case ICStub::GetElem_TypedArray: + stubType = MIRType_Object; + break; + + case ICStub::GetProp_Primitive: + stubType = MIRTypeFromValueType(stub->toGetProp_Primitive()->primitiveType()); + break; + + case ICStub::GetProp_StringLength: + stubType = MIRType_String; + break; + + default: + MOZ_CRASH("Unexpected stub"); + } + + if (type != MIRType_None) { + if (type != stubType) + return MIRType_Value; + } else { + type = stubType; + } + } + + return (type == MIRType_None) ? MIRType_Value : type; +} + bool BaselineInspector::instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot, JSObject** prototypeObject) diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h index d0e0965201a0..10d61d849eac 100644 --- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -104,6 +104,7 @@ class BaselineInspector MIRType expectedResultType(jsbytecode* pc); MCompare::CompareType expectedCompareType(jsbytecode* pc); MIRType expectedBinaryArithSpecialization(jsbytecode* pc); + MIRType expectedPropertyAccessInputType(jsbytecode* pc); bool hasSeenNonNativeGetElement(jsbytecode* pc); bool hasSeenNegativeIndexGetElement(jsbytecode* pc); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index b425f2599555..ef24bf0db382 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -7148,6 +7148,41 @@ CodeGenerator::visitArrayConcat(LArrayConcat* lir) callVM(ArrayConcatDenseInfo, lir); } +typedef JSObject* (*ArraySliceDenseFn)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); +static const VMFunction ArraySliceDenseInfo = FunctionInfo(array_slice_dense); + +void +CodeGenerator::visitArraySlice(LArraySlice* lir) +{ + Register object = ToRegister(lir->object()); + Register begin = ToRegister(lir->begin()); + Register end = ToRegister(lir->end()); + Register temp1 = ToRegister(lir->temp1()); + Register temp2 = ToRegister(lir->temp2()); + + Label call, fail; + + // Try to allocate an object. + masm.createGCObject(temp1, temp2, lir->mir()->templateObj(), lir->mir()->initialHeap(), &fail); + + // Fixup the group of the result in case it doesn't match the template object. + masm.loadPtr(Address(object, JSObject::offsetOfGroup()), temp2); + masm.storePtr(temp2, Address(temp1, JSObject::offsetOfGroup())); + + masm.jump(&call); + { + masm.bind(&fail); + masm.movePtr(ImmPtr(nullptr), temp1); + } + masm.bind(&call); + + pushArg(temp1); + pushArg(end); + pushArg(begin); + pushArg(object); + callVM(ArraySliceDenseInfo, lir); +} + typedef JSString* (*ArrayJoinFn)(JSContext*, HandleObject, HandleString); static const VMFunction ArrayJoinInfo = FunctionInfo(jit::ArrayJoin); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index f26504c79685..189cc84e895b 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -265,6 +265,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitArrayPushV(LArrayPushV* lir); void visitArrayPushT(LArrayPushT* lir); void visitArrayConcat(LArrayConcat* lir); + void visitArraySlice(LArraySlice* lir); void visitArrayJoin(LArrayJoin* lir); void visitLoadUnboxedScalar(LLoadUnboxedScalar* lir); void visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole* lir); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 92729dd4561e..845165b7372e 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -7194,27 +7194,26 @@ IonBuilder::testSingletonProperty(JSObject* obj, PropertyName* name) return nullptr; } -bool -IonBuilder::testSingletonPropertyTypes(MDefinition* obj, JSObject* singleton, PropertyName* name, - bool* testObject, bool* testString) +JSObject* +IonBuilder::testSingletonPropertyTypes(MDefinition* obj, PropertyName* name) { // As for TestSingletonProperty, but the input is any value in a type set - // rather than a specific object. If testObject is set then the constant - // result can only be used after ensuring the input is an object. - - *testObject = false; - *testString = false; + // rather than a specific object. TemporaryTypeSet* types = obj->resultTypeSet(); if (types && types->unknownObject()) - return false; + return nullptr; JSObject* objectSingleton = types ? types->maybeSingleton() : nullptr; if (objectSingleton) - return testSingletonProperty(objectSingleton, name) == singleton; + return testSingletonProperty(objectSingleton, name); + + MIRType objType = obj->type(); + if (objType == MIRType_Value && types) + objType = types->getKnownMIRType(); JSProtoKey key; - switch (obj->type()) { + switch (objType) { case MIRType_String: key = JSProto_String; break; @@ -7232,23 +7231,14 @@ IonBuilder::testSingletonPropertyTypes(MDefinition* obj, JSObject* singleton, Pr key = JSProto_Boolean; break; - case MIRType_Object: - case MIRType_Value: { + case MIRType_Object: { if (!types) - return false; - - if (types->hasType(TypeSet::StringType())) { - key = JSProto_String; - *testString = true; - break; - } - - if (!types->maybeObject()) - return false; + return nullptr; // For property accesses which may be on many objects, we just need to // find a prototype common to all the objects; if that prototype // has the singleton property, the access will not be on a missing property. + JSObject* singleton = nullptr; for (unsigned i = 0; i < types->getObjectCount(); i++) { TypeSet::ObjectKey* key = types->getObject(i); if (!key) @@ -7258,35 +7248,40 @@ IonBuilder::testSingletonPropertyTypes(MDefinition* obj, JSObject* singleton, Pr const Class* clasp = key->clasp(); if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, name)) - return false; + return nullptr; if (key->unknownProperties()) - return false; + return nullptr; HeapTypeSetKey property = key->property(NameToId(name)); if (property.isOwnProperty(constraints())) - return false; + return nullptr; if (JSObject* proto = key->proto().toObjectOrNull()) { // Test this type. - if (testSingletonProperty(proto, name) != singleton) - return false; + JSObject* thisSingleton = testSingletonProperty(proto, name); + if (!thisSingleton) + return nullptr; + if (singleton) { + if (thisSingleton != singleton) + return nullptr; + } else { + singleton = thisSingleton; + } } else { // Can't be on the prototype chain with no prototypes... - return false; + return nullptr; } } - // If this is not a known object, a test will be needed. - *testObject = (obj->type() != MIRType_Object); - return true; + return singleton; } default: - return false; + return nullptr; } JSObject* proto = GetBuiltinPrototypePure(&script()->global(), key); if (proto) - return testSingletonProperty(proto, name) == singleton; + return testSingletonProperty(proto, name); - return false; + return nullptr; } bool @@ -7731,6 +7726,8 @@ IonBuilder::jsop_getelem() return pushTypeBarrier(ins, types, BarrierKind::TypeSet); } + obj = maybeUnboxForPropertyAccess(obj); + bool emitted = false; trackOptimizationAttempt(TrackedStrategy::GetElem_TypedObject); @@ -10009,6 +10006,21 @@ IonBuilder::shouldAbortOnPreliminaryGroups(MDefinition *obj) return preliminary; } +MDefinition* +IonBuilder::maybeUnboxForPropertyAccess(MDefinition* def) +{ + if (def->type() != MIRType_Value) + return def; + + MIRType type = inspector->expectedPropertyAccessInputType(pc); + if (type == MIRType_Value || !def->mightBeType(type)) + return def; + + MUnbox* unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible); + current->add(unbox); + return unbox; +} + bool IonBuilder::jsop_getprop(PropertyName* name) { @@ -10034,6 +10046,8 @@ IonBuilder::jsop_getprop(PropertyName* name) return emitted; } + obj = maybeUnboxForPropertyAccess(obj); + BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj, name, types); @@ -10301,24 +10315,19 @@ IonBuilder::getPropTryConstant(bool* emitted, MDefinition* obj, PropertyName* na { MOZ_ASSERT(*emitted == false); - JSObject* singleton = types ? types->maybeSingleton() : nullptr; - if (!singleton) { - trackOptimizationOutcome(TrackedOutcome::NotSingleton); + if (!types->mightBeMIRType(MIRType_Object)) { + // If we have not observed an object result here, don't look for a + // singleton constant. + trackOptimizationOutcome(TrackedOutcome::NotObject); return true; } - bool testObject, testString; - if (!testSingletonPropertyTypes(obj, singleton, name, &testObject, &testString)) + JSObject* singleton = testSingletonPropertyTypes(obj, name); + if (!singleton) return true; // Property access is a known constant -- safe to emit. - MOZ_ASSERT(!testString || !testObject); - if (testObject) - current->add(MGuardObject::New(alloc(), obj)); - else if (testString) - current->add(MGuardString::New(alloc(), obj)); - else - obj->setImplicitlyUsedUnchecked(); + obj->setImplicitlyUsedUnchecked(); pushConstant(ObjectValue(*singleton)); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 74489b1b78f0..ace8c2d8707e 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -422,6 +422,7 @@ class IonBuilder bool shouldAbortOnPreliminaryGroups(MDefinition *obj); MDefinition* tryInnerizeWindow(MDefinition* obj); + MDefinition* maybeUnboxForPropertyAccess(MDefinition* def); // jsop_getprop() helpers. bool checkIsDefinitelyOptimizedArguments(MDefinition* obj, bool* isOptimizedArgs); @@ -745,6 +746,7 @@ class IonBuilder InliningStatus inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode); InliningStatus inlineArrayPush(CallInfo& callInfo); InliningStatus inlineArrayConcat(CallInfo& callInfo); + InliningStatus inlineArraySlice(CallInfo& callInfo); InliningStatus inlineArrayJoin(CallInfo& callInfo); InliningStatus inlineArraySplice(CallInfo& callInfo); @@ -940,8 +942,8 @@ class IonBuilder MGetPropertyCache* getInlineableGetPropertyCache(CallInfo& callInfo); JSObject* testSingletonProperty(JSObject* obj, PropertyName* name); - bool testSingletonPropertyTypes(MDefinition* obj, JSObject* singleton, PropertyName* name, - bool* testObject, bool* testString); + JSObject* testSingletonPropertyTypes(MDefinition* obj, PropertyName* name); + uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed, BaselineInspector::ObjectGroupVector& convertUnboxedGroups); MDefinition* convertUnboxedObjects(MDefinition* obj, diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 1b8aa8811825..d0ffc6c8033e 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -4945,6 +4945,39 @@ class LArrayConcat : public LCallInstructionHelper<1, 2, 2> } }; +class LArraySlice : public LCallInstructionHelper<1, 3, 2> +{ + public: + LIR_HEADER(ArraySlice) + + LArraySlice(const LAllocation& obj, const LAllocation& begin, const LAllocation& end, + const LDefinition& temp1, const LDefinition& temp2) { + setOperand(0, obj); + setOperand(1, begin); + setOperand(2, end); + setTemp(0, temp1); + setTemp(1, temp2); + } + const MArraySlice* mir() const { + return mir_->toArraySlice(); + } + const LAllocation* object() { + return getOperand(0); + } + const LAllocation* begin() { + return getOperand(1); + } + const LAllocation* end() { + return getOperand(2); + } + const LDefinition* temp1() { + return getTemp(0); + } + const LDefinition* temp2() { + return getTemp(1); + } +}; + class LArrayJoin : public LCallInstructionHelper<1, 2, 0> { public: diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index e0876c501f4e..7babe1467daf 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -238,6 +238,7 @@ _(ArrayPushV) \ _(ArrayPushT) \ _(ArrayConcat) \ + _(ArraySlice) \ _(ArrayJoin) \ _(StoreElementHoleV) \ _(StoreElementHoleT) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 39668aebfd13..f9a860fdcaaa 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2904,6 +2904,23 @@ LIRGenerator::visitArrayConcat(MArrayConcat* ins) assignSafepoint(lir, ins); } +void +LIRGenerator::visitArraySlice(MArraySlice* ins) +{ + MOZ_ASSERT(ins->type() == MIRType_Object); + MOZ_ASSERT(ins->object()->type() == MIRType_Object); + 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), + tempFixed(CallTempReg3), + tempFixed(CallTempReg4)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + void LIRGenerator::visitArrayJoin(MArrayJoin* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 0712babafc64..a4fd6a11ddc5 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -207,6 +207,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitArrayPopShift(MArrayPopShift* ins); void visitArrayPush(MArrayPush* ins); void visitArrayConcat(MArrayConcat* ins); + void visitArraySlice(MArraySlice* ins); void visitArrayJoin(MArrayJoin* ins); void visitLoadUnboxedScalar(MLoadUnboxedScalar* ins); void visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 84860d2bd518..5c867650f07b 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -82,6 +82,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineArrayPush(callInfo); if (native == js::array_concat) return inlineArrayConcat(callInfo); + if (native == js::array_slice) + return inlineArraySlice(callInfo); if (native == js::array_splice) return inlineArraySplice(callInfo); @@ -981,6 +983,107 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo) return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineArraySlice(CallInfo& callInfo) +{ + if (callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); + return InliningStatus_NotInlined; + } + + // Ensure |this| and result are objects. + if (getInlineReturnType() != MIRType_Object) + return InliningStatus_NotInlined; + if (callInfo.thisArg()->type() != MIRType_Object) + return InliningStatus_NotInlined; + + // Arguments for the sliced region must be integers. + if (callInfo.argc() > 0) { + if (callInfo.getArg(0)->type() != MIRType_Int32) + return InliningStatus_NotInlined; + if (callInfo.argc() > 1) { + if (callInfo.getArg(1)->type() != MIRType_Int32) + return InliningStatus_NotInlined; + } + } + + // |this| must be a dense array. + TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet(); + if (!thisTypes) + return InliningStatus_NotInlined; + + const Class* clasp = thisTypes->getKnownClass(constraints()); + if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) + return InliningStatus_NotInlined; + if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW)) + { + trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); + return InliningStatus_NotInlined; + } + + JSValueType unboxedType = JSVAL_TYPE_MAGIC; + if (clasp == &UnboxedArrayObject::class_) { + unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr); + if (unboxedType == JSVAL_TYPE_MAGIC) + return InliningStatus_NotInlined; + } + + // Watch out for indexed properties on the prototype. + if (ArrayPrototypeHasIndexedProperty(constraints(), script())) { + trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); + return InliningStatus_NotInlined; + } + + // The group of the result will be dynamically fixed up to match the input + // object, allowing us to handle 'this' objects that might have more than + // one group. Make sure that no singletons can be sliced here. + for (unsigned i = 0; i < thisTypes->getObjectCount(); i++) { + TypeSet::ObjectKey* key = thisTypes->getObject(i); + if (key && key->isSingleton()) + return InliningStatus_NotInlined; + } + + // Inline the call. + JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_slice); + if (!templateObj) + return InliningStatus_NotInlined; + + callInfo.setImplicitlyUsedUnchecked(); + + MDefinition* begin; + if (callInfo.argc() > 0) + begin = callInfo.getArg(0); + else + begin = constant(Int32Value(0)); + + MDefinition* end; + if (callInfo.argc() > 1) { + end = callInfo.getArg(1); + } else if (clasp == &ArrayObject::class_) { + MElements* elements = MElements::New(alloc(), callInfo.thisArg()); + current->add(elements); + + end = MArrayLength::New(alloc(), elements); + current->add(end->toInstruction()); + } else { + end = MUnboxedArrayLength::New(alloc(), callInfo.thisArg()); + current->add(end->toInstruction()); + } + + MArraySlice* ins = MArraySlice::New(alloc(), constraints(), + callInfo.thisArg(), begin, end, + templateObj, + templateObj->group()->initialHeap(constraints()), + unboxedType); + current->add(ins); + current->push(ins); + + if (!resumeAfter(ins)) + return InliningStatus_Error; + return InliningStatus_Inlined; +} + IonBuilder::InliningStatus IonBuilder::inlineMathAbs(CallInfo& callInfo) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 5130eabc918e..48598032c5ab 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9153,6 +9153,70 @@ class MArrayConcat } }; +// Array.prototype.slice on a dense array. +class MArraySlice + : public MTernaryInstruction, + public Mix3Policy, IntPolicy<1>, IntPolicy<2>>::Data +{ + AlwaysTenuredObject templateObj_; + gc::InitialHeap initialHeap_; + JSValueType unboxedType_; + + MArraySlice(CompilerConstraintList* constraints, MDefinition* obj, + MDefinition* begin, MDefinition* end, + JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType) + : MTernaryInstruction(obj, begin, end), + templateObj_(templateObj), + initialHeap_(initialHeap), + unboxedType_(unboxedType) + { + setResultType(MIRType_Object); + setResultTypeSet(obj->resultTypeSet()); + } + + public: + INSTRUCTION_HEADER(ArraySlice) + + static MArraySlice* New(TempAllocator& alloc, CompilerConstraintList* constraints, + MDefinition* obj, MDefinition* begin, MDefinition* end, + JSObject* templateObj, gc::InitialHeap initialHeap, + JSValueType unboxedType) + { + return new(alloc) MArraySlice(constraints, obj, begin, end, templateObj, + initialHeap, unboxedType); + } + + MDefinition* object() const { + return getOperand(0); + } + MDefinition* begin() const { + return getOperand(1); + } + MDefinition* end() const { + return getOperand(2); + } + + JSObject* templateObj() const { + return templateObj_; + } + + gc::InitialHeap initialHeap() const { + return initialHeap_; + } + + JSValueType unboxedType() const { + return unboxedType_; + } + + AliasSet getAliasSet() const override { + return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) | + AliasSet::ObjectFields); + } + bool possiblyCalls() const override { + return true; + } +}; + class MArrayJoin : public MBinaryInstruction, public MixPolicy, StringPolicy<1> >::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index fcdc789bdbe4..76d00e2f4233 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -200,6 +200,7 @@ namespace jit { _(ArrayPopShift) \ _(ArrayPush) \ _(ArrayConcat) \ + _(ArraySlice) \ _(ArrayJoin) \ _(LoadTypedArrayElementHole) \ _(LoadTypedArrayElementStatic) \ diff --git a/js/src/jit/arm/Assembler-arm.cpp b/js/src/jit/arm/Assembler-arm.cpp index 305feba0d1a0..45956d25deee 100644 --- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -26,12 +26,12 @@ void dbg_break() {} // Note this is used for inter-AsmJS calls and may pass arguments and results in // floating point registers even if the system ABI does not. -ABIArgGenerator::ABIArgGenerator() : - intRegIndex_(0), +ABIArgGenerator::ABIArgGenerator() + : intRegIndex_(0), floatRegIndex_(0), stackOffset_(0), current_() -{} +{ } ABIArg ABIArgGenerator::next(MIRType type) @@ -498,15 +498,17 @@ InstMOV::IsTHIS(const Instruction& i) } Op2Reg -Operand2::toOp2Reg() { +Operand2::toOp2Reg() const { return *(Op2Reg*)this; } + O2RegImmShift -Op2Reg::toO2RegImmShift() { +Op2Reg::toO2RegImmShift() const { return *(O2RegImmShift*)this; } + O2RegRegShift -Op2Reg::toO2RegRegShift() { +Op2Reg::toO2RegRegShift() const { return *(O2RegRegShift*)this; } @@ -1257,7 +1259,7 @@ BOffImm::BOffImm(Instruction& inst) } Instruction* -BOffImm::getDest(Instruction* src) +BOffImm::getDest(Instruction* src) const { // TODO: It is probably worthwhile to verify that src is actually a branch. // NOTE: This does not explicitly shift the offset of the destination left by 2, @@ -1425,120 +1427,119 @@ Assembler::as_nop() } static uint32_t -EncodeAlu(Register dest, Register src1, Operand2 op2, ALUOp op, SetCond_ sc, - Assembler::Condition c) +EncodeAlu(Register dest, Register src1, Operand2 op2, ALUOp op, SBit s, Assembler::Condition c) { - return (int)op | (int)sc | (int) c | op2.encode() | + return (int)op | (int)s | (int)c | op2.encode() | ((dest == InvalidReg) ? 0 : RD(dest)) | ((src1 == InvalidReg) ? 0 : RN(src1)); } BufferOffset Assembler::as_alu(Register dest, Register src1, Operand2 op2, - ALUOp op, SetCond_ sc, Condition c) + ALUOp op, SBit s, Condition c) { - return writeInst(EncodeAlu(dest, src1, op2, op, sc, c)); + return writeInst(EncodeAlu(dest, src1, op2, op, s, c)); } BufferOffset -Assembler::as_mov(Register dest, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_mov(Register dest, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, InvalidReg, op2, OpMov, sc, c); + return as_alu(dest, InvalidReg, op2, OpMov, s, c); } /* static */ void -Assembler::as_alu_patch(Register dest, Register src1, Operand2 op2, ALUOp op, SetCond_ sc, +Assembler::as_alu_patch(Register dest, Register src1, Operand2 op2, ALUOp op, SBit s, Condition c, uint32_t* pos) { - WriteInstStatic(EncodeAlu(dest, src1, op2, op, sc, c), pos); + WriteInstStatic(EncodeAlu(dest, src1, op2, op, s, c), pos); } /* static */ void -Assembler::as_mov_patch(Register dest, Operand2 op2, SetCond_ sc, Condition c, uint32_t* pos) +Assembler::as_mov_patch(Register dest, Operand2 op2, SBit s, Condition c, uint32_t* pos) { - as_alu_patch(dest, InvalidReg, op2, OpMov, sc, c, pos); + as_alu_patch(dest, InvalidReg, op2, OpMov, s, c, pos); } BufferOffset -Assembler::as_mvn(Register dest, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_mvn(Register dest, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, InvalidReg, op2, OpMvn, sc, c); + return as_alu(dest, InvalidReg, op2, OpMvn, s, c); } // Logical operations. BufferOffset -Assembler::as_and(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_and(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpAnd, sc, c); + return as_alu(dest, src1, op2, OpAnd, s, c); } BufferOffset -Assembler::as_bic(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_bic(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpBic, sc, c); + return as_alu(dest, src1, op2, OpBic, s, c); } BufferOffset -Assembler::as_eor(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_eor(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpEor, sc, c); + return as_alu(dest, src1, op2, OpEor, s, c); } BufferOffset -Assembler::as_orr(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_orr(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpOrr, sc, c); + return as_alu(dest, src1, op2, OpOrr, s, c); } // Mathematical operations. BufferOffset -Assembler::as_adc(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_adc(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpAdc, sc, c); + return as_alu(dest, src1, op2, OpAdc, s, c); } BufferOffset -Assembler::as_add(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_add(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpAdd, sc, c); + return as_alu(dest, src1, op2, OpAdd, s, c); } BufferOffset -Assembler::as_sbc(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_sbc(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpSbc, sc, c); + return as_alu(dest, src1, op2, OpSbc, s, c); } BufferOffset -Assembler::as_sub(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_sub(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpSub, sc, c); + return as_alu(dest, src1, op2, OpSub, s, c); } BufferOffset -Assembler::as_rsb(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_rsb(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpRsb, sc, c); + return as_alu(dest, src1, op2, OpRsb, s, c); } BufferOffset -Assembler::as_rsc(Register dest, Register src1, Operand2 op2, SetCond_ sc, Condition c) +Assembler::as_rsc(Register dest, Register src1, Operand2 op2, SBit s, Condition c) { - return as_alu(dest, src1, op2, OpRsc, sc, c); + return as_alu(dest, src1, op2, OpRsc, s, c); } // Test operations. BufferOffset Assembler::as_cmn(Register src1, Operand2 op2, Condition c) { - return as_alu(InvalidReg, src1, op2, OpCmn, SetCond, c); + return as_alu(InvalidReg, src1, op2, OpCmn, SetCC, c); } BufferOffset Assembler::as_cmp(Register src1, Operand2 op2, Condition c) { - return as_alu(InvalidReg, src1, op2, OpCmp, SetCond, c); + return as_alu(InvalidReg, src1, op2, OpCmp, SetCC, c); } BufferOffset Assembler::as_teq(Register src1, Operand2 op2, Condition c) { - return as_alu(InvalidReg, src1, op2, OpTeq, SetCond, c); + return as_alu(InvalidReg, src1, op2, OpTeq, SetCC, c); } BufferOffset Assembler::as_tst(Register src1, Operand2 op2, Condition c) { - return as_alu(InvalidReg, src1, op2, OpTst, SetCond, c); + return as_alu(InvalidReg, src1, op2, OpTst, SetCC, c); } static MOZ_CONSTEXPR_VAR Register NoAddend = { Registers::pc }; @@ -1619,59 +1620,59 @@ static const int mull_tag = 0x90; BufferOffset Assembler::as_genmul(Register dhi, Register dlo, Register rm, Register rn, - MULOp op, SetCond_ sc, Condition c) + MULOp op, SBit s, Condition c) { - return writeInst(RN(dhi) | maybeRD(dlo) | RM(rm) | rn.code() | op | sc | c | mull_tag); + return writeInst(RN(dhi) | maybeRD(dlo) | RM(rm) | rn.code() | op | s | c | mull_tag); } BufferOffset -Assembler::as_mul(Register dest, Register src1, Register src2, SetCond_ sc, Condition c) +Assembler::as_mul(Register dest, Register src1, Register src2, SBit s, Condition c) { - return as_genmul(dest, InvalidReg, src1, src2, OpmMul, sc, c); + return as_genmul(dest, InvalidReg, src1, src2, OpmMul, s, c); } BufferOffset Assembler::as_mla(Register dest, Register acc, Register src1, Register src2, - SetCond_ sc, Condition c) + SBit s, Condition c) { - return as_genmul(dest, acc, src1, src2, OpmMla, sc, c); + return as_genmul(dest, acc, src1, src2, OpmMla, s, c); } BufferOffset Assembler::as_umaal(Register destHI, Register destLO, Register src1, Register src2, Condition c) { - return as_genmul(destHI, destLO, src1, src2, OpmUmaal, NoSetCond, c); + return as_genmul(destHI, destLO, src1, src2, OpmUmaal, LeaveCC, c); } BufferOffset Assembler::as_mls(Register dest, Register acc, Register src1, Register src2, Condition c) { - return as_genmul(dest, acc, src1, src2, OpmMls, NoSetCond, c); + return as_genmul(dest, acc, src1, src2, OpmMls, LeaveCC, c); } BufferOffset Assembler::as_umull(Register destHI, Register destLO, Register src1, Register src2, - SetCond_ sc, Condition c) + SBit s, Condition c) { - return as_genmul(destHI, destLO, src1, src2, OpmUmull, sc, c); + return as_genmul(destHI, destLO, src1, src2, OpmUmull, s, c); } BufferOffset Assembler::as_umlal(Register destHI, Register destLO, Register src1, Register src2, - SetCond_ sc, Condition c) + SBit s, Condition c) { - return as_genmul(destHI, destLO, src1, src2, OpmUmlal, sc, c); + return as_genmul(destHI, destLO, src1, src2, OpmUmlal, s, c); } BufferOffset Assembler::as_smull(Register destHI, Register destLO, Register src1, Register src2, - SetCond_ sc, Condition c) + SBit s, Condition c) { - return as_genmul(destHI, destLO, src1, src2, OpmSmull, sc, c); + return as_genmul(destHI, destLO, src1, src2, OpmSmull, s, c); } BufferOffset Assembler::as_smlal(Register destHI, Register destLO, Register src1, Register src2, - SetCond_ sc, Condition c) + SBit s, Condition c) { - return as_genmul(destHI, destLO, src1, src2, OpmSmlal, sc, c); + return as_genmul(destHI, destLO, src1, src2, OpmSmlal, s, c); } BufferOffset @@ -1716,7 +1717,8 @@ Assembler::as_dtr_patch(LoadStore ls, int size, Index mode, Register rt, DTRAddr WriteInstStatic(EncodeDtr(ls, size, mode, rt, addr, c), dest); } -class PoolHintData { +class PoolHintData +{ public: enum LoadType { // Set 0 to bogus, since that is the value most likely to be @@ -1759,19 +1761,19 @@ class PoolHintData { destReg_ = destReg.id(); destType_ = destReg.isDouble(); } - Assembler::Condition getCond() { + Assembler::Condition getCond() const { return Assembler::Condition(cond_ << 28); } - Register getReg() { + Register getReg() const { return Register::FromCode(destReg_); } - VFPRegister getVFPReg() { + VFPRegister getVFPReg() const { VFPRegister r = VFPRegister(destReg_, destType_ ? VFPRegister::Double : VFPRegister::Single); return r; } - int32_t getIndex() { + int32_t getIndex() const { return index_; } void setIndex(uint32_t index) { @@ -1780,7 +1782,7 @@ class PoolHintData { MOZ_ASSERT(index_ == index); } - LoadType getLoadType() { + LoadType getLoadType() const { // If this *was* a PoolBranch, but the branch has already been bound // then this isn't going to look like a real poolhintdata, but we still // want to lie about it so everyone knows it *used* to be a branch. @@ -1789,7 +1791,7 @@ class PoolHintData { return loadType_; } - bool isValidPoolHint() { + bool isValidPoolHint() const { // Most instructions cannot have a condition that is 0xf. Notable // exceptions are blx and the entire NEON instruction set. For the // purposes of pool loads, and possibly patched branches, the possible @@ -1799,7 +1801,8 @@ class PoolHintData { } }; -union PoolHintPun { +union PoolHintPun +{ PoolHintData phd; uint32_t raw; }; @@ -1844,8 +1847,7 @@ BufferOffset Assembler::as_dtm(LoadStore ls, Register rn, uint32_t mask, DTMMode mode, DTMWriteBack wb, Condition c) { - return writeInst(0x08000000 | RN(rn) | ls | - mode | mask | c | wb); + return writeInst(0x08000000 | RN(rn) | ls | mode | mask | c | wb); } BufferOffset @@ -2056,12 +2058,14 @@ Assembler::as_bx(Register r, Condition c) BufferOffset ret = writeInst(((int) c) | OpBx | r.code()); return ret; } + void Assembler::WritePoolGuard(BufferOffset branch, Instruction* dest, BufferOffset afterPool) { BOffImm off = afterPool.diffB(branch); *dest = InstBImm(off, Always); } + // Branch can branch to an immediate *or* to a register. // Branches to immediates are pc relative, branches to registers are absolute. BufferOffset @@ -2106,6 +2110,7 @@ Assembler::as_b(Label* l, Condition c) MOZ_ASSERT(check == old); return ret; } + BufferOffset Assembler::as_b(BOffImm off, Condition c, BufferOffset inst) { @@ -2168,6 +2173,7 @@ Assembler::as_bl(Label* l, Condition c) MOZ_ASSERT(check == old); return ret; } + BufferOffset Assembler::as_bl(BOffImm off, Condition c, BufferOffset inst) { @@ -2195,6 +2201,7 @@ enum vfp_tags { VfpTag = 0x0C000A00, VfpArith = 0x02000000 }; + BufferOffset Assembler::writeVFPInst(vfp_size sz, uint32_t blob) { @@ -2225,43 +2232,37 @@ Assembler::as_vfp_float(VFPRegister vd, VFPRegister vn, VFPRegister vm, } BufferOffset -Assembler::as_vadd(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c) +Assembler::as_vadd(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c) { return as_vfp_float(vd, vn, vm, OpvAdd, c); } BufferOffset -Assembler::as_vdiv(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c) +Assembler::as_vdiv(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c) { return as_vfp_float(vd, vn, vm, OpvDiv, c); } BufferOffset -Assembler::as_vmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c) +Assembler::as_vmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c) { return as_vfp_float(vd, vn, vm, OpvMul, c); } BufferOffset -Assembler::as_vnmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c) +Assembler::as_vnmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c) { return as_vfp_float(vd, vn, vm, OpvMul, c); } BufferOffset -Assembler::as_vnmla(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c) +Assembler::as_vnmla(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c) { MOZ_CRASH("Feature NYI"); } BufferOffset -Assembler::as_vnmls(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c) +Assembler::as_vnmls(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c) { MOZ_CRASH("Feature NYI"); } @@ -2285,18 +2286,17 @@ Assembler::as_vabs(VFPRegister vd, VFPRegister vm, Condition c) } BufferOffset -Assembler::as_vsub(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c) +Assembler::as_vsub(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c) { return as_vfp_float(vd, vn, vm, OpvSub, c); } BufferOffset -Assembler::as_vcmp(VFPRegister vd, VFPRegister vm, - Condition c) +Assembler::as_vcmp(VFPRegister vd, VFPRegister vm, Condition c) { return as_vfp_float(vd, NoVFPRegister, vm, OpvCmp, c); } + BufferOffset Assembler::as_vcmpz(VFPRegister vd, Condition c) { @@ -2309,6 +2309,7 @@ Assembler::as_vmov(VFPRegister vd, VFPRegister vsrc, Condition c) { return as_vfp_float(vd, NoVFPRegister, vsrc, OpvMov, c); } + // Transfer between Core and VFP. // Unlike the next function, moving between the core registers and vfp registers @@ -2339,15 +2340,13 @@ Assembler::as_vxfer(Register vt1, Register vt2, VFPRegister vm, FloatToCore_ f2c MOZ_ASSERT(idx == 0); } - if (vt2 == InvalidReg) { - return writeVFPInst(sz, WordTransfer | f2c | c | - RT(vt1) | maybeRN(vt2) | VN(vm) | idx); - } else { - // We are doing a 64 bit transfer. - return writeVFPInst(sz, DoubleTransfer | f2c | c | - RT(vt1) | maybeRN(vt2) | VM(vm) | idx); - } + if (vt2 == InvalidReg) + return writeVFPInst(sz, WordTransfer | f2c | c | RT(vt1) | maybeRN(vt2) | VN(vm) | idx); + + // We are doing a 64 bit transfer. + return writeVFPInst(sz, DoubleTransfer | f2c | c | RT(vt1) | maybeRN(vt2) | VM(vm) | idx); } + enum vcvt_destFloatness { VcvtToInteger = 1 << 18, VcvtToFloat = 0 << 18 @@ -2384,9 +2383,9 @@ Assembler::as_vcvt(VFPRegister vd, VFPRegister vm, bool useFPSCR, vcvt_Signedness opSign; vcvt_toZero doToZero = VcvtToFPSCR; MOZ_ASSERT(vd.isFloat() || vm.isFloat()); - if (vd.isSingle() || vm.isSingle()) { + if (vd.isSingle() || vm.isSingle()) sz = IsSingle; - } + if (vd.isFloat()) { destFloat = VcvtToFloat; opSign = (vm.isSInt()) ? VcvtFromSigned : VcvtFromUnsigned; @@ -2459,6 +2458,7 @@ Assembler::as_vimm(VFPRegister vd, VFPImm imm, Condition c) return writeVFPInst(sz, c | imm.encode() | VD(vd) | 0x02B00000); } + BufferOffset Assembler::as_vmrs(Register r, Condition c) { @@ -2577,7 +2577,6 @@ Assembler::retarget(Label* label, Label* target) } - static int stopBKPT = -1; void Assembler::as_bkpt() @@ -2632,6 +2631,7 @@ Assembler::GetBranchOffset(const Instruction* i_) i->extractImm(&dest); return dest.decode(); } + void Assembler::RetargetNearBranch(Instruction* i, int offset, bool final) { @@ -2667,7 +2667,8 @@ Assembler::RetargetFarBranch(Instruction* i, uint8_t** slot, uint8_t* dest, Cond } -struct PoolHeader : Instruction { +struct PoolHeader : Instruction +{ struct Header { // The size should take into account the pool header. @@ -2708,6 +2709,7 @@ struct PoolHeader : Instruction { Header tmp(this); return tmp.isNatural; } + static bool IsTHIS(const Instruction& i) { return (*i.raw() & 0xffff0000) == 0xffff0000; } @@ -2718,12 +2720,10 @@ struct PoolHeader : Instruction { } }; - void Assembler::WritePoolHeader(uint8_t* start, Pool* p, bool isNatural) { - static_assert(sizeof(PoolHeader) == 4, - "PoolHandler must have the correct size."); + static_assert(sizeof(PoolHeader) == 4, "PoolHandler must have the correct size."); uint8_t* pool = start + 4; // Go through the usual rigmarole to get the size of the pool. pool += p->getPoolSize(); @@ -2735,7 +2735,6 @@ Assembler::WritePoolHeader(uint8_t* start, Pool* p, bool isNatural) *(PoolHeader*)start = header; } - // The size of an arbitrary 32-bit call in the instruction stream. On ARM this // sequence is |pc = ldr pc - 4; imm32| given that we never reach the imm32. uint32_t @@ -2743,6 +2742,7 @@ Assembler::PatchWrite_NearCallSize() { return sizeof(uint32_t); } + void Assembler::PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall) { @@ -2754,8 +2754,8 @@ Assembler::PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall new (inst) InstBLImm(BOffImm(dest - (uint8_t*)inst) , Always); // Ensure everyone sees the code that was just written into memory. AutoFlushICache::flush(uintptr_t(inst), 4); - } + void Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, PatchedImmPtr expectedValue) @@ -2796,7 +2796,6 @@ Assembler::PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm) { *(raw - 1) = imm.value; } - uint8_t* Assembler::NextInstruction(uint8_t* inst_, uint32_t* count) { @@ -2821,7 +2820,8 @@ InstIsGuard(Instruction* inst, const PoolHeader** ph) } static bool -InstIsBNop(Instruction* inst) { +InstIsBNop(Instruction* inst) +{ // In some special situations, it is necessary to insert a NOP into the // instruction stream that nobody knows about, since nobody should know // about it, make sure it gets skipped when Instruction::next() is called. @@ -3024,13 +3024,14 @@ void Assembler::UpdateBoundsCheck(uint32_t heapSize, Instruction* inst) Imm8 imm8 = Imm8(heapSize); MOZ_ASSERT(!imm8.invalid); - *inst = InstALU(InvalidReg, index, imm8, OpCmp, SetCond, Always); + *inst = InstALU(InvalidReg, index, imm8, OpCmp, SetCC, Always); // NOTE: we don't update the Auto Flush Cache! this function is currently // only called from within AsmJSModule::patchHeapAccesses, which does that // for us. Don't call this! } -InstructionIterator::InstructionIterator(Instruction* i_) : i(i_) +InstructionIterator::InstructionIterator(Instruction* i_) + : i(i_) { // Work around pools with an artificial pool guard and around nop-fill. i = i->skipPool(); diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index 66262de8250c..54b9a592bb0e 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -74,9 +74,11 @@ class ABIArgGenerator public: ABIArgGenerator(); + ABIArg next(MIRType argType); ABIArg& current() { return current_; } uint32_t stackBytesConsumedSoFar() const { return stackOffset_; } + static const Register NonArgReturnReg0; static const Register NonArgReturnReg1; static const Register NonReturn_VolatileReg0; @@ -237,7 +239,6 @@ enum IsImmEDTR_ { IsNotImmEDTR = 0 << 22 }; - enum ShiftType { LSL = 0, // << 5 LSR = 1, // << 5 @@ -273,14 +274,17 @@ enum DTMWriteBack { NoWriteBack = 0 << 21 }; -enum SetCond_ { - SetCond = 1 << 20, - NoSetCond = 0 << 20 +// Condition code updating mode. +enum SBit { + SetCC = 1 << 20, // Set condition code. + LeaveCC = 0 << 20 // Leave condition code unchanged. }; + enum LoadStore { IsLoad = 1 << 20, IsStore = 0 << 20 }; + // You almost never want to use this directly. Instead, you wantto pass in a // signed constant, and let this bit be implicitly set for you. This is however, // necessary if we want a negative index. @@ -341,16 +345,19 @@ enum VFPOp { OpvCmp = 0xB << 20 | 0x1 << 6 | 0x4 << 16, OpvCmpz = 0xB << 20 | 0x1 << 6 | 0x5 << 16 }; + // Negate the operation, AND negate the immediate that we were passed in. ALUOp ALUNeg(ALUOp op, Register dest, Imm32* imm, Register* negDest); bool can_dbl(ALUOp op); bool condsAreSafe(ALUOp op); + // If there is a variant of op that has a dest (think cmp/sub) return that // variant of it. ALUOp getDestVariant(ALUOp op); static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data); static const ValueOperand softfpReturnOperand = ValueOperand(r1, r0); + // All of these classes exist solely to shuffle data into the various operands. // For example Operand2 can be an imm8, a register-shifted-by-a-constant or a // register-shifted-by-a-register. We represent this in C++ by having a base @@ -372,7 +379,9 @@ static const ValueOperand softfpReturnOperand = ValueOperand(r1, r0); class Op2Reg; class O2RegImmShift; class O2RegRegShift; + namespace datastore { + struct Reg { // The "second register". @@ -389,12 +398,13 @@ struct Reg : RM(rm), RRS(rsr), Type(type), ShiftAmount(shiftamount), pad(0) { } - uint32_t encode() { - return RM | RRS << 4 | Type << 5 | ShiftAmount << 7; - } explicit Reg(const Op2Reg& op) { memcpy(this, &op, sizeof(*this)); } + + uint32_t encode() const { + return RM | RRS << 4 | Type << 5 | ShiftAmount << 7; + } }; // Op2 has a mode labelled "", which is arm's magical immediate encoding. @@ -409,10 +419,12 @@ struct Imm8mData // if we can encode it properly, a simple "|" will still suffice to meld it // into the instruction. uint32_t buff : 19; + public: uint32_t invalid : 1; - uint32_t encode() { + public: + uint32_t encode() const { MOZ_ASSERT(!invalid); return data | rot << 8; }; @@ -438,12 +450,16 @@ struct Imm8Data uint32_t imm4H : 4; public: - uint32_t encode() { - return imm4L | (imm4H << 8); - }; - Imm8Data(uint32_t imm) : imm4L(imm & 0xf), imm4H(imm >> 4) { + Imm8Data(uint32_t imm) + : imm4L(imm & 0xf), imm4H(imm >> 4) + { MOZ_ASSERT(imm <= 0xff); } + + public: + uint32_t encode() const { + return imm4L | (imm4H << 8); + }; }; // VLDR/VSTR take an 8 bit offset, which is implicitly left shifted by 2. @@ -453,12 +469,16 @@ struct Imm8VFPOffData uint32_t data; public: - uint32_t encode() { - return data; - }; - Imm8VFPOffData(uint32_t imm) : data (imm) { + Imm8VFPOffData(uint32_t imm) + : data (imm) + { MOZ_ASSERT((imm & ~(0xff)) == 0); } + + public: + uint32_t encode() const { + return data; + }; }; // ARM can magically encode 256 very special immediates to be moved into a @@ -478,7 +498,7 @@ struct Imm8VFPImmData uint32_t imm4H : 4; int32_t isInvalid : 24; - uint32_t encode() { + uint32_t encode() const { // This assert is an attempting at ensuring that we don't create random // instances of this structure and then asking to encode() it. MOZ_ASSERT(isInvalid == 0); @@ -489,9 +509,6 @@ struct Imm8VFPImmData struct Imm12Data { uint32_t data : 12; - uint32_t encode() { - return data; - } Imm12Data(uint32_t imm) : data(imm) @@ -499,21 +516,28 @@ struct Imm12Data MOZ_ASSERT(data == imm); } + uint32_t encode() const { + return data; + } }; struct RIS { uint32_t ShiftAmount : 5; - uint32_t encode () { - return ShiftAmount; - } RIS(uint32_t imm) : ShiftAmount(imm) { MOZ_ASSERT(ShiftAmount == imm); } - explicit RIS(Reg r) : ShiftAmount(r.ShiftAmount) {} + + explicit RIS(Reg r) + : ShiftAmount(r.ShiftAmount) + { } + + uint32_t encode() const { + return ShiftAmount; + } }; struct RRS @@ -528,7 +552,7 @@ struct RRS MOZ_ASSERT(rs == RS); } - uint32_t encode () { + uint32_t encode() const { return RS << 1; } }; @@ -542,40 +566,49 @@ class Operand2 friend class Operand; friend class MacroAssemblerARM; friend class InstALU; + public: uint32_t oper : 31; uint32_t invalid : 1; - bool isO2Reg() { - return !(oper & IsImmOp2); - } - Op2Reg toOp2Reg(); - bool isImm8() { - return oper & IsImmOp2; - } protected: - Operand2(datastore::Imm8mData base) + explicit Operand2(datastore::Imm8mData base) : oper(base.invalid ? -1 : (base.encode() | (uint32_t)IsImmOp2)), invalid(base.invalid) { } - Operand2(datastore::Reg base) + explicit Operand2(datastore::Reg base) : oper(base.encode() | (uint32_t)IsNotImmOp2) { } private: - Operand2(int blob) + explicit Operand2(int blob) : oper(blob) { } public: - uint32_t encode() { + bool isO2Reg() const { + return !(oper & IsImmOp2); + } + + Op2Reg toOp2Reg() const; + + bool isImm8() const { + return oper & IsImmOp2; + } + + uint32_t encode() const { return oper; } }; class Imm8 : public Operand2 { + public: + explicit Imm8(uint32_t imm) + : Operand2(EncodeImm(imm)) + { } + public: static datastore::Imm8mData EncodeImm(uint32_t imm) { // mozilla::CountLeadingZeroes32(imm) requires imm != 0. @@ -607,6 +640,7 @@ class Imm8 : public Operand2 return datastore::Imm8mData(mask, (8 - right) >> 1); return datastore::Imm8mData(); } + // Pair template? struct TwoImm8mData { @@ -622,41 +656,41 @@ class Imm8 : public Operand2 }; static TwoImm8mData EncodeTwoImms(uint32_t); - Imm8(uint32_t imm) - : Operand2(EncodeImm(imm)) - { } }; class Op2Reg : public Operand2 { public: - Op2Reg(Register rm, ShiftType type, datastore::RIS shiftImm) + explicit Op2Reg(Register rm, ShiftType type, datastore::RIS shiftImm) : Operand2(datastore::Reg(rm.code(), type, 0, shiftImm.encode())) { } - Op2Reg(Register rm, ShiftType type, datastore::RRS shiftReg) + explicit Op2Reg(Register rm, ShiftType type, datastore::RRS shiftReg) : Operand2(datastore::Reg(rm.code(), type, 1, shiftReg.encode())) { } - bool isO2RegImmShift() { + + public: + bool isO2RegImmShift() const { datastore::Reg r(*this); return !r.RRS; } - O2RegImmShift toO2RegImmShift(); - bool isO2RegRegShift() { + O2RegImmShift toO2RegImmShift() const; + + bool isO2RegRegShift() const { datastore::Reg r(*this); return r.RRS; } - O2RegRegShift toO2RegRegShift(); + O2RegRegShift toO2RegRegShift() const; - bool checkType(ShiftType type) { + bool checkType(ShiftType type) const { datastore::Reg r(*this); return r.Type == type; } - bool checkRM(Register rm) { + bool checkRM(Register rm) const { datastore::Reg r(*this); return r.RM == rm.code(); } - bool getRM(Register* rm) { + bool getRM(Register* rm) const { datastore::Reg r(*this); *rm = Register::FromCode(r.RM); return true; @@ -666,10 +700,12 @@ class Op2Reg : public Operand2 class O2RegImmShift : public Op2Reg { public: - O2RegImmShift(Register rn, ShiftType type, uint32_t shift) + explicit O2RegImmShift(Register rn, ShiftType type, uint32_t shift) : Op2Reg(rn, type, datastore::RIS(shift)) { } - int getShift() { + + public: + int getShift() const { datastore::Reg r(*this); datastore::RIS ris(r); return ris.ShiftAmount; @@ -679,22 +715,22 @@ class O2RegImmShift : public Op2Reg class O2RegRegShift : public Op2Reg { public: - O2RegRegShift(Register rn, ShiftType type, Register rs) + explicit O2RegRegShift(Register rn, ShiftType type, Register rs) : Op2Reg(rn, type, datastore::RRS(rs.code())) { } }; O2RegImmShift O2Reg(Register r); -O2RegImmShift lsl (Register r, int amt); -O2RegImmShift lsr (Register r, int amt); -O2RegImmShift asr (Register r, int amt); -O2RegImmShift rol (Register r, int amt); -O2RegImmShift ror (Register r, int amt); +O2RegImmShift lsl(Register r, int amt); +O2RegImmShift lsr(Register r, int amt); +O2RegImmShift asr(Register r, int amt); +O2RegImmShift rol(Register r, int amt); +O2RegImmShift ror(Register r, int amt); -O2RegRegShift lsl (Register r, Register amt); -O2RegRegShift lsr (Register r, Register amt); -O2RegRegShift asr (Register r, Register amt); -O2RegRegShift ror (Register r, Register amt); +O2RegRegShift lsl(Register r, Register amt); +O2RegRegShift lsr(Register r, Register amt); +O2RegRegShift asr(Register r, Register amt); +O2RegRegShift ror(Register r, Register amt); // An offset from a register to be used for ldr/str. This should include the // sign bit, since ARM has "signed-magnitude" offsets. That is it encodes an @@ -706,22 +742,22 @@ class DtrOff uint32_t data; protected: - DtrOff(datastore::Imm12Data immdata, IsUp_ iu) + explicit DtrOff(datastore::Imm12Data immdata, IsUp_ iu) : data(immdata.encode() | (uint32_t)IsImmDTR | ((uint32_t)iu)) { } - DtrOff(datastore::Reg reg, IsUp_ iu = IsUp) + explicit DtrOff(datastore::Reg reg, IsUp_ iu = IsUp) : data(reg.encode() | (uint32_t) IsNotImmDTR | iu) { } public: - uint32_t encode() { return data; } + uint32_t encode() const { return data; } }; class DtrOffImm : public DtrOff { public: - DtrOffImm(int32_t imm) + explicit DtrOffImm(int32_t imm) : DtrOff(datastore::Imm12Data(mozilla::Abs(imm)), imm >= 0 ? IsUp : IsDown) { MOZ_ASSERT(mozilla::Abs(imm) < 4096); @@ -733,11 +769,11 @@ class DtrOffReg : public DtrOff // These are designed to be called by a constructor of a subclass. // Constructing the necessary RIS/RRS structures are annoying. protected: - DtrOffReg(Register rn, ShiftType type, datastore::RIS shiftImm, IsUp_ iu = IsUp) + explicit DtrOffReg(Register rn, ShiftType type, datastore::RIS shiftImm, IsUp_ iu = IsUp) : DtrOff(datastore::Reg(rn.code(), type, 0, shiftImm.encode()), iu) { } - DtrOffReg(Register rn, ShiftType type, datastore::RRS shiftReg, IsUp_ iu = IsUp) + explicit DtrOffReg(Register rn, ShiftType type, datastore::RRS shiftReg, IsUp_ iu = IsUp) : DtrOff(datastore::Reg(rn.code(), type, 1, shiftReg.encode()), iu) { } }; @@ -745,7 +781,7 @@ class DtrOffReg : public DtrOff class DtrRegImmShift : public DtrOffReg { public: - DtrRegImmShift(Register rn, ShiftType type, uint32_t shift, IsUp_ iu = IsUp) + explicit DtrRegImmShift(Register rn, ShiftType type, uint32_t shift, IsUp_ iu = IsUp) : DtrOffReg(rn, type, datastore::RIS(shift), iu) { } }; @@ -753,7 +789,7 @@ class DtrRegImmShift : public DtrOffReg class DtrRegRegShift : public DtrOffReg { public: - DtrRegRegShift(Register rn, ShiftType type, Register rs, IsUp_ iu = IsUp) + explicit DtrRegRegShift(Register rn, ShiftType type, Register rs, IsUp_ iu = IsUp) : DtrOffReg(rn, type, datastore::RRS(rs.code()), iu) { } }; @@ -762,24 +798,28 @@ class DtrRegRegShift : public DtrOffReg // an "operand" to a load instruction. class DTRAddr { + friend class Operand; + uint32_t data; public: - DTRAddr(Register reg, DtrOff dtr) + explicit DTRAddr(Register reg, DtrOff dtr) : data(dtr.encode() | (reg.code() << 16)) { } - uint32_t encode() { - return data; - } - Register getBase() { - return Register::FromCode((data >> 16) &0xf); - } private: - friend class Operand; - DTRAddr(uint32_t blob) + explicit DTRAddr(uint32_t blob) : data(blob) { } + + public: + uint32_t encode() const { + return data; + } + + Register getBase() const { + return Register::FromCode((data >> 16) &0xf); + } }; // Offsets for the extended data transfer instructions: @@ -789,16 +829,16 @@ class EDtrOff uint32_t data; protected: - EDtrOff(datastore::Imm8Data imm8, IsUp_ iu = IsUp) + explicit EDtrOff(datastore::Imm8Data imm8, IsUp_ iu = IsUp) : data(imm8.encode() | IsImmEDTR | (uint32_t)iu) { } - EDtrOff(Register rm, IsUp_ iu = IsUp) + explicit EDtrOff(Register rm, IsUp_ iu = IsUp) : data(rm.code() | IsNotImmEDTR | iu) { } public: - uint32_t encode() { + uint32_t encode() const { return data; } }; @@ -806,7 +846,7 @@ class EDtrOff class EDtrOffImm : public EDtrOff { public: - EDtrOffImm(int32_t imm) + explicit EDtrOffImm(int32_t imm) : EDtrOff(datastore::Imm8Data(mozilla::Abs(imm)), (imm >= 0) ? IsUp : IsDown) { MOZ_ASSERT(mozilla::Abs(imm) < 256); @@ -818,7 +858,7 @@ class EDtrOffImm : public EDtrOff class EDtrOffReg : public EDtrOff { public: - EDtrOffReg(Register rm) + explicit EDtrOffReg(Register rm) : EDtrOff(rm) { } }; @@ -828,11 +868,11 @@ class EDtrAddr uint32_t data; public: - EDtrAddr(Register r, EDtrOff off) + explicit EDtrAddr(Register r, EDtrOff off) : data(RN(r) | off.encode()) { } - uint32_t encode() { + uint32_t encode() const { return data; } }; @@ -842,12 +882,12 @@ class VFPOff uint32_t data; protected: - VFPOff(datastore::Imm8VFPOffData imm, IsUp_ isup) + explicit VFPOff(datastore::Imm8VFPOffData imm, IsUp_ isup) : data(imm.encode() | (uint32_t)isup) { } public: - uint32_t encode() { + uint32_t encode() const { return data; } }; @@ -855,45 +895,49 @@ class VFPOff class VFPOffImm : public VFPOff { public: - VFPOffImm(int32_t imm) + explicit VFPOffImm(int32_t imm) : VFPOff(datastore::Imm8VFPOffData(mozilla::Abs(imm) / 4), imm < 0 ? IsDown : IsUp) { MOZ_ASSERT(mozilla::Abs(imm) <= 255 * 4); } }; + class VFPAddr { friend class Operand; uint32_t data; + public: + explicit VFPAddr(Register base, VFPOff off) + : data(RN(base) | off.encode()) + { } + protected: VFPAddr(uint32_t blob) : data(blob) { } public: - VFPAddr(Register base, VFPOff off) - : data(RN(base) | off.encode()) - { } - - uint32_t encode() { + uint32_t encode() const { return data; } }; -class VFPImm { +class VFPImm +{ uint32_t data; + public: + explicit VFPImm(uint32_t topWordOfDouble); + public: static const VFPImm One; - VFPImm(uint32_t topWordOfDouble); - - uint32_t encode() { + uint32_t encode() const { return data; } - bool isValid() { + bool isValid() const { return data != -1U; } }; @@ -903,16 +947,11 @@ class VFPImm { // constructing a branch. class BOffImm { + friend class InstBranchImm; + uint32_t data; public: - uint32_t encode() { - return data; - } - int32_t decode() { - return ((((int32_t)data) << 8) >> 6) + 8; - } - explicit BOffImm(int offset) : data ((offset - 8) >> 2 & 0x00ffffff) { @@ -920,27 +959,36 @@ class BOffImm if (!IsInRange(offset)) CrashAtUnhandlableOOM("BOffImm"); } - static bool IsInRange(int offset) - { + + explicit BOffImm() + : data(INVALID) + { } + + private: + BOffImm(Instruction& inst); + + public: + static const int INVALID = 0x00800000; + + uint32_t encode() const { + return data; + } + int32_t decode() const { + return ((((int32_t)data) << 8) >> 6) + 8; + } + + static bool IsInRange(int offset) { if ((offset - 8) < -33554432) return false; if ((offset - 8) > 33554428) return false; return true; } - static const int INVALID = 0x00800000; - BOffImm() - : data(INVALID) - { } - bool isInvalid() { + bool isInvalid() const { return data == uint32_t(INVALID); } - Instruction* getDest(Instruction* src); - - private: - friend class InstBranchImm; - BOffImm(Instruction& inst); + Instruction* getDest(Instruction* src) const; }; class Imm16 @@ -951,18 +999,18 @@ class Imm16 uint32_t invalid : 12; public: - Imm16(); - Imm16(uint32_t imm); - Imm16(Instruction& inst); + explicit Imm16(); + explicit Imm16(uint32_t imm); + explicit Imm16(Instruction& inst); - uint32_t encode() { + uint32_t encode() const { return lower | upper << 16; } - uint32_t decode() { + uint32_t decode() const { return lower | upper << 12; } - bool isInvalid () { + bool isInvalid () const { return invalid; } }; @@ -988,26 +1036,27 @@ class Operand uint32_t data; public: - Operand (Register reg_) + explicit Operand(Register reg_) : Tag(OP2), reg(reg_.code()) { } - Operand (FloatRegister freg) + explicit Operand(FloatRegister freg) : Tag(FOP), reg(freg.code()) { } - Operand (Register base, Imm32 off) + explicit Operand(Register base, Imm32 off) : Tag(MEM), reg(base.code()), offset(off.value) { } - Operand (Register base, int32_t off) + explicit Operand(Register base, int32_t off) : Tag(MEM), reg(base.code()), offset(off) { } - Operand (const Address& addr) + explicit Operand(const Address& addr) : Tag(MEM), reg(addr.base.code()), offset(addr.offset) { } + public: Tag_ getTag() const { return Tag; } @@ -1028,6 +1077,7 @@ class Operand *dest = Imm32(offset); } Address toAddress() const { + MOZ_ASSERT(Tag == MEM); return Address(Register::FromCode(reg), offset); } int32_t disp() const { @@ -1052,6 +1102,7 @@ class Operand void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label); + static inline void PatchBackedge(CodeLocationJump& jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target) { @@ -1179,8 +1230,8 @@ class Assembler : public AssemblerShared static uint32_t GetNopFill(); static uint32_t AsmPoolMaxOffset; static uint32_t GetPoolMaxOffset(); - protected: + protected: // Structure for fixing up pc-relative loads/jumps when a the machine code // gets moved (executable copy, gc, etc.). struct RelativePatch @@ -1214,8 +1265,7 @@ class Assembler : public AssemblerShared isFinished(false), dtmActive(false), dtmCond(Always) - { - } + { } // We need to wait until an AutoJitContextAlloc is created by the // MacroAssembler, before allocating any space. @@ -1318,48 +1368,45 @@ class Assembler : public AssemblerShared void nopAlign(int alignment); BufferOffset as_nop(); BufferOffset as_alu(Register dest, Register src1, Operand2 op2, - ALUOp op, SetCond_ sc = NoSetCond, Condition c = Always); + ALUOp op, SBit s = LeaveCC, Condition c = Always); BufferOffset as_mov(Register dest, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); BufferOffset as_mvn(Register dest, Operand2 op2, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); static void as_alu_patch(Register dest, Register src1, Operand2 op2, - ALUOp op, SetCond_ sc, Condition c, uint32_t* pos); + ALUOp op, SBit s, Condition c, uint32_t* pos); static void as_mov_patch(Register dest, - Operand2 op2, SetCond_ sc, Condition c, uint32_t* pos); + Operand2 op2, SBit s, Condition c, uint32_t* pos); // Logical operations: BufferOffset as_and(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); BufferOffset as_bic(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); BufferOffset as_eor(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); BufferOffset as_orr(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); // Mathematical operations: BufferOffset as_adc(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); BufferOffset as_add(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); BufferOffset as_sbc(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); BufferOffset as_sub(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); BufferOffset as_rsb(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); BufferOffset as_rsc(Register dest, Register src1, - Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); + Operand2 op2, SBit s = LeaveCC, Condition c = Always); // Test operations: - BufferOffset as_cmn(Register src1, Operand2 op2, - Condition c = Always); - BufferOffset as_cmp(Register src1, Operand2 op2, - Condition c = Always); - BufferOffset as_teq(Register src1, Operand2 op2, - Condition c = Always); - BufferOffset as_tst(Register src1, Operand2 op2, - Condition c = Always); + BufferOffset as_cmn(Register src1, Operand2 op2, Condition c = Always); + BufferOffset as_cmp(Register src1, Operand2 op2, Condition c = Always); + BufferOffset as_teq(Register src1, Operand2 op2, Condition c = Always); + BufferOffset as_tst(Register src1, Operand2 op2, Condition c = Always); + // Sign extension operations: BufferOffset as_sxtb(Register dest, Register src, int rotate, Condition c = Always); BufferOffset as_sxth(Register dest, Register src, int rotate, Condition c = Always); @@ -1367,8 +1414,7 @@ class Assembler : public AssemblerShared BufferOffset as_uxth(Register dest, Register src, int rotate, Condition c = Always); // Not quite ALU worthy, but useful none the less: These also have the issue - // of these being formatted completly differently from the standard ALU - // operations. + // of these being formatted completly differently from the standard ALU operations. BufferOffset as_movw(Register dest, Imm16 imm, Condition c = Always); BufferOffset as_movt(Register dest, Imm16 imm, Condition c = Always); @@ -1376,23 +1422,23 @@ class Assembler : public AssemblerShared static void as_movt_patch(Register dest, Imm16 imm, Condition c, Instruction* pos); BufferOffset as_genmul(Register d1, Register d2, Register rm, Register rn, - MULOp op, SetCond_ sc, Condition c = Always); + MULOp op, SBit s, Condition c = Always); BufferOffset as_mul(Register dest, Register src1, Register src2, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); BufferOffset as_mla(Register dest, Register acc, Register src1, Register src2, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); BufferOffset as_umaal(Register dest1, Register dest2, Register src1, Register src2, Condition c = Always); BufferOffset as_mls(Register dest, Register acc, Register src1, Register src2, Condition c = Always); BufferOffset as_umull(Register dest1, Register dest2, Register src1, Register src2, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); BufferOffset as_umlal(Register dest1, Register dest2, Register src1, Register src2, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); BufferOffset as_smull(Register dest1, Register dest2, Register src1, Register src2, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); BufferOffset as_smlal(Register dest1, Register dest2, Register src1, Register src2, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); BufferOffset as_sdiv(Register dest, Register num, Register div, Condition c = Always); BufferOffset as_udiv(Register dest, Register num, Register div, Condition c = Always); @@ -1420,7 +1466,8 @@ class Assembler : public AssemblerShared // Load a 32 bit immediate from a pool into a register. BufferOffset as_Imm32Pool(Register dest, uint32_t value, Condition c = Always); // Make a patchable jump that can target the entire 32 bit address space. - BufferOffset as_BranchPool(uint32_t value, RepatchLabel* label, ARMBuffer::PoolEntry* pe = nullptr, Condition c = Always); + BufferOffset as_BranchPool(uint32_t value, RepatchLabel* label, + ARMBuffer::PoolEntry* pe = nullptr, Condition c = Always); // Load a 64 bit floating point immediate from a pool into a register. BufferOffset as_FImm64Pool(VFPRegister dest, double value, Condition c = Always); @@ -1444,10 +1491,8 @@ class Assembler : public AssemblerShared BufferOffset as_strexh(Register rd, Register rt, Register rn, Condition c = Always); BufferOffset as_strexb(Register rd, Register rt, Register rn, Condition c = Always); - // Memory synchronization: dmb, dsb, isb. - // + // Memory synchronization. // These are available from ARMv7 forward. - BufferOffset as_dmb(BarrierOption option = BarrierSY); BufferOffset as_dsb(BarrierOption option = BarrierSY); BufferOffset as_isb(); @@ -1487,9 +1532,9 @@ class Assembler : public AssemblerShared BufferOffset as_mrs(Register r, Condition c = Always); BufferOffset as_msr(Register r, Condition c = Always); + // VFP instructions! private: - enum vfp_size { IsDouble = 1 << 8, IsSingle = 0 << 8 @@ -1505,39 +1550,22 @@ class Assembler : public AssemblerShared VFPOp op, Condition c = Always); public: - BufferOffset as_vadd(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c = Always); - - BufferOffset as_vdiv(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c = Always); - - BufferOffset as_vmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c = Always); - - BufferOffset as_vnmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c = Always); - - BufferOffset as_vnmla(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c = Always); - - BufferOffset as_vnmls(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c = Always); - + BufferOffset as_vadd(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c = Always); + BufferOffset as_vdiv(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c = Always); + BufferOffset as_vmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c = Always); + BufferOffset as_vnmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c = Always); + BufferOffset as_vnmla(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c = Always); + BufferOffset as_vnmls(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c = Always); BufferOffset as_vneg(VFPRegister vd, VFPRegister vm, Condition c = Always); - BufferOffset as_vsqrt(VFPRegister vd, VFPRegister vm, Condition c = Always); - BufferOffset as_vabs(VFPRegister vd, VFPRegister vm, Condition c = Always); - - BufferOffset as_vsub(VFPRegister vd, VFPRegister vn, VFPRegister vm, - Condition c = Always); - - BufferOffset as_vcmp(VFPRegister vd, VFPRegister vm, - Condition c = Always); + BufferOffset as_vsub(VFPRegister vd, VFPRegister vn, VFPRegister vm, Condition c = Always); + BufferOffset as_vcmp(VFPRegister vd, VFPRegister vm, Condition c = Always); BufferOffset as_vcmpz(VFPRegister vd, Condition c = Always); // Specifically, a move between two same sized-registers. BufferOffset as_vmov(VFPRegister vd, VFPRegister vsrc, Condition c = Always); + // Transfer between Core and VFP. enum FloatToCore_ { FloatToCore = 1 << 20, @@ -1564,26 +1592,29 @@ class Assembler : public AssemblerShared // to uniquely specify the encoding that we are going to use. BufferOffset as_vcvt(VFPRegister vd, VFPRegister vm, bool useFPSCR = false, Condition c = Always); + // Hard coded to a 32 bit fixed width result for now. - BufferOffset as_vcvtFixed(VFPRegister vd, bool isSigned, uint32_t fixedPoint, bool toFixed, Condition c = Always); + BufferOffset as_vcvtFixed(VFPRegister vd, bool isSigned, uint32_t fixedPoint, + bool toFixed, Condition c = Always); // Transfer between VFP and memory. BufferOffset as_vdtr(LoadStore ls, VFPRegister vd, VFPAddr addr, Condition c = Always /* vfp doesn't have a wb option*/); static void as_vdtr_patch(LoadStore ls, VFPRegister vd, VFPAddr addr, - Condition c /* vfp doesn't have a wb option*/, uint32_t* dest); + Condition c /* vfp doesn't have a wb option */, uint32_t* dest); // VFP's ldm/stm work differently from the standard arm ones. You can only // transfer a range. BufferOffset as_vdtm(LoadStore st, Register rn, VFPRegister vd, int length, - /*also has update conditions*/Condition c = Always); + /* also has update conditions */ Condition c = Always); BufferOffset as_vimm(VFPRegister vd, VFPImm imm, Condition c = Always); BufferOffset as_vmrs(Register r, Condition c = Always); BufferOffset as_vmsr(Register r, Condition c = Always); + // Label operations. bool nextLink(BufferOffset b, BufferOffset* next); void bind(Label* label, BufferOffset boff = BufferOffset()); @@ -1911,6 +1942,7 @@ class InstLDR : public InstDTR InstLDR(Index mode, Register rt, DTRAddr addr, Assembler::Condition c) : InstDTR(IsLoad, IsWord, mode, rt, addr, c) { } + static bool IsTHIS(const Instruction& i); static InstLDR* AsTHIS(const Instruction& i); @@ -1939,13 +1971,17 @@ class InstBranchReg : public Instruction IsBX = 0x012fff10, IsBLX = 0x012fff30 }; + static const uint32_t IsBRegMask = 0x0ffffff0; + InstBranchReg(BranchTag tag, Register rm, Assembler::Condition c) : Instruction(tag | rm.code(), c) { } + public: static bool IsTHIS (const Instruction& i); static InstBranchReg* AsTHIS (const Instruction& i); + // Get the register that is being branched to void extractDest(Register* dest); // Make sure we are branching to a pre-known register @@ -1961,6 +1997,7 @@ class InstBranchImm : public Instruction IsB = 0x0a000000, IsBL = 0x0b000000 }; + static const uint32_t IsBImmMask = 0x0f000000; InstBranchImm(BranchTag tag, BOffImm off, Assembler::Condition c) @@ -1970,6 +2007,7 @@ class InstBranchImm : public Instruction public: static bool IsTHIS (const Instruction& i); static InstBranchImm* AsTHIS (const Instruction& i); + void extractImm(BOffImm* dest); }; JS_STATIC_ASSERT(sizeof(InstBranchImm) == sizeof(Instruction)); @@ -1981,6 +2019,7 @@ class InstBXReg : public InstBranchReg static bool IsTHIS (const Instruction& i); static InstBXReg* AsTHIS (const Instruction& i); }; + class InstBLXReg : public InstBranchReg { public: @@ -1991,6 +2030,7 @@ class InstBLXReg : public InstBranchReg static bool IsTHIS (const Instruction& i); static InstBLXReg* AsTHIS (const Instruction& i); }; + class InstBImm : public InstBranchImm { public: @@ -2001,6 +2041,7 @@ class InstBImm : public InstBranchImm static bool IsTHIS (const Instruction& i); static InstBImm* AsTHIS (const Instruction& i); }; + class InstBLImm : public InstBranchImm { public: @@ -2056,6 +2097,7 @@ class InstMovT : public InstMovWT InstMovT (Register rd, Imm16 imm, Assembler::Condition c) : InstMovWT(rd, imm, IsT, c) { } + static bool IsTHIS (const Instruction& i); static InstMovT* AsTHIS (const Instruction& i); }; @@ -2063,12 +2105,15 @@ class InstMovT : public InstMovWT class InstALU : public Instruction { static const int32_t ALUMask = 0xc << 24; + public: - InstALU (Register rd, Register rn, Operand2 op2, ALUOp op, SetCond_ sc, Assembler::Condition c) - : Instruction(maybeRD(rd) | maybeRN(rn) | op2.encode() | op | sc, c) + InstALU (Register rd, Register rn, Operand2 op2, ALUOp op, SBit s, Assembler::Condition c) + : Instruction(maybeRD(rd) | maybeRN(rn) | op2.encode() | op | s, c) { } + static bool IsTHIS (const Instruction& i); static InstALU* AsTHIS (const Instruction& i); + void extractOp(ALUOp* ret); bool checkOp(ALUOp op); void extractDest(Register* ret); @@ -2093,11 +2138,14 @@ class InstMOV : public InstALU }; -class InstructionIterator { +class InstructionIterator +{ private: Instruction* i; + public: - InstructionIterator(Instruction* i_); + explicit InstructionIterator(Instruction* i_); + Instruction* next() { i = i->next(); return cur(); @@ -2108,6 +2156,7 @@ class InstructionIterator { }; static const uint32_t NumIntArgRegs = 4; + // There are 16 *float* registers available for arguments // If doubles are used, only half the number of registers are available. static const uint32_t NumFloatArgRegs = 16; @@ -2117,6 +2166,7 @@ GetIntArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out) { if (usedIntArgs >= NumIntArgRegs) return false; + *out = Register::FromCode(usedIntArgs); return true; } @@ -2131,12 +2181,14 @@ GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out) { if (GetIntArgReg(usedIntArgs, usedFloatArgs, out)) return true; + // Unfortunately, we have to assume things about the point at which // GetIntArgReg returns false, because we need to know how many registers it // can allocate. usedIntArgs -= NumIntArgRegs; if (usedIntArgs >= NumCallTempNonArgRegs) return false; + *out = CallTempNonArgRegs[usedIntArgs]; return true; } @@ -2220,7 +2272,8 @@ GetDoubleArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t* pa -class DoubleEncoder { +class DoubleEncoder +{ struct DoubleEntry { uint32_t dblTop; @@ -2230,7 +2283,7 @@ class DoubleEncoder { static const DoubleEntry table[256]; public: - bool lookup(uint32_t top, datastore::Imm8VFPImmData* ret) { + bool lookup(uint32_t top, datastore::Imm8VFPImmData* ret) const { for (int i = 0; i < 256; i++) { if (table[i].dblTop == top) { *ret = table[i].data; @@ -2241,8 +2294,10 @@ class DoubleEncoder { } }; -class AutoForbidPools { +class AutoForbidPools +{ Assembler* masm_; + public: // The maxInst argument is the maximum number of word sized instructions // that will be allocated within this context. It is used to determine if @@ -2251,9 +2306,12 @@ class AutoForbidPools { // // Allocation of pool entries is not supported within this content so the // code can not use large integers or float constants etc. - AutoForbidPools(Assembler* masm, size_t maxInst) : masm_(masm) { + AutoForbidPools(Assembler* masm, size_t maxInst) + : masm_(masm) + { masm_->enterNoPool(maxInst); } + ~AutoForbidPools() { masm_->leaveNoPool(); } diff --git a/js/src/jit/arm/BaselineIC-arm.cpp b/js/src/jit/arm/BaselineIC-arm.cpp index 91153b1ae065..bc7b51b9d973 100644 --- a/js/src/jit/arm/BaselineIC-arm.cpp +++ b/js/src/jit/arm/BaselineIC-arm.cpp @@ -29,8 +29,8 @@ ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm) // Compare payload regs of R0 and R1. Assembler::Condition cond = JSOpToCondition(op, /* signed = */true); masm.cmp32(R0.payloadReg(), R1.payloadReg()); - masm.ma_mov(Imm32(1), R0.payloadReg(), NoSetCond, cond); - masm.ma_mov(Imm32(0), R0.payloadReg(), NoSetCond, Assembler::InvertCondition(cond)); + masm.ma_mov(Imm32(1), R0.payloadReg(), LeaveCC, cond); + masm.ma_mov(Imm32(0), R0.payloadReg(), LeaveCC, Assembler::InvertCondition(cond)); // Result is implicitly boxed already. masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0); @@ -57,7 +57,7 @@ ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm) masm.compareDouble(FloatReg0, FloatReg1); masm.ma_mov(Imm32(0), dest); - masm.ma_mov(Imm32(1), dest, NoSetCond, cond); + masm.ma_mov(Imm32(1), dest, LeaveCC, cond); masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0); EmitReturnFromIC(masm); @@ -93,7 +93,7 @@ ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm) Label maybeNegZero, revertRegister; switch(op_) { case JSOP_ADD: - masm.ma_add(R0.payloadReg(), R1.payloadReg(), scratchReg, SetCond); + masm.ma_add(R0.payloadReg(), R1.payloadReg(), scratchReg, SetCC); // Just jump to failure on overflow. R0 and R1 are preserved, so we can // just jump to the next stub. @@ -104,7 +104,7 @@ ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm) masm.mov(scratchReg, R0.payloadReg()); break; case JSOP_SUB: - masm.ma_sub(R0.payloadReg(), R1.payloadReg(), scratchReg, SetCond); + masm.ma_sub(R0.payloadReg(), R1.payloadReg(), scratchReg, SetCC); masm.j(Assembler::Overflow, &failure); masm.mov(scratchReg, R0.payloadReg()); break; diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index 5694e2525ffc..4f37397aaf73 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -135,7 +135,7 @@ CodeGeneratorARM::visitCompare(LCompare* comp) else masm.ma_cmp(ToRegister(left), ToOperand(right)); masm.ma_mov(Imm32(0), ToRegister(def)); - masm.ma_mov(Imm32(1), ToRegister(def), NoSetCond, cond); + masm.ma_mov(Imm32(1), ToRegister(def), LeaveCC, cond); } void @@ -380,9 +380,9 @@ CodeGeneratorARM::visitAddI(LAddI* ins) const LDefinition* dest = ins->getDef(0); if (rhs->isConstant()) - masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCond); + masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC); else - masm.ma_add(ToRegister(lhs), ToOperand(rhs), ToRegister(dest), SetCond); + masm.ma_add(ToRegister(lhs), ToOperand(rhs), ToRegister(dest), SetCC); if (ins->snapshot()) bailoutIf(Assembler::Overflow, ins->snapshot()); @@ -396,9 +396,9 @@ CodeGeneratorARM::visitSubI(LSubI* ins) const LDefinition* dest = ins->getDef(0); if (rhs->isConstant()) - masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCond); + masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC); else - masm.ma_sub(ToRegister(lhs), ToOperand(rhs), ToRegister(dest), SetCond); + masm.ma_sub(ToRegister(lhs), ToOperand(rhs), ToRegister(dest), SetCC); if (ins->snapshot()) bailoutIf(Assembler::Overflow, ins->snapshot()); @@ -426,7 +426,7 @@ CodeGeneratorARM::visitMulI(LMulI* ins) // TODO: move these to ma_mul. switch (constant) { case -1: - masm.ma_rsb(ToRegister(lhs), Imm32(0), ToRegister(dest), SetCond); + masm.ma_rsb(ToRegister(lhs), Imm32(0), ToRegister(dest), SetCC); break; case 0: masm.ma_mov(Imm32(0), ToRegister(dest)); @@ -436,7 +436,7 @@ CodeGeneratorARM::visitMulI(LMulI* ins) masm.ma_mov(ToRegister(lhs), ToRegister(dest)); return; // Escape overflow check; case 2: - masm.ma_add(ToRegister(lhs), ToRegister(lhs), ToRegister(dest), SetCond); + masm.ma_add(ToRegister(lhs), ToRegister(lhs), ToRegister(dest), SetCC); // Overflow is handled later. break; default: { @@ -646,7 +646,7 @@ CodeGeneratorARM::visitDivPowTwoI(LDivPowTwoI* ins) MDiv* mir = ins->mir(); if (!mir->isTruncated()) { // If the remainder is != 0, bailout since this must be a double. - masm.as_mov(ScratchRegister, lsl(lhs, 32 - shift), SetCond); + masm.as_mov(ScratchRegister, lsl(lhs, 32 - shift), SetCC); bailoutIf(Assembler::NonZero, ins->snapshot()); } @@ -811,11 +811,11 @@ CodeGeneratorARM::visitModPowTwoI(LModPowTwoI* ins) Label fin; // bug 739870, jbramley has a different sequence that may help with speed // here. - masm.ma_mov(in, out, SetCond); + masm.ma_mov(in, out, SetCC); masm.ma_b(&fin, Assembler::Zero); - masm.ma_rsb(Imm32(0), out, NoSetCond, Assembler::Signed); + masm.ma_rsb(Imm32(0), out, LeaveCC, Assembler::Signed); masm.ma_and(Imm32((1 << ins->shift()) - 1), out); - masm.ma_rsb(Imm32(0), out, SetCond, Assembler::Signed); + masm.ma_rsb(Imm32(0), out, SetCC, Assembler::Signed); if (mir->canBeNegativeDividend()) { if (!mir->isTruncated()) { MOZ_ASSERT(mir->fallible()); @@ -1100,8 +1100,8 @@ CodeGeneratorARM::emitTableSwitchDispatch(MTableSwitch* mir, Register index, Reg int32_t cases = mir->numCases(); // Lower value with low value. - masm.ma_sub(index, Imm32(mir->low()), index, SetCond); - masm.ma_rsb(index, Imm32(cases - 1), index, SetCond, Assembler::NotSigned); + masm.ma_sub(index, Imm32(mir->low()), index, SetCC); + masm.ma_rsb(index, Imm32(cases - 1), index, SetCC, Assembler::NotSigned); // Inhibit pools within the following sequence because we are indexing into // a pc relative table. The region will have one instruction for ma_ldr, one // for ma_b, and each table case takes one word. @@ -1612,8 +1612,8 @@ CodeGeneratorARM::visitNotD(LNotD* ins) } else { masm.as_vmrs(pc); masm.ma_mov(Imm32(0), dest); - masm.ma_mov(Imm32(1), dest, NoSetCond, Assembler::Equal); - masm.ma_mov(Imm32(1), dest, NoSetCond, Assembler::Overflow); + masm.ma_mov(Imm32(1), dest, LeaveCC, Assembler::Equal); + masm.ma_mov(Imm32(1), dest, LeaveCC, Assembler::Overflow); } } @@ -1640,8 +1640,8 @@ CodeGeneratorARM::visitNotF(LNotF* ins) } else { masm.as_vmrs(pc); masm.ma_mov(Imm32(0), dest); - masm.ma_mov(Imm32(1), dest, NoSetCond, Assembler::Equal); - masm.ma_mov(Imm32(1), dest, NoSetCond, Assembler::Overflow); + masm.ma_mov(Imm32(1), dest, LeaveCC, Assembler::Equal); + masm.ma_mov(Imm32(1), dest, LeaveCC, Assembler::Overflow); } } @@ -1793,9 +1793,9 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) if (isFloat) { VFPRegister vd(ToFloatRegister(ins->output())); if (size == 32) - masm.ma_vldr(Operand(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always); + masm.ma_vldr(Address(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always); else - masm.ma_vldr(Operand(HeapReg, ptrImm), vd, Assembler::Always); + masm.ma_vldr(Address(HeapReg, ptrImm), vd, Assembler::Always); } else { masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm), ToRegister(ins->output()), Offset, Assembler::Always); @@ -1826,17 +1826,17 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) FloatRegister dst = ToFloatRegister(ins->output()); VFPRegister vd(dst); if (size == 32) { - masm.ma_vldr(Operand(GlobalReg, AsmJSNaN32GlobalDataOffset - AsmJSGlobalRegBias), + masm.ma_vldr(Address(GlobalReg, AsmJSNaN32GlobalDataOffset - AsmJSGlobalRegBias), vd.singleOverlay(), Assembler::AboveOrEqual); masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below); } else { - masm.ma_vldr(Operand(GlobalReg, AsmJSNaN64GlobalDataOffset - AsmJSGlobalRegBias), + masm.ma_vldr(Address(GlobalReg, AsmJSNaN64GlobalDataOffset - AsmJSGlobalRegBias), vd, Assembler::AboveOrEqual); masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Below); } } else { Register d = ToRegister(ins->output()); - masm.ma_mov(Imm32(0), d, NoSetCond, Assembler::AboveOrEqual); + masm.ma_mov(Imm32(0), d, LeaveCC, Assembler::AboveOrEqual); masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, d, Offset, Assembler::Below); } memoryBarrier(mir->barrierAfter()); @@ -1870,9 +1870,9 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) if (isFloat) { VFPRegister vd(ToFloatRegister(ins->value())); if (size == 32) - masm.ma_vstr(vd.singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always); + masm.ma_vstr(vd.singleOverlay(), Address(HeapReg, ptrImm), Assembler::Always); else - masm.ma_vstr(vd, Operand(HeapReg, ptrImm), Assembler::Always); + masm.ma_vstr(vd, Address(HeapReg, ptrImm), Assembler::Always); } else { masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm), ToRegister(ins->value()), Offset, Assembler::Always); @@ -2089,7 +2089,7 @@ void CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins) { const MAsmJSPassStackArg* mir = ins->mir(); - Operand dst(StackPointer, mir->spOffset()); + Address dst(StackPointer, mir->spOffset()); if (ins->arg()->isConstant()) { //masm.as_bkpt(); masm.ma_storeImm(Imm32(ToInt32(ins->arg())), dst); @@ -2231,9 +2231,9 @@ CodeGeneratorARM::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output())); } else if (mir->type() == MIRType_Float32) { VFPRegister vd(ToFloatRegister(ins->output())); - masm.ma_vldr(Operand(GlobalReg, addr), vd.singleOverlay()); + masm.ma_vldr(Address(GlobalReg, addr), vd.singleOverlay()); } else { - masm.ma_vldr(Operand(GlobalReg, addr), ToFloatRegister(ins->output())); + masm.ma_vldr(Address(GlobalReg, addr), ToFloatRegister(ins->output())); } } @@ -2250,9 +2250,9 @@ CodeGeneratorARM::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins) masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value())); } else if (type == MIRType_Float32) { VFPRegister vd(ToFloatRegister(ins->value())); - masm.ma_vstr(vd.singleOverlay(), Operand(GlobalReg, addr)); + masm.ma_vstr(vd.singleOverlay(), Address(GlobalReg, addr)); } else { - masm.ma_vstr(ToFloatRegister(ins->value()), Operand(GlobalReg, addr)); + masm.ma_vstr(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); } } @@ -2275,7 +2275,7 @@ CodeGeneratorARM::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins) { const MAsmJSLoadFFIFunc* mir = ins->mir(); - masm.ma_ldr(Operand(GlobalReg, mir->globalDataOffset() - AsmJSGlobalRegBias), + masm.ma_ldr(Address(GlobalReg, mir->globalDataOffset() - AsmJSGlobalRegBias), ToRegister(ins->output())); } diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 222125d59215..5afd47c989e3 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -57,7 +57,7 @@ MacroAssemblerARM::convertInt32ToDouble(Register src, FloatRegister dest_) void MacroAssemblerARM::convertInt32ToDouble(const Address& src, FloatRegister dest) { - ma_vldr(Operand(src), ScratchDoubleReg); + ma_vldr(src, ScratchDoubleReg); as_vcvt(dest, VFPRegister(ScratchDoubleReg).sintOverlay()); } @@ -214,7 +214,7 @@ MacroAssemblerARM::convertInt32ToFloat32(Register src, FloatRegister dest) { void MacroAssemblerARM::convertInt32ToFloat32(const Address& src, FloatRegister dest) { - ma_vldr(Operand(src), ScratchFloat32Reg); + ma_vldr(src, ScratchFloat32Reg); as_vcvt(dest, VFPRegister(ScratchFloat32Reg).sintOverlay()); } @@ -258,8 +258,8 @@ MacroAssemblerARM::inc64(AbsoluteAddress dest) ma_ldrd(EDtrAddr(ScratchRegister, EDtrOffImm(0)), r0, r1); - ma_add(Imm32(1), r0, SetCond); - ma_adc(Imm32(0), r1, NoSetCond); + ma_add(Imm32(1), r0, SetCC); + ma_adc(Imm32(0), r1, LeaveCC); ma_strd(r0, r1, EDtrAddr(ScratchRegister, EDtrOffImm(0))); @@ -269,9 +269,9 @@ MacroAssemblerARM::inc64(AbsoluteAddress dest) bool MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op, - SetCond_ sc, Condition c) + SBit s, Condition c) { - if ((sc == SetCond && ! condsAreSafe(op)) || !can_dbl(op)) + if ((s == SetCC && ! condsAreSafe(op)) || !can_dbl(op)) return false; ALUOp interop = getDestVariant(op); Imm8::TwoImm8mData both = Imm8::EncodeTwoImms(imm.value); @@ -282,8 +282,8 @@ MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op, // doesn't have a dest, such as check for overflow by doing first operation // don't do second operation if first operation overflowed. This preserves // the overflow condition code. Unfortunately, it is horribly brittle. - as_alu(ScratchRegister, src1, both.fst, interop, NoSetCond, c); - as_alu(dest, ScratchRegister, both.snd, op, sc, c); + as_alu(ScratchRegister, src1, Operand2(both.fst), interop, LeaveCC, c); + as_alu(dest, ScratchRegister, Operand2(both.snd), op, s, c); return true; } @@ -291,18 +291,18 @@ MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op, void MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, ALUOp op, - SetCond_ sc, Condition c) + SBit s, Condition c) { // As it turns out, if you ask for a compare-like instruction you *probably* // want it to set condition codes. if (dest == InvalidReg) - MOZ_ASSERT(sc == SetCond); + MOZ_ASSERT(s == SetCC); // The operator gives us the ability to determine how this can be used. Imm8 imm8 = Imm8(imm.value); // One instruction: If we can encode it using an imm8m, then do so. if (!imm8.invalid) { - as_alu(dest, src1, imm8, op, sc, c); + as_alu(dest, src1, imm8, op, s, c); return; } // One instruction, negated: @@ -317,7 +317,7 @@ MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, // but it will need to clobber *something*, and the scratch register isn't // being used, so... if (negOp != OpInvalid && !negImm8.invalid) { - as_alu(negDest, src1, negImm8, negOp, sc, c); + as_alu(negDest, src1, negImm8, negOp, s, c); return; } @@ -326,13 +326,13 @@ MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, // the bits into the destination. Otherwise, we'll need to fall back on // a multi-instruction format :( // movw/movt does not set condition codes, so don't hold your breath. - if (sc == NoSetCond && (op == OpMov || op == OpMvn)) { + if (s == LeaveCC && (op == OpMov || op == OpMvn)) { // ARMv7 supports movw/movt. movw zero-extends its 16 bit argument, // so we can set the register this way. movt leaves the bottom 16 // bits in tact, so it is unsuitable to move a constant that if (op == OpMov && ((imm.value & ~ 0xffff) == 0)) { MOZ_ASSERT(src1 == InvalidReg); - as_movw(dest, (uint16_t)imm.value, c); + as_movw(dest, Imm16((uint16_t)imm.value), c); return; } @@ -340,7 +340,7 @@ MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, // then do it. if (op == OpMvn && (((~imm.value) & ~ 0xffff) == 0)) { MOZ_ASSERT(src1 == InvalidReg); - as_movw(dest, (uint16_t)~imm.value, c); + as_movw(dest, Imm16((uint16_t)~imm.value), c); return; } @@ -354,8 +354,8 @@ MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, // src1. if (op == OpMvn) imm.value = ~imm.value; - as_movw(dest, imm.value & 0xffff, c); - as_movt(dest, (imm.value >> 16) & 0xffff, c); + as_movw(dest, Imm16(imm.value & 0xffff), c); + as_movt(dest, Imm16((imm.value >> 16) & 0xffff), c); return; } // If we weren't doing a movalike, a 16 bit immediate will require 2 @@ -380,21 +380,21 @@ MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, // assume that the overflow flag will be checked and add{,s} dest, src, // 0xff00; add{,s} dest, dest, 0xff is not guaranteed to set the overflow // flag the same as the (theoretical) one instruction variant. - if (alu_dbl(src1, imm, dest, op, sc, c)) + if (alu_dbl(src1, imm, dest, op, s, c)) return; // And try with its negative. if (negOp != OpInvalid && - alu_dbl(src1, negImm, negDest, negOp, sc, c)) + alu_dbl(src1, negImm, negDest, negOp, s, c)) return; // Well, damn. We can use two 16 bit mov's, then do the op or we can do a // single load from a pool then op. if (HasMOVWT()) { // Try to load the immediate into a scratch register then use that - as_movw(ScratchRegister, imm.value & 0xffff, c); + as_movw(ScratchRegister, Imm16(imm.value & 0xffff), c); if ((imm.value >> 16) != 0) - as_movt(ScratchRegister, (imm.value >> 16) & 0xffff, c); + as_movt(ScratchRegister, Imm16((imm.value >> 16) & 0xffff), c); } else { // Going to have to use a load. If the operation is a move, then just // move it into the destination register @@ -407,21 +407,21 @@ MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, as_Imm32Pool(ScratchRegister, imm.value, c); } } - as_alu(dest, src1, O2Reg(ScratchRegister), op, sc, c); + as_alu(dest, src1, O2Reg(ScratchRegister), op, s, c); } void MacroAssemblerARM::ma_alu(Register src1, Operand op2, Register dest, ALUOp op, - SetCond_ sc, Assembler::Condition c) + SBit s, Assembler::Condition c) { MOZ_ASSERT(op2.getTag() == Operand::OP2); - as_alu(dest, src1, op2.toOp2(), op, sc, c); + as_alu(dest, src1, op2.toOp2(), op, s, c); } void -MacroAssemblerARM::ma_alu(Register src1, Operand2 op2, Register dest, ALUOp op, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_alu(Register src1, Operand2 op2, Register dest, ALUOp op, SBit s, Condition c) { - as_alu(dest, src1, op2, op, sc, c); + as_alu(dest, src1, op2, op, s, c); } void @@ -484,24 +484,24 @@ MacroAssemblerARM::ma_mov_patch(ImmPtr imm, Register dest, Assembler::Condition } void -MacroAssemblerARM::ma_mov(Register src, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_mov(Register src, Register dest, SBit s, Assembler::Condition c) { - if (sc == SetCond || dest != src) - as_mov(dest, O2Reg(src), sc, c); + if (s == SetCC || dest != src) + as_mov(dest, O2Reg(src), s, c); } void MacroAssemblerARM::ma_mov(Imm32 imm, Register dest, - SetCond_ sc, Assembler::Condition c) + SBit s, Assembler::Condition c) { - ma_alu(InvalidReg, imm, dest, OpMov, sc, c); + ma_alu(InvalidReg, imm, dest, OpMov, s, c); } void MacroAssemblerARM::ma_mov(ImmWord imm, Register dest, - SetCond_ sc, Assembler::Condition c) + SBit s, Assembler::Condition c) { - ma_alu(InvalidReg, Imm32(imm.value), dest, OpMov, sc, c); + ma_alu(InvalidReg, Imm32(imm.value), dest, OpMov, s, c); } void @@ -576,230 +576,230 @@ MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst) // Move not (dest <- ~src) void -MacroAssemblerARM::ma_mvn(Imm32 imm, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_mvn(Imm32 imm, Register dest, SBit s, Assembler::Condition c) { - ma_alu(InvalidReg, imm, dest, OpMvn, sc, c); + ma_alu(InvalidReg, imm, dest, OpMvn, s, c); } void -MacroAssemblerARM::ma_mvn(Register src1, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_mvn(Register src1, Register dest, SBit s, Assembler::Condition c) { - as_alu(dest, InvalidReg, O2Reg(src1), OpMvn, sc, c); + as_alu(dest, InvalidReg, O2Reg(src1), OpMvn, s, c); } // Negate (dest <- -src), src is a register, rather than a general op2. void -MacroAssemblerARM::ma_neg(Register src1, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_neg(Register src1, Register dest, SBit s, Assembler::Condition c) { - as_rsb(dest, src1, Imm8(0), sc, c); + as_rsb(dest, src1, Imm8(0), s, c); } // And. void -MacroAssemblerARM::ma_and(Register src, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_and(Register src, Register dest, SBit s, Assembler::Condition c) { ma_and(dest, src, dest); } void MacroAssemblerARM::ma_and(Register src1, Register src2, Register dest, - SetCond_ sc, Assembler::Condition c) + SBit s, Assembler::Condition c) { - as_and(dest, src1, O2Reg(src2), sc, c); + as_and(dest, src1, O2Reg(src2), s, c); } void -MacroAssemblerARM::ma_and(Imm32 imm, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_and(Imm32 imm, Register dest, SBit s, Assembler::Condition c) { - ma_alu(dest, imm, dest, OpAnd, sc, c); + ma_alu(dest, imm, dest, OpAnd, s, c); } void MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest, - SetCond_ sc, Assembler::Condition c) + SBit s, Assembler::Condition c) { - ma_alu(src1, imm, dest, OpAnd, sc, c); + ma_alu(src1, imm, dest, OpAnd, s, c); } // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2). void -MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, SBit s, Assembler::Condition c) { - ma_alu(dest, imm, dest, OpBic, sc, c); + ma_alu(dest, imm, dest, OpBic, s, c); } // Exclusive or. void -MacroAssemblerARM::ma_eor(Register src, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_eor(Register src, Register dest, SBit s, Assembler::Condition c) { - ma_eor(dest, src, dest, sc, c); + ma_eor(dest, src, dest, s, c); } void MacroAssemblerARM::ma_eor(Register src1, Register src2, Register dest, - SetCond_ sc, Assembler::Condition c) + SBit s, Assembler::Condition c) { - as_eor(dest, src1, O2Reg(src2), sc, c); + as_eor(dest, src1, O2Reg(src2), s, c); } void -MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, SBit s, Assembler::Condition c) { - ma_alu(dest, imm, dest, OpEor, sc, c); + ma_alu(dest, imm, dest, OpEor, s, c); } void MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest, - SetCond_ sc, Assembler::Condition c) + SBit s, Assembler::Condition c) { - ma_alu(src1, imm, dest, OpEor, sc, c); + ma_alu(src1, imm, dest, OpEor, s, c); } // Or. void -MacroAssemblerARM::ma_orr(Register src, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_orr(Register src, Register dest, SBit s, Assembler::Condition c) { - ma_orr(dest, src, dest, sc, c); + ma_orr(dest, src, dest, s, c); } void MacroAssemblerARM::ma_orr(Register src1, Register src2, Register dest, - SetCond_ sc, Assembler::Condition c) + SBit s, Assembler::Condition c) { - as_orr(dest, src1, O2Reg(src2), sc, c); + as_orr(dest, src1, O2Reg(src2), s, c); } void -MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, SetCond_ sc, Assembler::Condition c) +MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, SBit s, Assembler::Condition c) { - ma_alu(dest, imm, dest, OpOrr, sc, c); + ma_alu(dest, imm, dest, OpOrr, s, c); } void MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest, - SetCond_ sc, Assembler::Condition c) + SBit s, Assembler::Condition c) { - ma_alu(src1, imm, dest, OpOrr, sc, c); + ma_alu(src1, imm, dest, OpOrr, s, c); } // Arithmetic-based ops. // Add with carry. void -MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, SBit s, Condition c) { - ma_alu(dest, imm, dest, OpAdc, sc, c); + ma_alu(dest, imm, dest, OpAdc, s, c); } void -MacroAssemblerARM::ma_adc(Register src, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_adc(Register src, Register dest, SBit s, Condition c) { - as_alu(dest, dest, O2Reg(src), OpAdc, sc, c); + as_alu(dest, dest, O2Reg(src), OpAdc, s, c); } void -MacroAssemblerARM::ma_adc(Register src1, Register src2, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_adc(Register src1, Register src2, Register dest, SBit s, Condition c) { - as_alu(dest, src1, O2Reg(src2), OpAdc, sc, c); + as_alu(dest, src1, O2Reg(src2), OpAdc, s, c); } // Add. void -MacroAssemblerARM::ma_add(Imm32 imm, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_add(Imm32 imm, Register dest, SBit s, Condition c) { - ma_alu(dest, imm, dest, OpAdd, sc, c); + ma_alu(dest, imm, dest, OpAdd, s, c); } void -MacroAssemblerARM::ma_add(Register src1, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_add(Register src1, Register dest, SBit s, Condition c) { - ma_alu(dest, O2Reg(src1), dest, OpAdd, sc, c); + ma_alu(dest, O2Reg(src1), dest, OpAdd, s, c); } void -MacroAssemblerARM::ma_add(Register src1, Register src2, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_add(Register src1, Register src2, Register dest, SBit s, Condition c) { - as_alu(dest, src1, O2Reg(src2), OpAdd, sc, c); + as_alu(dest, src1, O2Reg(src2), OpAdd, s, c); } void -MacroAssemblerARM::ma_add(Register src1, Operand op, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_add(Register src1, Operand op, Register dest, SBit s, Condition c) { - ma_alu(src1, op, dest, OpAdd, sc, c); + ma_alu(src1, op, dest, OpAdd, s, c); } void -MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, SBit s, Condition c) { - ma_alu(src1, op, dest, OpAdd, sc, c); + ma_alu(src1, op, dest, OpAdd, s, c); } // Subtract with carry. void -MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, SBit s, Condition c) { - ma_alu(dest, imm, dest, OpSbc, sc, c); + ma_alu(dest, imm, dest, OpSbc, s, c); } void -MacroAssemblerARM::ma_sbc(Register src1, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_sbc(Register src1, Register dest, SBit s, Condition c) { - as_alu(dest, dest, O2Reg(src1), OpSbc, sc, c); + as_alu(dest, dest, O2Reg(src1), OpSbc, s, c); } void -MacroAssemblerARM::ma_sbc(Register src1, Register src2, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_sbc(Register src1, Register src2, Register dest, SBit s, Condition c) { - as_alu(dest, src1, O2Reg(src2), OpSbc, sc, c); + as_alu(dest, src1, O2Reg(src2), OpSbc, s, c); } // Subtract. void -MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, SBit s, Condition c) { - ma_alu(dest, imm, dest, OpSub, sc, c); + ma_alu(dest, imm, dest, OpSub, s, c); } void -MacroAssemblerARM::ma_sub(Register src1, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_sub(Register src1, Register dest, SBit s, Condition c) { - ma_alu(dest, Operand(src1), dest, OpSub, sc, c); + ma_alu(dest, Operand(src1), dest, OpSub, s, c); } void -MacroAssemblerARM::ma_sub(Register src1, Register src2, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_sub(Register src1, Register src2, Register dest, SBit s, Condition c) { - ma_alu(src1, Operand(src2), dest, OpSub, sc, c); + ma_alu(src1, Operand(src2), dest, OpSub, s, c); } void -MacroAssemblerARM::ma_sub(Register src1, Operand op, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_sub(Register src1, Operand op, Register dest, SBit s, Condition c) { - ma_alu(src1, op, dest, OpSub, sc, c); + ma_alu(src1, op, dest, OpSub, s, c); } void -MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, SBit s, Condition c) { - ma_alu(src1, op, dest, OpSub, sc, c); + ma_alu(src1, op, dest, OpSub, s, c); } // Severse subtract. void -MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, SBit s, Condition c) { - ma_alu(dest, imm, dest, OpRsb, sc, c); + ma_alu(dest, imm, dest, OpRsb, s, c); } void -MacroAssemblerARM::ma_rsb(Register src1, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_rsb(Register src1, Register dest, SBit s, Condition c) { - as_alu(dest, dest, O2Reg(src1), OpAdd, sc, c); + as_alu(dest, dest, O2Reg(src1), OpAdd, s, c); } void -MacroAssemblerARM::ma_rsb(Register src1, Register src2, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_rsb(Register src1, Register src2, Register dest, SBit s, Condition c) { - as_alu(dest, src1, O2Reg(src2), OpRsb, sc, c); + as_alu(dest, src1, O2Reg(src2), OpRsb, s, c); } void -MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, SBit s, Condition c) { - ma_alu(src1, op2, dest, OpRsb, sc, c); + ma_alu(src1, op2, dest, OpRsb, s, c); } // Reverse subtract with carry. void -MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, SBit s, Condition c) { - ma_alu(dest, imm, dest, OpRsc, sc, c); + ma_alu(dest, imm, dest, OpRsc, s, c); } void -MacroAssemblerARM::ma_rsc(Register src1, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_rsc(Register src1, Register dest, SBit s, Condition c) { - as_alu(dest, dest, O2Reg(src1), OpRsc, sc, c); + as_alu(dest, dest, O2Reg(src1), OpRsc, s, c); } void -MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest, SetCond_ sc, Condition c) +MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest, SBit s, Condition c) { - as_alu(dest, src1, O2Reg(src2), OpRsc, sc, c); + as_alu(dest, src1, O2Reg(src2), OpRsc, s, c); } // Compares/tests. @@ -807,12 +807,12 @@ MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest, SetCond_ void MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, Condition c) { - ma_alu(src1, imm, InvalidReg, OpCmn, SetCond, c); + ma_alu(src1, imm, InvalidReg, OpCmn, SetCC, c); } void MacroAssemblerARM::ma_cmn(Register src1, Register src2, Condition c) { - as_alu(InvalidReg, src2, O2Reg(src1), OpCmn, SetCond, c); + as_alu(InvalidReg, src2, O2Reg(src1), OpCmn, SetCC, c); } void MacroAssemblerARM::ma_cmn(Register src1, Operand op, Condition c) @@ -824,7 +824,7 @@ MacroAssemblerARM::ma_cmn(Register src1, Operand op, Condition c) void MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm, Condition c) { - ma_alu(src1, imm, InvalidReg, OpCmp, SetCond, c); + ma_alu(src1, imm, InvalidReg, OpCmp, SetCC, c); } void @@ -847,7 +847,7 @@ MacroAssemblerARM::ma_cmp(Register src1, Operand op, Condition c) as_cmp(src1, op.toOp2(), c); break; case Operand::MEM: - ma_ldr(op, ScratchRegister); + ma_ldr(op.toAddress(), ScratchRegister); as_cmp(src1, O2Reg(ScratchRegister), c); break; default: @@ -864,7 +864,7 @@ MacroAssemblerARM::ma_cmp(Register src1, Register src2, Condition c) void MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, Condition c) { - ma_alu(src1, imm, InvalidReg, OpTeq, SetCond, c); + ma_alu(src1, imm, InvalidReg, OpTeq, SetCC, c); } void MacroAssemblerARM::ma_teq(Register src1, Register src2, Condition c) @@ -882,7 +882,7 @@ MacroAssemblerARM::ma_teq(Register src1, Operand op, Condition c) void MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, Condition c) { - ma_alu(src1, imm, InvalidReg, OpTst, SetCond, c); + ma_alu(src1, imm, InvalidReg, OpTst, SetCC, c); } void MacroAssemblerARM::ma_tst(Register src1, Register src2, Condition c) @@ -914,7 +914,7 @@ MacroAssemblerARM::ma_check_mul(Register src1, Register src2, Register dest, Con // TODO: this operation is illegal on armv6 and earlier if src2 == // ScratchRegister or src2 == dest. if (cond == Equal || cond == NotEqual) { - as_smull(ScratchRegister, dest, src1, src2, SetCond); + as_smull(ScratchRegister, dest, src1, src2, SetCC); return cond; } @@ -932,7 +932,7 @@ MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm, Register dest, Conditi { ma_mov(imm, ScratchRegister); if (cond == Equal || cond == NotEqual) { - as_smull(ScratchRegister, dest, ScratchRegister, src1, SetCond); + as_smull(ScratchRegister, dest, ScratchRegister, src1, SetCC); return cond; } @@ -979,13 +979,13 @@ MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold, Regis // Note that we cannot use ScratchRegister in place of tmp here, as ma_and // below on certain architectures move the mask into ScratchRegister before // performing the bitwise and. - as_mov(tmp, O2Reg(src), SetCond); + as_mov(tmp, O2Reg(src), SetCC); // Zero out the dest. ma_mov(Imm32(0), dest); // Set the hold appropriately. ma_mov(Imm32(1), hold); - ma_mov(Imm32(-1), hold, NoSetCond, Signed); - ma_rsb(Imm32(0), tmp, SetCond, Signed); + ma_mov(Imm32(-1), hold, LeaveCC, Signed); + ma_rsb(Imm32(0), tmp, SetCC, Signed); // Begin the main loop. bind(&head); @@ -995,11 +995,11 @@ MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold, Regis ma_add(secondScratchReg_, dest, dest); // Do a trial subtraction, this is the same operation as cmp, but we store // the dest. - ma_sub(dest, Imm32(mask), secondScratchReg_, SetCond); + ma_sub(dest, Imm32(mask), secondScratchReg_, SetCC); // If (sum - C) > 0, store sum - C back into sum, thus performing a modulus. - ma_mov(secondScratchReg_, dest, NoSetCond, NotSigned); + ma_mov(secondScratchReg_, dest, LeaveCC, NotSigned); // Get rid of the bits that we extracted before, and set the condition codes. - as_mov(tmp, lsr(tmp, shift), SetCond); + as_mov(tmp, lsr(tmp, shift), SetCC); // If the shift produced zero, finish, otherwise, continue in the loop. ma_b(&head, NonZero); // Check the hold to see if we need to negate the result. Hold can only be @@ -1007,7 +1007,7 @@ MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold, Regis ma_cmp(hold, Imm32(0)); // If the hold was non-zero, negate the result to be in line with what JS // wants this will set the condition codes if we try to negate. - ma_rsb(Imm32(0), dest, SetCond, Signed); + ma_rsb(Imm32(0), dest, SetCC, Signed); // Since the Zero flag is not set by the compare, we can *only* set the Zero // flag in the rsb, so Zero is set iff we negated zero (e.g. the result of // the computation was -0.0). @@ -1068,15 +1068,13 @@ MacroAssemblerARM::ma_str(Register rt, DTRAddr addr, Index mode, Condition cc) } void -MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, const Operand& addr, Index mode, Condition cc) +MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, const Address& addr, Index mode, Condition cc) { - ma_dataTransferN(ls, 32, true, - Register::FromCode(addr.base()), Imm32(addr.disp()), - rt, mode, cc); + ma_dataTransferN(ls, 32, true, addr.base, Imm32(addr.offset), rt, mode, cc); } void -MacroAssemblerARM::ma_str(Register rt, const Operand& addr, Index mode, Condition cc) +MacroAssemblerARM::ma_str(Register rt, const Address& addr, Index mode, Condition cc) { ma_dtr(IsStore, rt, addr, mode, cc); } @@ -1094,7 +1092,7 @@ MacroAssemblerARM::ma_ldr(DTRAddr addr, Register rt, Index mode, Condition cc) as_dtr(IsLoad, 32, mode, rt, addr, cc); } void -MacroAssemblerARM::ma_ldr(const Operand& addr, Register rt, Index mode, Condition cc) +MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, Index mode, Condition cc) { ma_dtr(IsLoad, rt, addr, mode, cc); } @@ -1234,7 +1232,7 @@ MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, Operand2 sub_off = Imm8(-(off - bottom)); // sub_off = bottom - off if (!sub_off.invalid) { // - sub_off = off - bottom - as_sub(ScratchRegister, rn, sub_off, NoSetCond, cc); + as_sub(ScratchRegister, rn, sub_off, LeaveCC, cc); return as_dtr(ls, size, Offset, rt, DTRAddr(ScratchRegister, DtrOffImm(bottom)), cc); } // sub_off = -neg_bottom - off @@ -1243,7 +1241,7 @@ MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, // Guarded against by: bottom != 0 MOZ_ASSERT(neg_bottom < 0x1000); // - sub_off = neg_bottom + off - as_sub(ScratchRegister, rn, sub_off, NoSetCond, cc); + as_sub(ScratchRegister, rn, sub_off, LeaveCC, cc); return as_dtr(ls, size, Offset, rt, DTRAddr(ScratchRegister, DtrOffImm(-neg_bottom)), cc); } } else { @@ -1251,7 +1249,7 @@ MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, Operand2 sub_off = Imm8(off - bottom); if (!sub_off.invalid) { // sub_off = off - bottom - as_add(ScratchRegister, rn, sub_off, NoSetCond, cc); + as_add(ScratchRegister, rn, sub_off, LeaveCC, cc); return as_dtr(ls, size, Offset, rt, DTRAddr(ScratchRegister, DtrOffImm(bottom)), cc); } // sub_off = neg_bottom + off @@ -1260,7 +1258,7 @@ MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, // Guarded against by: bottom != 0 MOZ_ASSERT(neg_bottom < 0x1000); // sub_off = neg_bottom + off - as_add(ScratchRegister, rn, sub_off, NoSetCond, cc); + as_add(ScratchRegister, rn, sub_off, LeaveCC, cc); return as_dtr(ls, size, Offset, rt, DTRAddr(ScratchRegister, DtrOffImm(-neg_bottom)), cc); } } @@ -1286,7 +1284,7 @@ MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, Operand2 sub_off = Imm8(-(off - bottom)); if (!sub_off.invalid) { // - sub_off = off - bottom - as_sub(ScratchRegister, rn, sub_off, NoSetCond, cc); + as_sub(ScratchRegister, rn, sub_off, LeaveCC, cc); return as_extdtr(ls, size, IsSigned, Offset, rt, EDtrAddr(ScratchRegister, EDtrOffImm(bottom)), cc); @@ -1297,7 +1295,7 @@ MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, // Guarded against by: bottom != 0 MOZ_ASSERT(neg_bottom < 0x100); // - sub_off = neg_bottom + off - as_sub(ScratchRegister, rn, sub_off, NoSetCond, cc); + as_sub(ScratchRegister, rn, sub_off, LeaveCC, cc); return as_extdtr(ls, size, IsSigned, Offset, rt, EDtrAddr(ScratchRegister, EDtrOffImm(-neg_bottom)), cc); @@ -1307,7 +1305,7 @@ MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, Operand2 sub_off = Imm8(off - bottom); if (!sub_off.invalid) { // sub_off = off - bottom - as_add(ScratchRegister, rn, sub_off, NoSetCond, cc); + as_add(ScratchRegister, rn, sub_off, LeaveCC, cc); return as_extdtr(ls, size, IsSigned, Offset, rt, EDtrAddr(ScratchRegister, EDtrOffImm(bottom)), cc); @@ -1318,7 +1316,7 @@ MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, // Guarded against by: bottom != 0 MOZ_ASSERT(neg_bottom < 0x100); // sub_off = neg_bottom + off - as_add(ScratchRegister, rn, sub_off, NoSetCond, cc); + as_add(ScratchRegister, rn, sub_off, LeaveCC, cc); return as_extdtr(ls, size, IsSigned, Offset, rt, EDtrAddr(ScratchRegister, EDtrOffImm(-neg_bottom)), cc); @@ -1709,13 +1707,13 @@ MacroAssemblerARM::ma_vxfer(Register src1, Register src2, FloatRegister dest, Co } BufferOffset -MacroAssemblerARM::ma_vdtr(LoadStore ls, const Operand& addr, VFPRegister rt, Condition cc) +MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr, VFPRegister rt, Condition cc) { - int off = addr.disp(); + int off = addr.offset; MOZ_ASSERT((off & 3) == 0); - Register base = Register::FromCode(addr.base()); + Register base = addr.base; if (off > -1024 && off < 1024) - return as_vdtr(ls, rt, addr.toVFPAddr(), cc); + return as_vdtr(ls, rt, Operand(addr).toVFPAddr(), cc); // We cannot encode this offset in a a single ldr. Try to encode it as an // add scratch, base, imm; ldr dest, [scratch, +offset]. @@ -1732,7 +1730,7 @@ MacroAssemblerARM::ma_vdtr(LoadStore ls, const Operand& addr, VFPRegister rt, Co Operand2 sub_off = Imm8(-(off - bottom)); if (!sub_off.invalid) { // - sub_off = off - bottom - as_sub(ScratchRegister, base, sub_off, NoSetCond, cc); + as_sub(ScratchRegister, base, sub_off, LeaveCC, cc); return as_vdtr(ls, rt, VFPAddr(ScratchRegister, VFPOffImm(bottom)), cc); } // sub_off = -neg_bottom - off @@ -1741,7 +1739,7 @@ MacroAssemblerARM::ma_vdtr(LoadStore ls, const Operand& addr, VFPRegister rt, Co // Guarded against by: bottom != 0 MOZ_ASSERT(neg_bottom < 0x400); // - sub_off = neg_bottom + off - as_sub(ScratchRegister, base, sub_off, NoSetCond, cc); + as_sub(ScratchRegister, base, sub_off, LeaveCC, cc); return as_vdtr(ls, rt, VFPAddr(ScratchRegister, VFPOffImm(-neg_bottom)), cc); } } else { @@ -1749,7 +1747,7 @@ MacroAssemblerARM::ma_vdtr(LoadStore ls, const Operand& addr, VFPRegister rt, Co Operand2 sub_off = Imm8(off - bottom); if (!sub_off.invalid) { // sub_off = off - bottom - as_add(ScratchRegister, base, sub_off, NoSetCond, cc); + as_add(ScratchRegister, base, sub_off, LeaveCC, cc); return as_vdtr(ls, rt, VFPAddr(ScratchRegister, VFPOffImm(bottom)), cc); } // sub_off = neg_bottom + off @@ -1758,11 +1756,11 @@ MacroAssemblerARM::ma_vdtr(LoadStore ls, const Operand& addr, VFPRegister rt, Co // Guarded against by: bottom != 0 MOZ_ASSERT(neg_bottom < 0x400); // sub_off = neg_bottom + off - as_add(ScratchRegister, base, sub_off, NoSetCond, cc); + as_add(ScratchRegister, base, sub_off, LeaveCC, cc); return as_vdtr(ls, rt, VFPAddr(ScratchRegister, VFPOffImm(-neg_bottom)), cc); } } - ma_add(base, Imm32(off), ScratchRegister, NoSetCond, cc); + ma_add(base, Imm32(off), ScratchRegister, LeaveCC, cc); return as_vdtr(ls, rt, VFPAddr(ScratchRegister, VFPOffImm(0)), cc); } @@ -1772,15 +1770,15 @@ MacroAssemblerARM::ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc) return as_vdtr(IsLoad, dest, addr, cc); } BufferOffset -MacroAssemblerARM::ma_vldr(const Operand& addr, VFPRegister dest, Condition cc) +MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, Condition cc) { return ma_vdtr(IsLoad, addr, dest, cc); } BufferOffset MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, int32_t shift, Condition cc) { - as_add(ScratchRegister, base, lsl(index, shift), NoSetCond, cc); - return ma_vldr(Operand(ScratchRegister, 0), src, cc); + as_add(ScratchRegister, base, lsl(index, shift), LeaveCC, cc); + return ma_vldr(Address(ScratchRegister, 0), src, cc); } BufferOffset @@ -1790,7 +1788,7 @@ MacroAssemblerARM::ma_vstr(VFPRegister src, VFPAddr addr, Condition cc) } BufferOffset -MacroAssemblerARM::ma_vstr(VFPRegister src, const Operand& addr, Condition cc) +MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, Condition cc) { return ma_vdtr(IsStore, addr, src, cc); } @@ -1798,8 +1796,8 @@ BufferOffset MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift, int32_t offset, Condition cc) { - as_add(ScratchRegister, base, lsl(index, shift), NoSetCond, cc); - return ma_vstr(src, Operand(ScratchRegister, offset), cc); + as_add(ScratchRegister, base, lsl(index, shift), LeaveCC, cc); + return ma_vstr(src, Address(ScratchRegister, offset), cc); } void @@ -1931,58 +1929,58 @@ MacroAssemblerARMCompat::freeStack(Register amount) void MacroAssemblerARMCompat::add32(Register src, Register dest) { - ma_add(src, dest, SetCond); + ma_add(src, dest, SetCC); } void MacroAssemblerARMCompat::add32(Imm32 imm, Register dest) { - ma_add(imm, dest, SetCond); + ma_add(imm, dest, SetCC); } void MacroAssemblerARMCompat::xor32(Imm32 imm, Register dest) { - ma_eor(imm, dest, SetCond); + ma_eor(imm, dest, SetCC); } void MacroAssemblerARMCompat::add32(Imm32 imm, const Address& dest) { load32(dest, ScratchRegister); - ma_add(imm, ScratchRegister, SetCond); + ma_add(imm, ScratchRegister, SetCC); store32(ScratchRegister, dest); } void MacroAssemblerARMCompat::sub32(Imm32 imm, Register dest) { - ma_sub(imm, dest, SetCond); + ma_sub(imm, dest, SetCC); } void MacroAssemblerARMCompat::sub32(Register src, Register dest) { - ma_sub(src, dest, SetCond); + ma_sub(src, dest, SetCC); } void MacroAssemblerARMCompat::and32(Register src, Register dest) { - ma_and(src, dest, SetCond); + ma_and(src, dest, SetCC); } void MacroAssemblerARMCompat::and32(Imm32 imm, Register dest) { - ma_and(imm, dest, SetCond); + ma_and(imm, dest, SetCC); } void MacroAssemblerARMCompat::and32(const Address& src, Register dest) { load32(src, ScratchRegister); - ma_and(ScratchRegister, dest, SetCond); + ma_and(ScratchRegister, dest, SetCC); } void @@ -1995,7 +1993,7 @@ void MacroAssemblerARMCompat::addPtr(const Address& src, Register dest) { load32(src, ScratchRegister); - ma_add(ScratchRegister, dest, SetCond); + ma_add(ScratchRegister, dest, SetCC); } void @@ -2232,7 +2230,7 @@ MacroAssemblerARMCompat::load32(AbsoluteAddress address, Register dest) void MacroAssemblerARMCompat::loadPtr(const Address& address, Register dest) { - ma_ldr(Operand(address), dest); + ma_ldr(address, dest); } void @@ -2261,23 +2259,16 @@ MacroAssemblerARMCompat::loadPtr(AsmJSAbsoluteAddress address, Register dest) loadPtr(Address(ScratchRegister, 0x0), dest); } -Operand payloadOf(const Address& address) { - return Operand(address.base, address.offset); -} -Operand tagOf(const Address& address) { - return Operand(address.base, address.offset + 4); -} - void MacroAssemblerARMCompat::loadPrivate(const Address& address, Register dest) { - ma_ldr(payloadOf(address), dest); + ma_ldr(ToPayload(address), dest); } void MacroAssemblerARMCompat::loadDouble(const Address& address, FloatRegister dest) { - ma_vldr(Operand(address), dest); + ma_vldr(address, dest); } void @@ -2291,14 +2282,14 @@ MacroAssemblerARMCompat::loadDouble(const BaseIndex& src, FloatRegister dest) int32_t offset = src.offset; as_add(ScratchRegister, base, lsl(index, scale)); - ma_vldr(Operand(ScratchRegister, offset), dest); + ma_vldr(Address(ScratchRegister, offset), dest); } void MacroAssemblerARMCompat::loadFloatAsDouble(const Address& address, FloatRegister dest) { VFPRegister rt = dest; - ma_vldr(Operand(address), rt.singleOverlay()); + ma_vldr(address, rt.singleOverlay()); as_vcvt(rt, rt.singleOverlay()); } @@ -2314,14 +2305,14 @@ MacroAssemblerARMCompat::loadFloatAsDouble(const BaseIndex& src, FloatRegister d VFPRegister rt = dest; as_add(ScratchRegister, base, lsl(index, scale)); - ma_vldr(Operand(ScratchRegister, offset), rt.singleOverlay()); + ma_vldr(Address(ScratchRegister, offset), rt.singleOverlay()); as_vcvt(rt, rt.singleOverlay()); } void MacroAssemblerARMCompat::loadFloat32(const Address& address, FloatRegister dest) { - ma_vldr(Operand(address), VFPRegister(dest).singleOverlay()); + ma_vldr(address, VFPRegister(dest).singleOverlay()); } void @@ -2335,7 +2326,7 @@ MacroAssemblerARMCompat::loadFloat32(const BaseIndex& src, FloatRegister dest) int32_t offset = src.offset; as_add(ScratchRegister, base, lsl(index, scale)); - ma_vldr(Operand(ScratchRegister, offset), VFPRegister(dest).singleOverlay()); + ma_vldr(Address(ScratchRegister, offset), VFPRegister(dest).singleOverlay()); } void @@ -2488,7 +2479,7 @@ template void MacroAssemblerARMCompat::storePtr(ImmGCPtr imm, BaseInd void MacroAssemblerARMCompat::storePtr(Register src, const Address& address) { - ma_str(src, Operand(address)); + ma_str(src, address); } void @@ -2531,7 +2522,7 @@ MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) ma_cmp(ScratchRegister, Imm32(0)); // If the lower 32 bits of the double were 0, then this was an exact number, // and it should be even. - ma_bic(Imm32(1), output, NoSetCond, Zero); + ma_bic(Imm32(1), output, LeaveCC, Zero); bind(¬Split); } else { Label outOfRange; @@ -2543,17 +2534,17 @@ MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) // Copy the converted value out. as_vxfer(output, InvalidReg, ScratchDoubleReg, FloatToCore); as_vmrs(pc); - ma_mov(Imm32(0), output, NoSetCond, Overflow); // NaN => 0 + ma_mov(Imm32(0), output, LeaveCC, Overflow); // NaN => 0 ma_b(&outOfRange, Overflow); // NaN ma_cmp(output, Imm32(0xff)); - ma_mov(Imm32(0xff), output, NoSetCond, Above); + ma_mov(Imm32(0xff), output, LeaveCC, Above); ma_b(&outOfRange, Above); // Convert it back to see if we got the same value back. as_vcvt(ScratchDoubleReg, VFPRegister(ScratchDoubleReg).uintOverlay()); // Do the check. as_vcmp(ScratchDoubleReg, input); as_vmrs(pc); - ma_bic(Imm32(1), output, NoSetCond, Zero); + ma_bic(Imm32(1), output, LeaveCC, Zero); bind(&outOfRange); } } @@ -3147,19 +3138,19 @@ MacroAssemblerARMCompat::branchTestValue(Condition cond, const Address& valaddr, // Check payload before tag, since payload is more likely to differ. if (cond == NotEqual) { - ma_ldr(payloadOf(valaddr), ScratchRegister); + ma_ldr(ToPayload(valaddr), ScratchRegister); branchPtr(NotEqual, ScratchRegister, value.payloadReg(), label); - ma_ldr(tagOf(valaddr), ScratchRegister); + ma_ldr(ToType(valaddr), ScratchRegister); branchPtr(NotEqual, ScratchRegister, value.typeReg(), label); } else { Label fallthrough; - ma_ldr(payloadOf(valaddr), ScratchRegister); + ma_ldr(ToPayload(valaddr), ScratchRegister); branchPtr(NotEqual, ScratchRegister, value.payloadReg(), &fallthrough); - ma_ldr(tagOf(valaddr), ScratchRegister); + ma_ldr(ToType(valaddr), ScratchRegister); branchPtr(Equal, ScratchRegister, value.typeReg(), label); bind(&fallthrough); @@ -3177,7 +3168,7 @@ MacroAssemblerARMCompat::unboxNonDouble(const ValueOperand& operand, Register de void MacroAssemblerARMCompat::unboxNonDouble(const Address& src, Register dest) { - ma_ldr(payloadOf(src), dest); + ma_ldr(ToPayload(src), dest); } void @@ -3199,7 +3190,7 @@ void MacroAssemblerARMCompat::unboxDouble(const Address& src, FloatRegister dest) { MOZ_ASSERT(dest.isDouble()); - ma_vldr(Operand(src), dest); + ma_vldr(src, dest); } void @@ -3286,7 +3277,7 @@ MacroAssemblerARMCompat::loadConstantFloat32(float f, FloatRegister dest) } void -MacroAssemblerARMCompat::loadInt32OrDouble(const Operand& src, FloatRegister dest) +MacroAssemblerARMCompat::loadInt32OrDouble(const Address& src, FloatRegister dest) { Label notInt32, end; // If it's an int, convert it to double. @@ -3365,21 +3356,21 @@ MacroAssemblerARMCompat::testDoubleTruthy(bool truthy, FloatRegister reg) Register MacroAssemblerARMCompat::extractObject(const Address& address, Register scratch) { - ma_ldr(payloadOf(address), scratch); + ma_ldr(ToPayload(address), scratch); return scratch; } Register MacroAssemblerARMCompat::extractTag(const Address& address, Register scratch) { - ma_ldr(tagOf(address), scratch); + ma_ldr(ToType(address), scratch); return scratch; } Register MacroAssemblerARMCompat::extractTag(const BaseIndex& address, Register scratch) { - ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd, NoSetCond); + ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd, LeaveCC); return extractTag(Address(scratch, address.offset), scratch); } @@ -3432,7 +3423,7 @@ MacroAssemblerARMCompat::moveValue(const Value& val, const ValueOperand& dest) // X86/X64-common (ARM too now) interface. ///////////////////////////////////////////////////////////////// void -MacroAssemblerARMCompat::storeValue(ValueOperand val, Operand dst) +MacroAssemblerARMCompat::storeValue(ValueOperand val, const Address& dst) { ma_str(val.payloadReg(), ToPayload(dst)); ma_str(val.typeReg(), ToType(dst)); @@ -3489,17 +3480,17 @@ MacroAssemblerARMCompat::loadValue(const BaseIndex& addr, ValueOperand val) void MacroAssemblerARMCompat::loadValue(Address src, ValueOperand val) { - Operand srcOp = Operand(src); - Operand payload = ToPayload(srcOp); - Operand type = ToType(srcOp); + Address payload = ToPayload(src); + Address type = ToType(src); + // TODO: copy this code into a generic function that acts on all sequences // of memory accesses if (isValueDTRDCandidate(val)) { // If the value we want is in two consecutive registers starting with an // even register, they can be combined as a single ldrd. - int offset = srcOp.disp(); + int offset = src.offset; if (offset < 256 && offset > -256) { - ma_ldrd(EDtrAddr(Register::FromCode(srcOp.base()), EDtrOffImm(srcOp.disp())), val.payloadReg(), val.typeReg()); + ma_ldrd(EDtrAddr(src.base, EDtrOffImm(src.offset)), val.payloadReg(), val.typeReg()); return; } } @@ -3507,11 +3498,11 @@ MacroAssemblerARMCompat::loadValue(Address src, ValueOperand val) // instruction. if (val.payloadReg().code() < val.typeReg().code()) { - if (srcOp.disp() <= 4 && srcOp.disp() >= -8 && (srcOp.disp() & 3) == 0) { + if (src.offset <= 4 && src.offset >= -8 && (src.offset & 3) == 0) { // Turns out each of the 4 value -8, -4, 0, 4 corresponds exactly // with one of LDM{DB, DA, IA, IB} DTMMode mode; - switch(srcOp.disp()) { + switch (src.offset) { case -8: mode = DB; break; @@ -3527,7 +3518,7 @@ MacroAssemblerARMCompat::loadValue(Address src, ValueOperand val) default: MOZ_CRASH("Bogus Offset for LoadValue as DTM"); } - startDataTransferM(IsLoad, Register::FromCode(srcOp.base()), mode); + startDataTransferM(IsLoad, src.base, mode); transferReg(val.payloadReg()); transferReg(val.typeReg()); finishDataTransfer(); @@ -3536,7 +3527,7 @@ MacroAssemblerARMCompat::loadValue(Address src, ValueOperand val) } // Ensure that loading the payload does not erase the pointer to the Value // in memory. - if (Register::FromCode(type.base()) != val.payloadReg()) { + if (type.base != val.payloadReg()) { ma_ldr(payload, val.payloadReg()); ma_ldr(type, val.typeReg()); } else { @@ -3562,13 +3553,9 @@ MacroAssemblerARMCompat::pushValue(ValueOperand val) { void MacroAssemblerARMCompat::pushValue(const Address& addr) { - Operand srcOp = Operand(addr); - Operand type = ToType(srcOp); - Operand payload = ToPayloadAfterStackPush(srcOp); - - ma_ldr(type, ScratchRegister); + ma_ldr(ToType(addr), ScratchRegister); ma_push(ScratchRegister); - ma_ldr(payload, ScratchRegister); + ma_ldr(ToPayloadAfterStackPush(addr), ScratchRegister); ma_push(ScratchRegister); } @@ -3578,7 +3565,7 @@ MacroAssemblerARMCompat::popValue(ValueOperand val) { ma_pop(val.typeReg()); } void -MacroAssemblerARMCompat::storePayload(const Value& val, Operand dest) +MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest) { jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) @@ -3587,15 +3574,11 @@ MacroAssemblerARMCompat::storePayload(const Value& val, Operand dest) ma_mov(Imm32(jv.s.payload.i32), secondScratchReg_); ma_str(secondScratchReg_, ToPayload(dest)); } -void -MacroAssemblerARMCompat::storePayload(Register src, Operand dest) -{ - if (dest.getTag() == Operand::MEM) { - ma_str(src, ToPayload(dest)); - return; - } - MOZ_CRASH("why do we do all of these things?"); +void +MacroAssemblerARMCompat::storePayload(Register src, const Address& dest) +{ + ma_str(src, ToPayload(dest)); } void @@ -3651,15 +3634,10 @@ MacroAssemblerARMCompat::storePayload(Register src, const BaseIndex& dest) } void -MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, Operand dest) { - if (dest.getTag() == Operand::MEM) { - ma_mov(tag, secondScratchReg_); - ma_str(secondScratchReg_, ToType(dest)); - return; - } - - MOZ_CRASH("why do we do all of these things?"); - +MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const Address& dest) +{ + ma_mov(tag, secondScratchReg_); + ma_str(secondScratchReg_, ToType(dest)); } void @@ -4210,7 +4188,7 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler) Label return_; Label bailout; - ma_ldr(Operand(sp, offsetof(ResumeFromException, kind)), r0); + ma_ldr(Address(sp, offsetof(ResumeFromException, kind)), r0); branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame); branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_CATCH), &catch_); branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FINALLY), &finally); @@ -4223,7 +4201,7 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler) // and return from the entry frame. bind(&entryFrame); moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); - ma_ldr(Operand(sp, offsetof(ResumeFromException, stackPointer)), sp); + ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); // We're going to be returning by the ion calling convention, which returns // by ??? (for now, I think ldr pc, [sp]!) @@ -4232,9 +4210,9 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler) // If we found a catch handler, this must be a baseline frame. Restore state // and jump to the catch block. bind(&catch_); - ma_ldr(Operand(sp, offsetof(ResumeFromException, target)), r0); - ma_ldr(Operand(sp, offsetof(ResumeFromException, framePointer)), r11); - ma_ldr(Operand(sp, offsetof(ResumeFromException, stackPointer)), sp); + ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0); + ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11); + ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); jump(r0); // If we found a finally block, this must be a baseline frame. Push two @@ -4243,9 +4221,9 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler) ValueOperand exception = ValueOperand(r1, r2); loadValue(Operand(sp, offsetof(ResumeFromException, exception)), exception); - ma_ldr(Operand(sp, offsetof(ResumeFromException, target)), r0); - ma_ldr(Operand(sp, offsetof(ResumeFromException, framePointer)), r11); - ma_ldr(Operand(sp, offsetof(ResumeFromException, stackPointer)), sp); + ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0); + ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11); + ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); pushValue(BooleanValue(true)); pushValue(exception); @@ -4254,8 +4232,8 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler) // Only used in debug mode. Return BaselineFrame->returnValue() to the // caller. bind(&return_); - ma_ldr(Operand(sp, offsetof(ResumeFromException, framePointer)), r11); - ma_ldr(Operand(sp, offsetof(ResumeFromException, stackPointer)), sp); + ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11); + ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); ma_mov(r11, sp); pop(r11); @@ -4276,9 +4254,9 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler) // If we are bailing out to baseline to handle an exception, jump to the // bailout tail stub. bind(&bailout); - ma_ldr(Operand(sp, offsetof(ResumeFromException, bailoutInfo)), r2); + ma_ldr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r2); ma_mov(Imm32(BAILOUT_RETURN_OK), r0); - ma_ldr(Operand(sp, offsetof(ResumeFromException, target)), r1); + ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r1); jump(r1); } @@ -4309,7 +4287,7 @@ MacroAssemblerARMCompat::floor(FloatRegister input, Register output, Label* bail // that clamps to INT_MAX. ma_vcvt_F64_U32(input, ScratchDoubleReg.uintOverlay()); ma_vxfer(ScratchDoubleReg.uintOverlay(), output); - ma_mov(output, output, SetCond); + ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(&fin); @@ -4328,10 +4306,10 @@ MacroAssemblerARMCompat::floor(FloatRegister input, Register output, Label* bail ma_vxfer(ScratchDoubleReg.uintOverlay(), output); ma_vcvt_U32_F64(ScratchDoubleReg.uintOverlay(), ScratchDoubleReg); compareDouble(ScratchDoubleReg, input); - ma_add(output, Imm32(1), output, NoSetCond, NotEqual); + ma_add(output, Imm32(1), output, LeaveCC, NotEqual); // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the // result will still be a negative number. - ma_rsb(output, Imm32(0), output, SetCond); + ma_rsb(output, Imm32(0), output, SetCC); // Flip the negated input back to its original value. ma_vneg(input, input); // If the result looks non-negative, then this value didn't actually fit @@ -4360,7 +4338,7 @@ MacroAssemblerARMCompat::floorf(FloatRegister input, Register output, Label* bai // that clamps to INT_MAX. ma_vcvt_F32_U32(input, ScratchFloat32Reg.uintOverlay()); ma_vxfer(VFPRegister(ScratchFloat32Reg).uintOverlay(), output); - ma_mov(output, output, SetCond); + ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(&fin); @@ -4379,10 +4357,10 @@ MacroAssemblerARMCompat::floorf(FloatRegister input, Register output, Label* bai ma_vxfer(VFPRegister(ScratchFloat32Reg).uintOverlay(), output); ma_vcvt_U32_F32(ScratchFloat32Reg.uintOverlay(), ScratchFloat32Reg); compareFloat(ScratchFloat32Reg, input); - ma_add(output, Imm32(1), output, NoSetCond, NotEqual); + ma_add(output, Imm32(1), output, LeaveCC, NotEqual); // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the // result will still be a negative number. - ma_rsb(output, Imm32(0), output, SetCond); + ma_rsb(output, Imm32(0), output, SetCC); // Flip the negated input back to its original value. ma_vneg_f32(input, input); // If the result looks non-negative, then this value didn't actually fit @@ -4418,7 +4396,7 @@ MacroAssemblerARMCompat::ceil(FloatRegister input, Register output, Label* bail) FloatRegister ScratchUIntReg = ScratchDoubleReg.uintOverlay(); ma_vcvt_F64_U32(ScratchDoubleReg, ScratchUIntReg); ma_vxfer(ScratchUIntReg, output); - ma_neg(output, output, SetCond); + ma_neg(output, output, SetCC); ma_b(bail, NotSigned); ma_b(&fin); @@ -4437,9 +4415,9 @@ MacroAssemblerARMCompat::ceil(FloatRegister input, Register output, Label* bail) ma_vxfer(ScratchUIntReg, output); ma_vcvt_U32_F64(ScratchUIntReg, ScratchDoubleReg); compareDouble(ScratchDoubleReg, input); - ma_add(output, Imm32(1), output, NoSetCond, NotEqual); + ma_add(output, Imm32(1), output, LeaveCC, NotEqual); // Bail out if the add overflowed or the result is non positive. - ma_mov(output, output, SetCond); + ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(bail, Zero); @@ -4471,7 +4449,7 @@ MacroAssemblerARMCompat::ceilf(FloatRegister input, Register output, Label* bail FloatRegister ScratchUIntReg = ScratchDoubleReg.uintOverlay(); ma_vcvt_F32_U32(ScratchFloat32Reg, ScratchUIntReg); ma_vxfer(ScratchUIntReg, output); - ma_neg(output, output, SetCond); + ma_neg(output, output, SetCC); ma_b(bail, NotSigned); ma_b(&fin); @@ -4490,9 +4468,9 @@ MacroAssemblerARMCompat::ceilf(FloatRegister input, Register output, Label* bail ma_vxfer(ScratchUIntReg, output); ma_vcvt_U32_F32(ScratchUIntReg, ScratchFloat32Reg); compareFloat(ScratchFloat32Reg, input); - ma_add(output, Imm32(1), output, NoSetCond, NotEqual); + ma_add(output, Imm32(1), output, LeaveCC, NotEqual); // Bail out if the add overflowed or the result is non positive. - ma_mov(output, output, SetCond); + ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(bail, Zero); @@ -4552,7 +4530,7 @@ MacroAssemblerARMCompat::round(FloatRegister input, Register output, Label* bail ma_vcvt_F64_U32(tmp, ScratchDoubleReg.uintOverlay()); ma_vxfer(VFPRegister(ScratchDoubleReg).uintOverlay(), output); - ma_mov(output, output, SetCond); + ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(&fin); @@ -4580,10 +4558,10 @@ MacroAssemblerARMCompat::round(FloatRegister input, Register output, Label* bail // away from zero, when it should be rounded towards \infty. ma_vcvt_U32_F64(ScratchDoubleReg.uintOverlay(), ScratchDoubleReg); compareDouble(ScratchDoubleReg, tmp); - ma_sub(output, Imm32(1), output, NoSetCond, Equal); + ma_sub(output, Imm32(1), output, LeaveCC, Equal); // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the // result will still be a negative number. - ma_rsb(output, Imm32(0), output, SetCond); + ma_rsb(output, Imm32(0), output, SetCC); // If the result looks non-negative, then this value didn't actually fit // into the int range, and special handling is required, or it was zero, @@ -4626,7 +4604,7 @@ MacroAssemblerARMCompat::roundf(FloatRegister input, Register output, Label* bai // we are not applying any fixup after the operation). ma_vcvt_F32_U32(tmp, ScratchFloat32Reg.uintOverlay()); ma_vxfer(VFPRegister(ScratchFloat32Reg).uintOverlay(), output); - ma_mov(output, output, SetCond); + ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(&fin); @@ -4666,12 +4644,12 @@ MacroAssemblerARMCompat::roundf(FloatRegister input, Register output, Label* bai // away from zero, when it should be rounded towards \infty. ma_vcvt_U32_F32(tmp.uintOverlay(), tmp); compareFloat(tmp, ScratchFloat32Reg); - ma_sub(output, Imm32(1), output, NoSetCond, Equal); + ma_sub(output, Imm32(1), output, LeaveCC, Equal); // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the // result will still be a negative number. bind(&flipSign); - ma_rsb(output, Imm32(0), output, SetCond); + ma_rsb(output, Imm32(0), output, SetCC); // If the result looks non-negative, then this value didn't actually fit // into the int range, and special handling is required, or it was zero, diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 2ab97f300a59..d7e0e12e4c73 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -40,28 +40,27 @@ class MacroAssemblerARM : public Assembler public: // Higher level tag testing code. - Operand ToPayload(Operand base) { + // TODO: Can probably remove the Operand versions. + Operand ToPayload(Operand base) const { return Operand(Register::FromCode(base.base()), base.disp()); } - Address ToPayload(Address base) { - return ToPayload(Operand(base)).toAddress(); + Address ToPayload(const Address& base) const { + return base; } protected: - Operand ToType(Operand base) { + Operand ToType(Operand base) const { return Operand(Register::FromCode(base.base()), base.disp() + sizeof(void*)); } - Address ToType(Address base) { + Address ToType(const Address& base) const { return ToType(Operand(base)).toAddress(); } - Operand ToPayloadAfterStackPush(Operand base) { - Register baseReg = Register::FromCode(base.base()); + Address ToPayloadAfterStackPush(const Address& base) const { // If we are based on StackPointer, pass over the type tag just pushed. - if (baseReg == StackPointer) - return Operand(Register::FromCode(base.base()), base.disp() + sizeof(void*)); - else - return ToPayload(base); + if (base.base == StackPointer) + return Address(base.base, base.offset + sizeof(void *)); + return ToPayload(base); } public: @@ -106,17 +105,17 @@ class MacroAssemblerARM : public Assembler // instructions. private: bool alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op, - SetCond_ sc, Condition c); + SBit s, Condition c); public: void ma_alu(Register src1, Operand2 op2, Register dest, ALUOp op, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_alu(Register src1, Imm32 imm, Register dest, ALUOp op, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_alu(Register src1, Operand op2, Register dest, ALUOp op, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_nop(); void ma_movPatchable(Imm32 imm, Register dest, Assembler::Condition c, @@ -136,12 +135,12 @@ class MacroAssemblerARM : public Assembler // ALU based ops // mov void ma_mov(Register src, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_mov(Imm32 imm, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_mov(ImmWord imm, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_mov(ImmGCPtr ptr, Register dest); @@ -160,98 +159,98 @@ class MacroAssemblerARM : public Assembler // Move not (dest <- ~src) void ma_mvn(Imm32 imm, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_mvn(Register src1, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); // Negate (dest <- -src) implemented as rsb dest, src, 0 void ma_neg(Register src, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); // And void ma_and(Register src, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_and(Register src1, Register src2, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_and(Imm32 imm, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_and(Imm32 imm, Register src1, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2) void ma_bic(Imm32 imm, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); // Exclusive or void ma_eor(Register src, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_eor(Register src1, Register src2, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_eor(Imm32 imm, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_eor(Imm32 imm, Register src1, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); // Or void ma_orr(Register src, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_orr(Register src1, Register src2, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_orr(Imm32 imm, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); void ma_orr(Imm32 imm, Register src1, Register dest, - SetCond_ sc = NoSetCond, Condition c = Always); + SBit s = LeaveCC, Condition c = Always); // Arithmetic based ops. // Add with carry: - void ma_adc(Imm32 imm, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_adc(Register src, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_adc(Register src1, Register src2, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); + void ma_adc(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_adc(Register src, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_adc(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always); // Add: - void ma_add(Imm32 imm, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_add(Register src1, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_add(Register src1, Register src2, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_add(Register src1, Operand op, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_add(Register src1, Imm32 op, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); + void ma_add(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_add(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_add(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_add(Register src1, Operand op, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_add(Register src1, Imm32 op, Register dest, SBit s = LeaveCC, Condition c = Always); // Subtract with carry: - void ma_sbc(Imm32 imm, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_sbc(Register src1, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_sbc(Register src1, Register src2, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); + void ma_sbc(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_sbc(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_sbc(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always); // Subtract: - void ma_sub(Imm32 imm, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_sub(Register src1, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_sub(Register src1, Register src2, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_sub(Register src1, Operand op, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_sub(Register src1, Imm32 op, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); + void ma_sub(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_sub(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_sub(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_sub(Register src1, Operand op, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_sub(Register src1, Imm32 op, Register dest, SBit s = LeaveCC, Condition c = Always); // Reverse subtract: - void ma_rsb(Imm32 imm, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_rsb(Register src1, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_rsb(Register src1, Register src2, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_rsb(Register src1, Imm32 op2, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); + void ma_rsb(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_rsb(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_rsb(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_rsb(Register src1, Imm32 op2, Register dest, SBit s = LeaveCC, Condition c = Always); // Reverse subtract with carry: - void ma_rsc(Imm32 imm, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_rsc(Register src1, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); - void ma_rsc(Register src1, Register src2, Register dest, SetCond_ sc = NoSetCond, Condition c = Always); + void ma_rsc(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_rsc(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always); + void ma_rsc(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always); // Compares/tests. // Compare negative (sets condition codes as src1 + src2 would): @@ -309,11 +308,11 @@ class MacroAssemblerARM : public Assembler void ma_str(Register rt, DTRAddr addr, Index mode = Offset, Condition cc = Always); - void ma_str(Register rt, const Operand& addr, Index mode = Offset, Condition cc = Always); - void ma_dtr(LoadStore ls, Register rt, const Operand& addr, Index mode, Condition cc); + void ma_str(Register rt, const Address& addr, Index mode = Offset, Condition cc = Always); + void ma_dtr(LoadStore ls, Register rt, const Address& addr, Index mode, Condition cc); void ma_ldr(DTRAddr addr, Register rt, Index mode = Offset, Condition cc = Always); - void ma_ldr(const Operand& addr, Register rt, Index mode = Offset, Condition cc = Always); + void ma_ldr(const Address& addr, Register rt, Index mode = Offset, Condition cc = Always); void ma_ldrb(DTRAddr addr, Register rt, Index mode = Offset, Condition cc = Always); void ma_ldrh(EDtrAddr addr, Register rt, Index mode = Offset, Condition cc = Always); @@ -407,15 +406,15 @@ class MacroAssemblerARM : public Assembler void ma_vxfer(Register src1, Register src2, FloatRegister dest, Condition cc = Always); - BufferOffset ma_vdtr(LoadStore ls, const Operand& addr, VFPRegister dest, Condition cc = Always); + BufferOffset ma_vdtr(LoadStore ls, const Address& addr, VFPRegister dest, Condition cc = Always); BufferOffset ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc = Always); - BufferOffset ma_vldr(const Operand& addr, VFPRegister dest, Condition cc = Always); + BufferOffset ma_vldr(const Address& addr, VFPRegister dest, Condition cc = Always); BufferOffset ma_vldr(VFPRegister src, Register base, Register index, int32_t shift = defaultShift, Condition cc = Always); BufferOffset ma_vstr(VFPRegister src, VFPAddr addr, Condition cc = Always); - BufferOffset ma_vstr(VFPRegister src, const Operand& addr, Condition cc = Always); + BufferOffset ma_vstr(VFPRegister src, const Address& addr, Condition cc = Always); BufferOffset ma_vstr(VFPRegister src, Register base, Register index, int32_t shift, int32_t offset, Condition cc = Always); @@ -665,8 +664,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void push(ImmMaybeNurseryPtr imm) { push(noteMaybeNurseryPtr(imm)); } - void push(const Address& address) { - ma_ldr(Operand(address.base, address.offset), ScratchRegister); + void push(const Address& addr) { + ma_ldr(addr, ScratchRegister); ma_push(ScratchRegister); } void push(Register reg) { @@ -729,16 +728,16 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void jump(Register reg) { ma_bx(reg); } - void jump(const Address& address) { - ma_ldr(Operand(address.base, address.offset), ScratchRegister); + void jump(const Address& addr) { + ma_ldr(addr, ScratchRegister); ma_bx(ScratchRegister); } void neg32(Register reg) { - ma_neg(reg, reg, SetCond); + ma_neg(reg, reg, SetCC); } void negl(Register reg) { - ma_neg(reg, reg, SetCond); + ma_neg(reg, reg, SetCC); } void test32(Register lhs, Register rhs) { ma_tst(lhs, rhs); @@ -746,8 +745,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void test32(Register lhs, Imm32 imm) { ma_tst(lhs, imm); } - void test32(const Address& address, Imm32 imm) { - ma_ldr(Operand(address.base, address.offset), ScratchRegister); + void test32(const Address& addr, Imm32 imm) { + ma_ldr(addr, ScratchRegister); ma_tst(ScratchRegister, imm); } void testPtr(Register lhs, Register rhs) { @@ -873,7 +872,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void boolValueToDouble(const ValueOperand& operand, FloatRegister dest); void int32ValueToDouble(const ValueOperand& operand, FloatRegister dest); - void loadInt32OrDouble(const Operand& src, FloatRegister dest); + void loadInt32OrDouble(const Address& src, FloatRegister dest); void loadInt32OrDouble(Register base, Register index, FloatRegister dest, int32_t shift = defaultShift); void loadConstantDouble(double dp, FloatRegister dest); @@ -909,7 +908,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM if (lhs.getTag() == Operand::OP2) { branch32(cond, lhs.toReg(), rhs, label); } else { - ma_ldr(lhs, ScratchRegister); + ma_ldr(lhs.toAddress(), ScratchRegister); branch32(cond, ScratchRegister, rhs, label); } } @@ -918,7 +917,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM branch32(cond, lhs.toReg(), rhs, label); } else { // branch32 will use ScratchRegister. - ma_ldr(lhs, secondScratchReg_); + ma_ldr(lhs.toAddress(), secondScratchReg_); branch32(cond, secondScratchReg_, rhs, label); } } @@ -1137,7 +1136,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void loadUnboxedValue(Address address, MIRType type, AnyRegister dest) { if (dest.isFloat()) - loadInt32OrDouble(Operand(address), dest.fpu()); + loadInt32OrDouble(address, dest.fpu()); else ma_ldr(address, dest.gpr()); } @@ -1195,29 +1194,26 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM ma_mov(s1, d1); } - void storeValue(ValueOperand val, Operand dst); + void storeValue(ValueOperand val, const Address& dst); void storeValue(ValueOperand val, const BaseIndex& dest); void storeValue(JSValueType type, Register reg, BaseIndex dest) { ma_alu(dest.base, lsl(dest.index, dest.scale), ScratchRegister, OpAdd); storeValue(type, reg, Address(ScratchRegister, dest.offset)); } - void storeValue(ValueOperand val, const Address& dest) { - storeValue(val, Operand(dest)); - } void storeValue(JSValueType type, Register reg, Address dest) { ma_str(reg, dest); ma_mov(ImmTag(JSVAL_TYPE_TO_TAG(type)), secondScratchReg_); ma_str(secondScratchReg_, Address(dest.base, dest.offset + 4)); } - void storeValue(const Value& val, Address dest) { + void storeValue(const Value& val, const Address& dest) { jsval_layout jv = JSVAL_TO_IMPL(val); ma_mov(Imm32(jv.s.tag), secondScratchReg_); - ma_str(secondScratchReg_, Address(dest.base, dest.offset + 4)); + ma_str(secondScratchReg_, ToType(dest)); if (val.isMarkable()) ma_mov(ImmGCPtr(reinterpret_cast(val.toGCThing())), secondScratchReg_); else ma_mov(Imm32(jv.s.payload.i32), secondScratchReg_); - ma_str(secondScratchReg_, dest); + ma_str(secondScratchReg_, ToPayload(dest)); } void storeValue(const Value& val, BaseIndex dest) { ma_alu(dest.base, lsl(dest.index, dest.scale), ScratchRegister, OpAdd); @@ -1247,11 +1243,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM } void pushValue(const Address& addr); - void storePayload(const Value& val, Operand dest); - void storePayload(Register src, Operand dest); + void storePayload(const Value& val, const Address& dest); + void storePayload(Register src, const Address& dest); void storePayload(const Value& val, const BaseIndex& dest); void storePayload(Register src, const BaseIndex& dest); - void storeTypeTag(ImmTag tag, Operand dest); + void storeTypeTag(ImmTag tag, const Address& dest); void storeTypeTag(ImmTag tag, const BaseIndex& dest); void makeFrameDescriptor(Register frameSizeReg, FrameType type) { @@ -1443,7 +1439,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void storePtr(Register src, const BaseIndex& address); void storePtr(Register src, AbsoluteAddress dest); void storeDouble(FloatRegister src, Address addr) { - ma_vstr(src, Operand(addr)); + ma_vstr(src, addr); } void storeDouble(FloatRegister src, BaseIndex addr) { uint32_t scale = Imm32::ShiftOf(addr.scale).value; @@ -1453,10 +1449,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM ma_vmov(src, dest); } - void storeFloat32(FloatRegister src, Address addr) { - ma_vstr(VFPRegister(src).singleOverlay(), Operand(addr)); + void storeFloat32(FloatRegister src, const Address& addr) { + ma_vstr(VFPRegister(src).singleOverlay(), addr); } - void storeFloat32(FloatRegister src, BaseIndex addr) { + void storeFloat32(FloatRegister src, const BaseIndex& addr) { uint32_t scale = Imm32::ShiftOf(addr.scale).value; ma_vstr(VFPRegister(src).singleOverlay(), addr.base, addr.index, scale, addr.offset); } @@ -1702,9 +1698,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void clampIntToUint8(Register reg) { // Look at (reg >> 8) if it is 0, then reg shouldn't be clamped if it is // <0, then we want to clamp to 0, otherwise, we wish to clamp to 255 - as_mov(ScratchRegister, asr(reg, 8), SetCond); - ma_mov(Imm32(0xff), reg, NoSetCond, NotEqual); - ma_mov(Imm32(0), reg, NoSetCond, Signed); + as_mov(ScratchRegister, asr(reg, 8), SetCC); + ma_mov(Imm32(0xff), reg, LeaveCC, NotEqual); + ma_mov(Imm32(0), reg, LeaveCC, Signed); } void incrementInt32Value(const Address& addr) { @@ -1782,7 +1778,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM emitSet(Assembler::Condition cond, Register dest) { ma_mov(Imm32(0), dest); - ma_mov(Imm32(1), dest, NoSetCond, cond); + ma_mov(Imm32(1), dest, LeaveCC, cond); } template @@ -1859,12 +1855,12 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM } void computeEffectiveAddress(const Address& address, Register dest) { - ma_add(address.base, Imm32(address.offset), dest, NoSetCond); + ma_add(address.base, Imm32(address.offset), dest, LeaveCC); } void computeEffectiveAddress(const BaseIndex& address, Register dest) { - ma_alu(address.base, lsl(address.index, address.scale), dest, OpAdd, NoSetCond); + ma_alu(address.base, lsl(address.index, address.scale), dest, OpAdd, LeaveCC); if (address.offset) - ma_add(dest, Imm32(address.offset), dest, NoSetCond); + ma_add(dest, Imm32(address.offset), dest, LeaveCC); } void floor(FloatRegister input, Register output, Label* handleNotAnInt); void floorf(FloatRegister input, Register output, Label* handleNotAnInt); @@ -1901,19 +1897,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM ma_add(addr.baseReg(), Imm32(addr.disp()), dest); } - void stackCheck(ImmWord limitAddr, Label* label) { - int* foo = 0; - *foo = 5; - movePtr(limitAddr, ScratchRegister); - ma_ldr(Address(ScratchRegister, 0), ScratchRegister); - ma_cmp(ScratchRegister, StackPointer); - ma_b(label, Assembler::AboveOrEqual); - } void abiret() { as_bx(lr); } - void ma_storeImm(Imm32 c, const Operand& dest) { + void ma_storeImm(Imm32 c, const Address& dest) { ma_mov(c, lr); ma_str(lr, dest); } diff --git a/js/src/jit/arm/MoveEmitter-arm.cpp b/js/src/jit/arm/MoveEmitter-arm.cpp index df054c82178b..92f2352b1ed0 100644 --- a/js/src/jit/arm/MoveEmitter-arm.cpp +++ b/js/src/jit/arm/MoveEmitter-arm.cpp @@ -38,43 +38,36 @@ MoveEmitterARM::~MoveEmitterARM() assertDone(); } -Operand +Address MoveEmitterARM::cycleSlot(uint32_t slot, uint32_t subslot) const { int32_t offset = masm.framePushed() - pushedAtCycle_; MOZ_ASSERT(offset < 4096 && offset > -4096); - return Operand(StackPointer, offset + slot * sizeof(double) + subslot); + return Address(StackPointer, offset + slot * sizeof(double) + subslot); } -// THIS IS ALWAYS AN LDRAddr. It should not be wrapped in an operand, methinks. -Operand +Address MoveEmitterARM::spillSlot() const { int32_t offset = masm.framePushed() - pushedAtSpill_; MOZ_ASSERT(offset < 4096 && offset > -4096); - return Operand(StackPointer, offset); + return Address(StackPointer, offset); } -Operand -MoveEmitterARM::toOperand(const MoveOperand& operand, bool isFloat) const +Address +MoveEmitterARM::toAddress(const MoveOperand& operand) const { - if (operand.isMemoryOrEffectiveAddress()) { - if (operand.base() != StackPointer) { - MOZ_ASSERT(operand.disp() < 1024 && operand.disp() > -1024); - return Operand(operand.base(), operand.disp()); - } + MOZ_ASSERT(operand.isMemoryOrEffectiveAddress()); - MOZ_ASSERT(operand.disp() >= 0); - - // Otherwise, the stack offset may need to be adjusted. - return Operand(StackPointer, operand.disp() + (masm.framePushed() - pushedAtStart_)); + if (operand.base() != StackPointer) { + MOZ_ASSERT(operand.disp() < 1024 && operand.disp() > -1024); + return Operand(operand.base(), operand.disp()).toAddress(); } - if (operand.isGeneralReg()) - return Operand(operand.reg()); + MOZ_ASSERT(operand.disp() >= 0); - MOZ_ASSERT(operand.isFloatReg()); - return Operand(operand.floatReg()); + // Otherwise, the stack offset may need to be adjusted. + return Address(StackPointer, operand.disp() + (masm.framePushed() - pushedAtStart_)); } Register @@ -113,7 +106,7 @@ MoveEmitterARM::breakCycle(const MoveOperand& from, const MoveOperand& to, case MoveOp::FLOAT32: if (to.isMemory()) { VFPRegister temp = ScratchFloat32Reg; - masm.ma_vldr(toOperand(to, true), temp); + masm.ma_vldr(toAddress(to), temp); // Since it is uncertain if the load will be aligned or not // just fill both of them with the same value. masm.ma_vstr(temp, cycleSlot(slotId, 0)); @@ -128,7 +121,7 @@ MoveEmitterARM::breakCycle(const MoveOperand& from, const MoveOperand& to, case MoveOp::DOUBLE: if (to.isMemory()) { FloatRegister temp = ScratchDoubleReg; - masm.ma_vldr(toOperand(to, true), temp); + masm.ma_vldr(toAddress(to), temp); masm.ma_vstr(temp, cycleSlot(slotId, 0)); } else { masm.ma_vstr(to.floatReg().doubleOverlay(), cycleSlot(slotId, 0)); @@ -139,7 +132,7 @@ MoveEmitterARM::breakCycle(const MoveOperand& from, const MoveOperand& to, // an non-vfp value if (to.isMemory()) { Register temp = tempReg(); - masm.ma_ldr(toOperand(to, false), temp); + masm.ma_ldr(toAddress(to), temp); masm.ma_str(temp, cycleSlot(0,0)); } else { if (to.reg() == spilledReg_) { @@ -170,7 +163,7 @@ MoveEmitterARM::completeCycle(const MoveOperand& from, const MoveOperand& to, Mo if (to.isMemory()) { FloatRegister temp = ScratchDoubleReg; masm.ma_vldr(cycleSlot(slotId, 0), temp); - masm.ma_vstr(temp, toOperand(to, true)); + masm.ma_vstr(temp, toAddress(to)); } else { uint32_t offset = 0; if ((!from.isMemory()) && from.floatReg().numAlignedAliased() == 1) @@ -184,7 +177,7 @@ MoveEmitterARM::completeCycle(const MoveOperand& from, const MoveOperand& to, Mo if (to.isMemory()) { Register temp = tempReg(); masm.ma_ldr(cycleSlot(slotId, 0), temp); - masm.ma_str(temp, toOperand(to, false)); + masm.ma_str(temp, toAddress(to)); } else { if (to.reg() == spilledReg_) { // Make sure we don't re-clobber the spilled register later. @@ -214,21 +207,14 @@ MoveEmitterARM::emitMove(const MoveOperand& from, const MoveOperand& to) masm.ma_ldr(spillSlot(), spilledReg_); spilledReg_ = InvalidReg; } - switch (toOperand(to, false).getTag()) { - case Operand::OP2: - // secretly must be a register + if (to.isMemoryOrEffectiveAddress()) + masm.ma_str(from.reg(), toAddress(to)); + else masm.ma_mov(from.reg(), to.reg()); - break; - case Operand::MEM: - masm.ma_str(from.reg(), toOperand(to, false)); - break; - default: - MOZ_CRASH("strange move!"); - } } else if (to.isGeneralReg()) { MOZ_ASSERT(from.isMemoryOrEffectiveAddress()); if (from.isMemory()) - masm.ma_ldr(toOperand(from, false), to.reg()); + masm.ma_ldr(toAddress(from), to.reg()); else masm.ma_add(from.base(), Imm32(from.disp()), to.reg()); } else { @@ -237,11 +223,11 @@ MoveEmitterARM::emitMove(const MoveOperand& from, const MoveOperand& to) MOZ_ASSERT(from.isMemoryOrEffectiveAddress()); if (from.isMemory()) - masm.ma_ldr(toOperand(from, false), reg); + masm.ma_ldr(toAddress(from), reg); else masm.ma_add(from.base(), Imm32(from.disp()), reg); MOZ_ASSERT(to.base() != reg); - masm.ma_str(reg, toOperand(to, false)); + masm.ma_str(reg, toAddress(to)); } } @@ -252,19 +238,15 @@ MoveEmitterARM::emitFloat32Move(const MoveOperand& from, const MoveOperand& to) if (to.isFloatReg()) masm.ma_vmov_f32(from.floatReg(), to.floatReg()); else - masm.ma_vstr(VFPRegister(from.floatReg()).singleOverlay(), - toOperand(to, true)); + masm.ma_vstr(VFPRegister(from.floatReg()).singleOverlay(), toAddress(to)); } else if (to.isFloatReg()) { - masm.ma_vldr(toOperand(from, true), - VFPRegister(to.floatReg()).singleOverlay()); + masm.ma_vldr(toAddress(from), VFPRegister(to.floatReg()).singleOverlay()); } else { // Memory to memory move. MOZ_ASSERT(from.isMemory()); FloatRegister reg = ScratchFloat32Reg; - masm.ma_vldr(toOperand(from, true), - VFPRegister(reg).singleOverlay()); - masm.ma_vstr(VFPRegister(reg).singleOverlay(), - toOperand(to, true)); + masm.ma_vldr(toAddress(from), VFPRegister(reg).singleOverlay()); + masm.ma_vstr(VFPRegister(reg).singleOverlay(), toAddress(to)); } } @@ -275,15 +257,15 @@ MoveEmitterARM::emitDoubleMove(const MoveOperand& from, const MoveOperand& to) if (to.isFloatReg()) masm.ma_vmov(from.floatReg(), to.floatReg()); else - masm.ma_vstr(from.floatReg(), toOperand(to, true)); + masm.ma_vstr(from.floatReg(), toAddress(to)); } else if (to.isFloatReg()) { - masm.ma_vldr(toOperand(from, true), to.floatReg()); + masm.ma_vldr(toAddress(from), to.floatReg()); } else { // Memory to memory move. MOZ_ASSERT(from.isMemory()); FloatRegister reg = ScratchDoubleReg; - masm.ma_vldr(toOperand(from, true), reg); - masm.ma_vstr(reg, toOperand(to, true)); + masm.ma_vldr(toAddress(from), reg); + masm.ma_vstr(reg, toAddress(to)); } } diff --git a/js/src/jit/arm/MoveEmitter-arm.h b/js/src/jit/arm/MoveEmitter-arm.h index fb7e6460f1de..70aafbdf6c0f 100644 --- a/js/src/jit/arm/MoveEmitter-arm.h +++ b/js/src/jit/arm/MoveEmitter-arm.h @@ -36,9 +36,9 @@ class MoveEmitterARM void assertDone(); Register tempReg(); FloatRegister tempFloatReg(); - Operand cycleSlot(uint32_t slot, uint32_t subslot) const; - Operand spillSlot() const; - Operand toOperand(const MoveOperand& operand, bool isFloat) const; + Address cycleSlot(uint32_t slot, uint32_t subslot) const; + Address spillSlot() const; + Address toAddress(const MoveOperand& operand) const; void emitMove(const MoveOperand& from, const MoveOperand& to); void emitFloat32Move(const MoveOperand& from, const MoveOperand& to); diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index 86773134f5ee..15dab0d1f79f 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -171,7 +171,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type) // Get a copy of the number of args to use as a decrement counter, also set // the zero condition code. - aasm->as_mov(r5, O2Reg(r1), SetCond); + aasm->as_mov(r5, O2Reg(r1), SetCC); // Loop over arguments, copying them from an unknown buffer onto the Ion // stack so they can be accessed from JIT'ed code. @@ -181,7 +181,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type) aasm->as_b(&footer, Assembler::Zero); // Get the top of the loop. masm.bind(&header); - aasm->as_sub(r5, r5, Imm8(1), SetCond); + aasm->as_sub(r5, r5, Imm8(1), SetCC); // We could be more awesome, and unroll this, using a loadm // (particularly since the offset is effectively 0) but that seems more // error prone, and complex. @@ -473,7 +473,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut) Label undefLoopTop; masm.bind(&undefLoopTop); masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex); - masm.ma_sub(r2, Imm32(1), r2, SetCond); + masm.ma_sub(r2, Imm32(1), r2, SetCC); masm.ma_b(&undefLoopTop, Assembler::NonZero); } @@ -490,7 +490,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut) masm.ma_dataTransferN(IsLoad, 64, true, r3, Imm32(-8), r4, PostIndex); masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex); - masm.ma_sub(r8, Imm32(1), r8, SetCond); + masm.ma_sub(r8, Imm32(1), r8, SetCC); masm.ma_b(©LoopTop, Assembler::NotSigned); } diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index a0ac77868461..393b349bfded 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2642,15 +2642,8 @@ ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(result) == 0); - if (Type == JSVAL_TYPE_MAGIC) { - if (!result->as().ensureElements(cx, len)) - return DenseElementResult::Failure; - } else { - if (result->as().capacity() < len) { - if (!result->as().growElements(cx, len)) - return DenseElementResult::Failure; - } - } + if (!EnsureBoxedOrUnboxedDenseElements(cx, result, len)) + return DenseElementResult::Failure; CopyBoxedOrUnboxedDenseElements(cx, result, obj1, 0, 0, initlen1); CopyBoxedOrUnboxedDenseElements(cx, result, obj2, initlen1, 0, initlen2); @@ -2899,6 +2892,20 @@ SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, Handl return true; } +template +static inline uint32_t +NormalizeSliceTerm(T value, uint32_t length) +{ + if (value < 0) { + value += length; + if (value < 0) + return 0; + } else if (double(value) > double(length)) { + return length; + } + return uint32_t(value); +} + bool js::array_slice(JSContext* cx, unsigned argc, Value* vp) { @@ -2918,26 +2925,12 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp) double d; if (!ToInteger(cx, args[0], &d)) return false; - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - begin = (uint32_t)d; + begin = NormalizeSliceTerm(d, length); if (args.hasDefined(1)) { if (!ToInteger(cx, args[1], &d)) return false; - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - end = (uint32_t)d; + end = NormalizeSliceTerm(d, length); } } @@ -3000,6 +2993,57 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp) return true; } +template +DenseElementResult +ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result) +{ + int32_t length = GetAnyBoxedOrUnboxedArrayLength(obj); + + uint32_t begin = NormalizeSliceTerm(beginArg, length); + uint32_t end = NormalizeSliceTerm(endArg, length); + + if (begin > end) + begin = end; + + size_t initlen = GetBoxedOrUnboxedInitializedLength(obj); + if (initlen > begin) { + size_t count = Min(initlen - begin, end - begin); + if (count) { + if (!EnsureBoxedOrUnboxedDenseElements(cx, result, count)) + return DenseElementResult::Failure; + CopyBoxedOrUnboxedDenseElements(cx, result, obj, 0, begin, count); + } + } + + SetAnyBoxedOrUnboxedArrayLength(cx, result, end - begin); + return DenseElementResult::Success; +} + +DefineBoxedOrUnboxedFunctor5(ArraySliceDenseKernel, + JSContext*, JSObject*, int32_t, int32_t, JSObject*); + +JSObject* +js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end, + HandleObject result) +{ + if (result) { + ArraySliceDenseKernelFunctor functor(cx, obj, begin, end, result); + DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result); + MOZ_ASSERT(rv != DenseElementResult::Incomplete); + return rv == DenseElementResult::Success ? result : nullptr; + } + + // Slower path if the JIT wasn't able to allocate an object inline. + JS::AutoValueArray<4> argv(cx); + argv[0].setUndefined(); + argv[1].setObject(*obj); + argv[2].setInt32(begin); + argv[3].setInt32(end); + if (!array_slice(cx, 2, argv.begin())) + return nullptr; + return &argv[0].toObject(); +} + /* ES5 15.4.4.20. */ static bool array_filter(JSContext* cx, unsigned argc, Value* vp) diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 24b6711a7c7e..797884a61538 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -180,6 +180,9 @@ array_unshift(JSContext* cx, unsigned argc, js::Value* vp); extern bool array_slice(JSContext* cx, unsigned argc, js::Value* vp); +extern JSObject* +array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end, HandleObject result); + /* * Append the given (non-hole) value to the end of an array. The array must be * a newborn array -- that is, one which has not been exposed to script for diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index f561d414e1d7..7c2f13c1c81c 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -7092,9 +7092,33 @@ JS::AbortIncrementalGC(JSRuntime* rt) } char16_t* -JS::GCDescription::formatMessage(JSRuntime* rt) const +JS::GCDescription::formatSliceMessage(JSRuntime* rt) const { - return rt->gc.stats.formatMessage(); + UniqueChars cstr = rt->gc.stats.formatCompactSliceMessage(); + + size_t nchars = strlen(cstr.get()); + UniquePtr out(js_pod_malloc(nchars + 1)); + if (!out) + return nullptr; + out.get()[nchars] = 0; + + CopyAndInflateChars(out.get(), cstr.get(), nchars); + return out.release(); +} + +char16_t* +JS::GCDescription::formatSummaryMessage(JSRuntime* rt) const +{ + UniqueChars cstr = rt->gc.stats.formatCompactSummaryMessage(); + + size_t nchars = strlen(cstr.get()); + UniquePtr out(js_pod_malloc(nchars + 1)); + if (!out) + return nullptr; + out.get()[nchars] = 0; + + CopyAndInflateChars(out.get(), cstr.get(), nchars); + return out.release(); } JS::dbg::GarbageCollectionEvent::Ptr diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index b7a314afe973..53054e88da42 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -13,6 +13,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/FloatingPoint.h" +#include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" #include @@ -1654,6 +1655,58 @@ SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleValue receiver, result.checkStrictErrorOrWarning(cx, obj, id, strict); } +/* + * As an optimization, the interpreter creates a handful of reserved Rooted + * variables at the beginning, thus inserting them into the Rooted list once + * upon entry. ReservedRooted "borrows" a reserved Rooted variable and uses it + * within a local scope, resetting the value to nullptr (or the appropriate + * equivalent for T) at scope end. This avoids inserting/removing the Rooted + * from the rooter list, while preventing stale values from being kept alive + * unnecessarily. + */ + +template +class ReservedRootedBase { +}; + +template +class ReservedRooted : public ReservedRootedBase +{ + Rooted* savedRoot; + + public: + ReservedRooted(Rooted* root, const T& ptr) : savedRoot(root) { + *root = ptr; + } + + explicit ReservedRooted(Rooted* root) : savedRoot(root) { + *root = js::GCMethods::initial(); + } + + ~ReservedRooted() { + *savedRoot = js::GCMethods::initial(); + } + + void set(const T& p) const { *savedRoot = p; } + operator Handle() { return *savedRoot; } + operator Rooted&() { return *savedRoot; } + MutableHandle operator&() { return &*savedRoot; } + + DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot->get()) + DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot->get()) + DECLARE_POINTER_CONSTREF_OPS(T) + DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T) +}; + +template <> +class ReservedRootedBase : public ValueOperations> +{ + friend class ValueOperations>; + const Value* extract() const { + return static_cast*>(this)->address(); + } +}; + static MOZ_NEVER_INLINE bool Interpret(JSContext* cx, RunState& state) { @@ -2038,11 +2091,9 @@ END_CASE(JSOP_SETRVAL) CASE(JSOP_ENTERWITH) { - RootedValue& val = rootValue0; - RootedObject& staticWith = rootObject0; - val = REGS.sp[-1]; + ReservedRooted val(&rootValue0, REGS.sp[-1]); REGS.sp--; - staticWith = script->getObject(REGS.pc); + ReservedRooted staticWith(&rootObject0, script->getObject(REGS.pc)); if (!EnterWithOperation(cx, REGS.fp(), val, staticWith)) goto error; @@ -2170,13 +2221,14 @@ CASE(JSOP_IN) ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, nullptr); goto error; } - RootedObject& obj = rootObject0; - obj = &rref.toObject(); - RootedId& id = rootId0; - FETCH_ELEMENT_ID(-2, id); bool found; - if (!HasProperty(cx, obj, id, &found)) - goto error; + { + ReservedRooted obj(&rootObject0, &rref.toObject()); + ReservedRooted id(&rootId0); + FETCH_ELEMENT_ID(-2, id); + if (!HasProperty(cx, obj, id, &found)) + goto error; + } TRY_BRANCH_AFTER_COND(found, 2); REGS.sp--; REGS.sp[-1].setBoolean(found); @@ -2199,8 +2251,7 @@ CASE(JSOP_MOREITER) MOZ_ASSERT(REGS.stackDepth() >= 1); MOZ_ASSERT(REGS.sp[-1].isObject()); PUSH_NULL(); - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); if (!IteratorMore(cx, obj, REGS.stackHandleAt(-1))) goto error; } @@ -2216,8 +2267,7 @@ END_CASE(JSOP_ISNOITER) CASE(JSOP_ENDITER) { MOZ_ASSERT(REGS.stackDepth() >= 1); - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); bool ok = CloseIterator(cx, obj); REGS.sp--; if (!ok) @@ -2264,14 +2314,9 @@ END_CASE(JSOP_PICK) CASE(JSOP_SETCONST) { - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); - - RootedValue& rval = rootValue0; - rval = REGS.sp[-1]; - - RootedObject& obj = rootObject0; - obj = ®S.fp()->varObj(); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); + ReservedRooted rval(&rootValue0, REGS.sp[-1]); + ReservedRooted obj(&rootObject0, ®S.fp()->varObj()); if (!SetConstOperation(cx, obj, name, rval)) goto error; @@ -2287,14 +2332,11 @@ CASE(JSOP_BINDNAME) { JSOp op = JSOp(*REGS.pc); if (op == JSOP_BINDNAME || script->hasPollutedGlobalScope()) { - RootedObject& scopeChain = rootObject0; - scopeChain = REGS.fp()->scopeChain(); - - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); + ReservedRooted scopeChain(&rootObject0, REGS.fp()->scopeChain()); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); /* Assigning to an undeclared name adds a property to the global object. */ - RootedObject& scope = rootObject1; + ReservedRooted scope(&rootObject1); if (!LookupNameUnqualified(cx, name, scopeChain, &scope)) goto error; @@ -2482,10 +2524,8 @@ END_CASE(JSOP_ADD) CASE(JSOP_SUB) { - RootedValue& lval = rootValue0; - RootedValue& rval = rootValue1; - lval = REGS.sp[-2]; - rval = REGS.sp[-1]; + ReservedRooted lval(&rootValue0, REGS.sp[-2]); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-2); if (!SubOperation(cx, lval, rval, res)) goto error; @@ -2495,10 +2535,8 @@ END_CASE(JSOP_SUB) CASE(JSOP_MUL) { - RootedValue& lval = rootValue0; - RootedValue& rval = rootValue1; - lval = REGS.sp[-2]; - rval = REGS.sp[-1]; + ReservedRooted lval(&rootValue0, REGS.sp[-2]); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-2); if (!MulOperation(cx, lval, rval, res)) goto error; @@ -2508,10 +2546,8 @@ END_CASE(JSOP_MUL) CASE(JSOP_DIV) { - RootedValue& lval = rootValue0; - RootedValue& rval = rootValue1; - lval = REGS.sp[-2]; - rval = REGS.sp[-1]; + ReservedRooted lval(&rootValue0, REGS.sp[-2]); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-2); if (!DivOperation(cx, lval, rval, res)) goto error; @@ -2521,10 +2557,8 @@ END_CASE(JSOP_DIV) CASE(JSOP_MOD) { - RootedValue& lval = rootValue0; - RootedValue& rval = rootValue1; - lval = REGS.sp[-2]; - rval = REGS.sp[-1]; + ReservedRooted lval(&rootValue0, REGS.sp[-2]); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-2); if (!ModOperation(cx, lval, rval, res)) goto error; @@ -2552,8 +2586,7 @@ END_CASE(JSOP_BITNOT) CASE(JSOP_NEG) { - RootedValue& val = rootValue0; - val = REGS.sp[-1]; + ReservedRooted val(&rootValue0, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-1); if (!NegOperation(cx, script, REGS.pc, val, res)) goto error; @@ -2567,11 +2600,8 @@ END_CASE(JSOP_POS) CASE(JSOP_DELNAME) { - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); - - RootedObject& scopeObj = rootObject0; - scopeObj = REGS.fp()->scopeChain(); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); + ReservedRooted scopeObj(&rootObject0, REGS.fp()->scopeChain()); PUSH_BOOLEAN(true); MutableHandleValue res = REGS.stackHandleAt(-1); @@ -2585,10 +2615,8 @@ CASE(JSOP_STRICTDELPROP) { static_assert(JSOP_DELPROP_LENGTH == JSOP_STRICTDELPROP_LENGTH, "delprop and strictdelprop must be the same size"); - RootedId& id = rootId0; - id = NameToId(script->getName(REGS.pc)); - - RootedObject& obj = rootObject0; + ReservedRooted id(&rootId0, NameToId(script->getName(REGS.pc))); + ReservedRooted obj(&rootObject0); FETCH_OBJECT(cx, -1, obj); ObjectOpResult result; @@ -2609,14 +2637,13 @@ CASE(JSOP_STRICTDELELEM) static_assert(JSOP_DELELEM_LENGTH == JSOP_STRICTDELELEM_LENGTH, "delelem and strictdelelem must be the same size"); /* Fetch the left part and resolve it to a non-null object. */ - RootedObject& obj = rootObject0; + ReservedRooted obj(&rootObject0); FETCH_OBJECT(cx, -2, obj); - RootedValue& propval = rootValue0; - propval = REGS.sp[-1]; + ReservedRooted propval(&rootValue0, REGS.sp[-1]); ObjectOpResult result; - RootedId& id = rootId0; + ReservedRooted id(&rootId0); if (!ValueToId(cx, propval, &id)) goto error; if (!DeleteProperty(cx, obj, id, result)) @@ -2639,11 +2666,8 @@ CASE(JSOP_TOID) * but we need to avoid the observable stringification the second time. * There must be an object value below the id, which will not be popped. */ - RootedValue& objval = rootValue0; - RootedValue& idval = rootValue1; - objval = REGS.sp[-2]; - idval = REGS.sp[-1]; - + ReservedRooted objval(&rootValue0, REGS.sp[-2]); + ReservedRooted idval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-1); if (!ToIdOperation(cx, script, REGS.pc, objval, idval, res)) goto error; @@ -2682,12 +2706,9 @@ END_CASE(JSOP_GETPROP) CASE(JSOP_GETPROP_SUPER) { - RootedObject& receiver = rootObject0; - RootedObject& obj = rootObject1; - + ReservedRooted receiver(&rootObject0); FETCH_OBJECT(cx, -2, receiver); - obj = ®S.sp[-1].toObject(); - + ReservedRooted obj(&rootObject1, ®S.sp[-1].toObject()); MutableHandleValue rref = REGS.stackHandleAt(-2); if (!GetProperty(cx, obj, receiver, script->getName(REGS.pc), rref)) @@ -2699,10 +2720,8 @@ END_CASE(JSOP_GETPROP_SUPER) CASE(JSOP_GETXPROP) { - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); - RootedId& id = rootId0; - id = NameToId(script->getName(REGS.pc)); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); + ReservedRooted id(&rootId0, NameToId(script->getName(REGS.pc))); MutableHandleValue rval = REGS.stackHandleAt(-1); if (!GetPropertyForNameLookup(cx, obj, id, rval)) goto error; @@ -2736,8 +2755,7 @@ CASE(JSOP_STRICTSETNAME) static_assert(JSOP_SETNAME_LENGTH == JSOP_SETGNAME_LENGTH, "We're sharing the END_CASE so the lengths better match"); - RootedObject& scope = rootObject0; - scope = ®S.sp[-2].toObject(); + ReservedRooted scope(&rootObject0, ®S.sp[-2].toObject()); HandleValue value = REGS.stackHandleAt(-1); if (!SetNameOperation(cx, script, REGS.pc, scope, value)) @@ -2756,8 +2774,7 @@ CASE(JSOP_STRICTSETPROP) HandleValue lval = REGS.stackHandleAt(-2); HandleValue rval = REGS.stackHandleAt(-1); - RootedId& id = rootId0; - id = NameToId(script->getName(REGS.pc)); + ReservedRooted id(&rootId0, NameToId(script->getName(REGS.pc))); if (!SetPropertyOperation(cx, JSOp(*REGS.pc), lval, id, rval)) goto error; @@ -2773,17 +2790,10 @@ CASE(JSOP_STRICTSETPROP_SUPER) "setprop-super and strictsetprop-super must be the same size"); - RootedValue& receiver = rootValue0; - receiver = REGS.sp[-3]; - - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); - - RootedValue& rval = rootValue1; - rval = REGS.sp[-1]; - - RootedId& id = rootId0; - id = NameToId(script->getName(REGS.pc)); + ReservedRooted receiver(&rootValue0, REGS.sp[-3]); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); + ReservedRooted id(&rootId0, NameToId(script->getName(REGS.pc))); ObjectOpResult result; if (!SetProperty(cx, obj, id, rval, receiver, result)) @@ -2822,10 +2832,9 @@ END_CASE(JSOP_GETELEM) CASE(JSOP_GETELEM_SUPER) { HandleValue rval = REGS.stackHandleAt(-3); - RootedObject& receiver = rootObject0; + ReservedRooted receiver(&rootObject0); FETCH_OBJECT(cx, -2, receiver); - RootedObject& obj = rootObject1; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject1, ®S.sp[-1].toObject()); MutableHandleValue res = REGS.stackHandleAt(-3); @@ -2845,13 +2854,12 @@ CASE(JSOP_STRICTSETELEM) { static_assert(JSOP_SETELEM_LENGTH == JSOP_STRICTSETELEM_LENGTH, "setelem and strictsetelem must be the same size"); - RootedObject& obj = rootObject0; + ReservedRooted obj(&rootObject0); FETCH_OBJECT(cx, -3, obj); - RootedId& id = rootId0; + ReservedRooted id(&rootId0); FETCH_ELEMENT_ID(-2, id); Value& value = REGS.sp[-1]; - RootedValue& receiver = rootValue0; - receiver = ObjectValue(*obj); + ReservedRooted receiver(&rootValue0, ObjectValue(*obj)); if (!SetObjectElementOperation(cx, obj, receiver, id, value, *REGS.pc == JSOP_STRICTSETELEM)) goto error; REGS.sp[-3] = value; @@ -2865,12 +2873,10 @@ CASE(JSOP_STRICTSETELEM_SUPER) static_assert(JSOP_SETELEM_SUPER_LENGTH == JSOP_STRICTSETELEM_SUPER_LENGTH, "setelem-super and strictsetelem-super must be the same size"); - RootedId& id = rootId0; + ReservedRooted id(&rootId0); FETCH_ELEMENT_ID(-4, id); - RootedValue& receiver = rootValue0; - receiver = REGS.sp[-3]; - RootedObject& obj = rootObject1; - obj = ®S.sp[-2].toObject(); + ReservedRooted receiver(&rootValue0, REGS.sp[-3]); + ReservedRooted obj(&rootObject1, ®S.sp[-2].toObject()); Value& value = REGS.sp[-1]; bool strict = JSOp(*REGS.pc) == JSOP_STRICTSETELEM_SUPER; @@ -2943,11 +2949,11 @@ CASE(JSOP_FUNCALL) bool construct = (*REGS.pc == JSOP_NEW); - RootedFunction& fun = rootFunction0; - bool isFunction = IsFunctionObject(args.calleev(), fun.address()); + JSFunction* maybeFun; + bool isFunction = IsFunctionObject(args.calleev(), &maybeFun); /* Don't bother trying to fast-path calls to scripted non-constructors. */ - if (!isFunction || !fun->isInterpreted() || !fun->isConstructor()) { + if (!isFunction || !maybeFun->isInterpreted() || !maybeFun->isConstructor()) { if (construct) { if (!InvokeConstructor(cx, args)) goto error; @@ -2961,27 +2967,30 @@ CASE(JSOP_FUNCALL) ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH); } - RootedScript& funScript = rootScript0; - funScript = fun->getOrCreateScript(cx); - if (!funScript) - goto error; - - InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; - bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc); - - TypeMonitorCall(cx, args, construct); - { - InvokeState state(cx, args, initial); + MOZ_ASSERT(maybeFun); + ReservedRooted fun(&rootFunction0, maybeFun); + ReservedRooted funScript(&rootScript0, fun->getOrCreateScript(cx)); + if (!funScript) + goto error; + + InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; + bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc); + + TypeMonitorCall(cx, args, construct); + + mozilla::Maybe state; + state.emplace(cx, args, initial); + if (createSingleton) - state.setCreateSingleton(); + state->setCreateSingleton(); if (!createSingleton && jit::IsIonEnabled(cx)) { - jit::MethodStatus status = jit::CanEnter(cx, state); + jit::MethodStatus status = jit::CanEnter(cx, state.ref()); if (status == jit::Method_Error) goto error; if (status == jit::Method_Compiled) { - jit::JitExecStatus exec = jit::IonCannon(cx, state); + jit::JitExecStatus exec = jit::IonCannon(cx, state.ref()); CHECK_BRANCH(); REGS.sp = args.spAfterCall(); interpReturnOK = !IsErrorStatus(exec); @@ -2990,26 +2999,28 @@ CASE(JSOP_FUNCALL) } if (jit::IsBaselineEnabled(cx)) { - jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state); + jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state.ref()); if (status == jit::Method_Error) goto error; if (status == jit::Method_Compiled) { - jit::JitExecStatus exec = jit::EnterBaselineMethod(cx, state); + jit::JitExecStatus exec = jit::EnterBaselineMethod(cx, state.ref()); CHECK_BRANCH(); REGS.sp = args.spAfterCall(); interpReturnOK = !IsErrorStatus(exec); goto jit_return; } } + + state.reset(); + funScript = fun->nonLazyScript(); + + if (!activation.pushInlineFrame(args, funScript, initial)) + goto error; + + if (createSingleton) + REGS.fp()->setCreateSingleton(); } - funScript = fun->nonLazyScript(); - if (!activation.pushInlineFrame(args, funScript, initial)) - goto error; - - if (createSingleton) - REGS.fp()->setCreateSingleton(); - SET_SCRIPT(REGS.fp()->script()); { @@ -3051,17 +3062,13 @@ CASE(JSOP_GIMPLICITTHIS) { JSOp op = JSOp(*REGS.pc); if (op == JSOP_IMPLICITTHIS || script->hasPollutedGlobalScope()) { - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); - - RootedObject& scopeObj = rootObject0; - scopeObj = REGS.fp()->scopeChain(); - - RootedObject& scope = rootObject1; + ReservedRooted name(&rootName0, script->getName(REGS.pc)); + ReservedRooted scopeObj(&rootObject0, REGS.fp()->scopeChain()); + ReservedRooted scope(&rootObject1); if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope)) goto error; - RootedValue& v = rootValue0; + ReservedRooted v(&rootValue0); if (!ComputeImplicitThis(cx, scope, &v)) goto error; PUSH_COPY(v); @@ -3077,8 +3084,7 @@ END_CASE(JSOP_IMPLICITTHIS) CASE(JSOP_GETGNAME) CASE(JSOP_GETNAME) { - RootedValue& rval = rootValue0; - + ReservedRooted rval(&rootValue0); if (!GetNameOperation(cx, REGS.fp(), REGS.pc, &rval)) goto error; @@ -3091,8 +3097,7 @@ END_CASE(JSOP_GETNAME) CASE(JSOP_GETINTRINSIC) { - RootedValue& rval = rootValue0; - + ReservedRooted rval(&rootValue0); if (!GetIntrinsicOperation(cx, REGS.pc, &rval)) goto error; @@ -3148,8 +3153,7 @@ END_CASE(JSOP_SYMBOL) CASE(JSOP_OBJECT) { - RootedObject& ref = rootObject0; - ref = script->getObject(REGS.pc); + ReservedRooted ref(&rootObject0, script->getObject(REGS.pc)); if (JS::CompartmentOptionsRef(cx).cloneSingletons()) { JSObject* obj = DeepCloneObjectLiteral(cx, ref, TenuredObject); if (!obj) @@ -3165,12 +3169,9 @@ END_CASE(JSOP_OBJECT) CASE(JSOP_CALLSITEOBJ) { - RootedObject& cso = rootObject0; - cso = script->getObject(REGS.pc); - RootedObject& raw = rootObject1; - raw = script->getObject(GET_UINT32_INDEX(REGS.pc) + 1); - RootedValue& rawValue = rootValue0; - rawValue.setObject(*raw); + ReservedRooted cso(&rootObject0, script->getObject(REGS.pc)); + ReservedRooted raw(&rootObject1, script->getObject(GET_UINT32_INDEX(REGS.pc) + 1)); + ReservedRooted rawValue(&rootValue0, ObjectValue(*raw)); if (!ProcessCallSiteObjOperation(cx, cso, raw, rawValue)) goto error; @@ -3270,8 +3271,7 @@ END_CASE(JSOP_RUNONCE) CASE(JSOP_REST) { - RootedObject& rest = rootObject0; - rest = REGS.fp()->createRestParameter(cx); + ReservedRooted rest(&rootObject0, REGS.fp()->createRestParameter(cx)); if (!rest) goto error; PUSH_COPY(ObjectValue(*rest)); @@ -3281,8 +3281,7 @@ END_CASE(JSOP_REST) CASE(JSOP_GETALIASEDVAR) { ScopeCoordinate sc = ScopeCoordinate(REGS.pc); - RootedValue& val = rootValue0; - val = REGS.fp()->aliasedVarScope(sc).aliasedVar(sc); + ReservedRooted val(&rootValue0, REGS.fp()->aliasedVarScope(sc).aliasedVar(sc)); MOZ_ASSERT(!IsUninitializedLexical(val)); PUSH_COPY(val); TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]); @@ -3300,8 +3299,7 @@ END_CASE(JSOP_SETALIASEDVAR) CASE(JSOP_CHECKLEXICAL) { uint32_t i = GET_LOCALNO(REGS.pc); - RootedValue& val = rootValue0; - val = REGS.fp()->unaliasedLocal(i); + ReservedRooted val(&rootValue0, REGS.fp()->unaliasedLocal(i)); if (!CheckUninitializedLexical(cx, script, REGS.pc, val)) goto error; } @@ -3317,8 +3315,7 @@ END_CASE(JSOP_INITLEXICAL) CASE(JSOP_CHECKALIASEDLEXICAL) { ScopeCoordinate sc = ScopeCoordinate(REGS.pc); - RootedValue& val = rootValue0; - val = REGS.fp()->aliasedVarScope(sc).aliasedVar(sc); + ReservedRooted val(&rootValue0, REGS.fp()->aliasedVarScope(sc).aliasedVar(sc)); if (!CheckUninitializedLexical(cx, script, REGS.pc, val)) goto error; } @@ -3392,11 +3389,8 @@ CASE(JSOP_DEFVAR) attrs |= JSPROP_PERMANENT; /* Step 8b. */ - RootedObject& obj = rootObject0; - obj = ®S.fp()->varObj(); - - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); + ReservedRooted obj(&rootObject0, ®S.fp()->varObj()); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); if (!DefVarOrConstOperation(cx, obj, name, attrs)) goto error; @@ -3411,9 +3405,7 @@ CASE(JSOP_DEFFUN) * a compound statement (not at the top statement level of global code, or * at the top level of a function body). */ - RootedFunction& fun = rootFunction0; - fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); - + ReservedRooted fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); if (!DefFunOperation(cx, script, REGS.fp()->scopeChain(), fun)) goto error; } @@ -3422,9 +3414,7 @@ END_CASE(JSOP_DEFFUN) CASE(JSOP_LAMBDA) { /* Load the specified function object literal. */ - RootedFunction& fun = rootFunction0; - fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); - + ReservedRooted fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); JSObject* obj = Lambda(cx, fun, REGS.fp()->scopeChain()); if (!obj) goto error; @@ -3436,10 +3426,8 @@ END_CASE(JSOP_LAMBDA) CASE(JSOP_LAMBDA_ARROW) { /* Load the specified function object literal. */ - RootedFunction& fun = rootFunction0; - fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); - RootedValue& thisv = rootValue0; - thisv = REGS.sp[-1]; + ReservedRooted fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); + ReservedRooted thisv(&rootValue0, REGS.sp[-1]); JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), thisv); if (!obj) goto error; @@ -3456,14 +3444,11 @@ END_CASE(JSOP_CALLEE) CASE(JSOP_INITPROP_GETTER) CASE(JSOP_INITPROP_SETTER) { - RootedObject& obj = rootObject0; - RootedPropertyName& name = rootName0; - RootedObject& val = rootObject1; - MOZ_ASSERT(REGS.stackDepth() >= 2); - obj = ®S.sp[-2].toObject(); - name = script->getName(REGS.pc); - val = ®S.sp[-1].toObject(); + + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); + ReservedRooted val(&rootObject1, ®S.sp[-1].toObject()); if (!InitGetterSetterOperation(cx, REGS.pc, obj, name, val)) goto error; @@ -3475,14 +3460,11 @@ END_CASE(JSOP_INITPROP_GETTER) CASE(JSOP_INITELEM_GETTER) CASE(JSOP_INITELEM_SETTER) { - RootedObject& obj = rootObject0; - RootedValue& idval = rootValue0; - RootedObject& val = rootObject1; - MOZ_ASSERT(REGS.stackDepth() >= 3); - obj = ®S.sp[-3].toObject(); - idval = REGS.sp[-2]; - val = ®S.sp[-1].toObject(); + + ReservedRooted obj(&rootObject0, ®S.sp[-3].toObject()); + ReservedRooted idval(&rootValue0, REGS.sp[-2]); + ReservedRooted val(&rootObject1, ®S.sp[-1].toObject()); if (!InitGetterSetterOperation(cx, REGS.pc, obj, idval, val)) goto error; @@ -3524,13 +3506,11 @@ END_CASE(JSOP_NEWARRAY) CASE(JSOP_NEWARRAY_COPYONWRITE) { - RootedObject& baseobj = rootObject0; - baseobj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, script, REGS.pc); + ReservedRooted baseobj(&rootObject0, ObjectGroup::getOrFixupCopyOnWriteObject(cx, script, REGS.pc)); if (!baseobj) goto error; - RootedObject& obj = rootObject1; - obj = NewDenseCopyOnWriteArray(cx, baseobj.as(), gc::DefaultHeap); + ReservedRooted obj(&rootObject1, NewDenseCopyOnWriteArray(cx, ((RootedObject&)(baseobj)).as(), gc::DefaultHeap)); if (!obj) goto error; @@ -3552,11 +3532,8 @@ CASE(JSOP_MUTATEPROTO) MOZ_ASSERT(REGS.stackDepth() >= 2); if (REGS.sp[-1].isObjectOrNull()) { - RootedObject& newProto = rootObject1; - rootObject1 = REGS.sp[-1].toObjectOrNull(); - - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); + ReservedRooted newProto(&rootObject1, REGS.sp[-1].toObjectOrNull()); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); MOZ_ASSERT(obj->is()); if (!SetPrototype(cx, obj, newProto)) @@ -3577,12 +3554,10 @@ CASE(JSOP_INITHIDDENPROP) "initprop and inithiddenprop must be the same size"); /* Load the property's initial value into rval. */ MOZ_ASSERT(REGS.stackDepth() >= 2); - RootedValue& rval = rootValue0; - rval = REGS.sp[-1]; + ReservedRooted rval(&rootValue0, REGS.sp[-1]); /* Load the object being initialized into lval/obj. */ - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); PropertyName* name = script->getName(REGS.pc); @@ -3602,8 +3577,7 @@ CASE(JSOP_INITELEM) HandleValue val = REGS.stackHandleAt(-1); HandleValue id = REGS.stackHandleAt(-2); - RootedObject& obj = rootObject0; - obj = ®S.sp[-3].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-3].toObject()); if (!InitElemOperation(cx, obj, id, val)) goto error; @@ -3617,8 +3591,7 @@ CASE(JSOP_INITELEM_ARRAY) MOZ_ASSERT(REGS.stackDepth() >= 2); HandleValue val = REGS.stackHandleAt(-1); - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); uint32_t index = GET_UINT24(REGS.pc); if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val)) @@ -3633,8 +3606,7 @@ CASE(JSOP_INITELEM_INC) MOZ_ASSERT(REGS.stackDepth() >= 3); HandleValue val = REGS.stackHandleAt(-1); - RootedObject& obj = rootObject0; - obj = ®S.sp[-3].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-3].toObject()); uint32_t index = REGS.sp[-2].toInt32(); if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val)) @@ -3693,7 +3665,7 @@ END_CASE(JSOP_FINALLY) CASE(JSOP_THROWING) { - RootedValue& v = rootValue0; + ReservedRooted v(&rootValue0); POP_COPY_TO(v); MOZ_ALWAYS_TRUE(ThrowingOperation(cx, v)); } @@ -3702,7 +3674,7 @@ END_CASE(JSOP_THROWING) CASE(JSOP_THROW) { CHECK_BRANCH(); - RootedValue& v = rootValue0; + ReservedRooted v(&rootValue0); POP_COPY_TO(v); JS_ALWAYS_FALSE(Throw(cx, v)); /* let the code at error try to catch the exception. */ @@ -3711,14 +3683,12 @@ CASE(JSOP_THROW) CASE(JSOP_INSTANCEOF) { - RootedValue& rref = rootValue0; - rref = REGS.sp[-1]; - if (rref.isPrimitive()) { + ReservedRooted rref(&rootValue0, REGS.sp[-1]); + if (HandleValue(rref).isPrimitive()) { ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, nullptr); goto error; } - RootedObject& obj = rootObject0; - obj = &rref.toObject(); + ReservedRooted obj(&rootObject0, &rref.toObject()); bool cond = false; if (!HasInstance(cx, obj, REGS.stackHandleAt(-2), &cond)) goto error; @@ -3814,8 +3784,7 @@ CASE(JSOP_INITIALYIELD) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); POP_RETURN_VALUE(); MOZ_ASSERT(REGS.stackDepth() == 0); if (!GeneratorObject::initialSuspend(cx, obj, REGS.fp(), REGS.pc)) @@ -3827,8 +3796,7 @@ CASE(JSOP_YIELD) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); if (!GeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc, REGS.spForStackDepth(0), REGS.stackDepth() - 2)) { @@ -3843,19 +3811,18 @@ CASE(JSOP_YIELD) CASE(JSOP_RESUME) { - RootedObject& gen = rootObject0; - RootedValue& val = rootValue0; - val = REGS.sp[-1]; - gen = ®S.sp[-2].toObject(); - // popInlineFrame expects there to be an additional value on the stack to - // pop off, so leave "gen" on the stack. - - GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(REGS.pc); - bool ok = GeneratorObject::resume(cx, activation, gen, val, resumeKind); - SET_SCRIPT(REGS.fp()->script()); - if (!ok) - goto error; + { + ReservedRooted gen(&rootObject0, ®S.sp[-2].toObject()); + ReservedRooted val(&rootValue0, REGS.sp[-1]); + // popInlineFrame expects there to be an additional value on the stack + // to pop off, so leave "gen" on the stack. + GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(REGS.pc); + bool ok = GeneratorObject::resume(cx, activation, gen, val, resumeKind); + SET_SCRIPT(REGS.fp()->script()); + if (!ok) + goto error; + } ADVANCE_AND_DISPATCH(0); } @@ -3869,8 +3836,7 @@ END_CASE(JSOP_DEBUGAFTERYIELD) CASE(JSOP_FINALYIELDRVAL) { - RootedObject& gen = rootObject0; - gen = ®S.sp[-1].toObject(); + ReservedRooted gen(&rootObject0, ®S.sp[-1].toObject()); REGS.sp--; if (!GeneratorObject::finalSuspend(cx, gen)) { @@ -3883,8 +3849,7 @@ CASE(JSOP_FINALYIELDRVAL) CASE(JSOP_ARRAYPUSH) { - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); if (!NewbornArrayPush(cx, obj, REGS.sp[-2])) goto error; REGS.sp -= 2; @@ -3893,13 +3858,12 @@ END_CASE(JSOP_ARRAYPUSH) CASE(JSOP_CLASSHERITAGE) { - RootedValue& val = rootValue0; - val = REGS.sp[-1]; + ReservedRooted val(&rootValue0, REGS.sp[-1]); - RootedValue& objProto = rootValue1; - RootedObject& funcProto = rootObject0; + ReservedRooted objProto(&rootValue1); + ReservedRooted funcProto(&rootObject0); if (val.isNull()) { - objProto.setNull(); + objProto = NullValue(); if (!GetBuiltinPrototype(cx, JSProto_Function, &funcProto)) goto error; } else { @@ -3926,12 +3890,10 @@ END_CASE(JSOP_CLASSHERITAGE) CASE(JSOP_FUNWITHPROTO) { - RootedObject& proto = rootObject1; - proto = ®S.sp[-1].toObject(); + ReservedRooted proto(&rootObject1, ®S.sp[-1].toObject()); /* Load the specified function object literal. */ - RootedFunction& fun = rootFunction0; - fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); + ReservedRooted fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); JSObject* obj = CloneFunctionObjectIfNotSingleton(cx, fun, REGS.fp()->scopeChain(), proto, GenericObject); @@ -3944,8 +3906,7 @@ END_CASE(JSOP_FUNWITHPROTO) CASE(JSOP_OBJWITHPROTO) { - RootedObject& proto = rootObject0; - proto = REGS.sp[-1].toObjectOrNull(); + ReservedRooted proto(&rootObject0, REGS.sp[-1].toObjectOrNull()); JSObject* obj = NewObjectWithGivenProto(cx, proto); if (!obj) @@ -3961,12 +3922,11 @@ CASE(JSOP_INITHOMEOBJECT) MOZ_ASSERT(REGS.stackDepth() >= skipOver + 2); /* Load the function to be initialized */ - RootedFunction& func = rootFunction0; - func = ®S.sp[-1].toObject().as(); + ReservedRooted func(&rootFunction0, ®S.sp[-1].toObject().as()); MOZ_ASSERT(func->allowSuperProperty()); /* Load the home object */ - RootedNativeObject& obj = rootNativeObject0; + ReservedRooted obj(&rootNativeObject0); obj = ®S.sp[int(-2 - skipOver)].toObject().as(); MOZ_ASSERT(obj->is() || obj->is()); @@ -3991,10 +3951,8 @@ CASE(JSOP_SUPERBASE) MOZ_ASSERT(callee.nonLazyScript()->needsHomeObject()); const Value& homeObjVal = callee.getExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT); - RootedObject& homeObj = rootObject0; - homeObj = &homeObjVal.toObject(); - - RootedObject& superBase = rootObject1; + ReservedRooted homeObj(&rootObject0, &homeObjVal.toObject()); + ReservedRooted superBase(&rootObject1); if (!GetPrototype(cx, homeObj, &superBase)) goto error; @@ -4037,12 +3995,12 @@ DEFAULT() case CatchContinuation: ADVANCE_AND_DISPATCH(0); - case FinallyContinuation: + case FinallyContinuation: { /* * Push (true, exception) pair for finally to indicate that [retsub] * should rethrow the exception. */ - RootedValue& exception = rootValue0; + ReservedRooted exception(&rootValue0); if (!cx->getPendingException(&exception)) { interpReturnOK = false; goto return_continuation; @@ -4050,7 +4008,8 @@ DEFAULT() PUSH_BOOLEAN(true); PUSH_COPY(exception); cx->clearPendingException(); - ADVANCE_AND_DISPATCH(0); + } + ADVANCE_AND_DISPATCH(0); } MOZ_CRASH("Invalid HandleError continuation"); diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index a820318a5f3a..55b4e3644c38 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -205,9 +205,9 @@ TypeSet::TypeString(TypeSet::Type type) which = (which + 1) & 3; if (type.isSingleton()) - JS_snprintf(bufs[which], 40, "<0x%p>", (void*) type.singleton()); + JS_snprintf(bufs[which], 40, "<0x%p>", (void*) type.singletonNoBarrier()); else - JS_snprintf(bufs[which], 40, "[0x%p]", (void*) type.group()); + JS_snprintf(bufs[which], 40, "[0x%p]", (void*) type.groupNoBarrier()); return bufs[which]; } diff --git a/js/src/vm/UnboxedObject-inl.h b/js/src/vm/UnboxedObject-inl.h index 3934ab17071a..7fdf616ae98b 100644 --- a/js/src/vm/UnboxedObject-inl.h +++ b/js/src/vm/UnboxedObject-inl.h @@ -387,6 +387,22 @@ SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Val obj->as().setElementNoTypeChangeSpecific(index, value); } +template +static inline bool +EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count) +{ + if (Type == JSVAL_TYPE_MAGIC) { + if (!obj->as().ensureElements(cx, count)) + return false; + } else { + if (obj->as().capacity() < count) { + if (!obj->as().growElements(cx, count)) + return false; + } + } + return true; +} + enum ShouldUpdateTypes { UpdateTypes = true, @@ -532,7 +548,7 @@ CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, srcData + srcStart * elementSize, length * elementSize); - // Add a post barrier if we might have copied a nursery pointer to dst. + // Add a store buffer entry if we might have copied a nursery pointer to dst. if (UnboxedTypeNeedsPostBarrier(Type) && !IsInsideNursery(dst)) dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCellFromMainThread(dst); } diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp index 00c3f661b40d..284f8dbf3e2c 100644 --- a/layout/base/AccessibleCaretManager.cpp +++ b/layout/base/AccessibleCaretManager.cpp @@ -350,6 +350,7 @@ AccessibleCaretManager::SelectWordOrShortcut(const nsPoint& aPoint) // Content is empty. No need to select word. AC_LOG("%s, Cannot select word bacause content is empty", __FUNCTION__); DispatchCaretStateChangedEvent(CaretChangedReason::Longpressonemptycontent); + UpdateCarets(); return NS_OK; } diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 667c9ad3cb5f..c2cc1e235571 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -2395,6 +2395,15 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle MOZ_ASSERT(!mDocElementContainingBlock, "Shouldn't have a doc element containing block here"); + // Resolve a new style context for the viewport since it may be affected + // by a new root element style (e.g. a propagated 'direction'). + // @see nsStyleContext::ApplyStyleFixups + { + nsRefPtr sc = mPresShell->StyleSet()-> + ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewport, nullptr); + GetRootFrame()->SetStyleContextWithoutNotification(sc); + } + // Make sure to call PropagateScrollToViewport before // SetUpDocElementContainingBlock, since it sets up our scrollbar state // properly. diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 57c4e566e4c4..638eb93f9d8e 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1547,34 +1547,53 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB // want the root container layer to have metrics. If the parent process is // using XUL windows, there is no root scrollframe, and without explicitly // creating metrics there will be no guaranteed top-level APZC. - if (gfxPrefs::LayoutUseContainersForRootFrames() || - (XRE_IsParentProcess() && !presShell->GetRootScrollFrame())) - { + bool addMetrics = gfxPrefs::LayoutUseContainersForRootFrames() || + (XRE_IsParentProcess() && !presShell->GetRootScrollFrame()); + + // Add metrics if there are none in the layer tree with the id (create an id + // if there isn't one already) of the root scroll frame/root content. + bool ensureMetricsForRootId = + gfxPrefs::AsyncPanZoomEnabled() && + !gfxPrefs::LayoutUseContainersForRootFrames() && + aBuilder->IsPaintingToWindow() && + !presContext->GetParentPresContext(); + + nsIContent* content = nullptr; + nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); + if (rootScrollFrame) { + content = rootScrollFrame->GetContent(); + } else { + // If there is no root scroll frame, pick the document element instead. + // The only case we don't want to do this is in non-APZ fennec, where + // we want the root xul document to get a null scroll id so that the root + // content document gets the first non-null scroll id. +#if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ) + content = document->GetDocumentElement(); +#endif + } + + + if (ensureMetricsForRootId && content) { + ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(content); + if (nsLayoutUtils::ContainsMetricsWithId(root, scrollId)) { + ensureMetricsForRootId = false; + } + } + + if (addMetrics || ensureMetricsForRootId) { bool isRoot = presContext->IsRootContentDocument(); nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize()); - nsIFrame* scrollFrame = presShell->GetRootScrollFrame(); - nsIContent* content = nullptr; - if (scrollFrame) { - content = scrollFrame->GetContent(); - } else { - // If there is no root scroll frame, pick the document element instead. - // The only case we don't want to do this is in non-APZ fennec, where - // we want the root xul document to get a null scroll id so that the root - // content document gets the first non-null scroll id. -#if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ) - content = document->GetDocumentElement(); -#endif - } - root->SetFrameMetrics( nsLayoutUtils::ComputeFrameMetrics(frame, - presShell->GetRootScrollFrame(), - content, + rootScrollFrame, content, aBuilder->FindReferenceFrameFor(frame), root, FrameMetrics::NULL_SCROLL_ID, viewport, Nothing(), isRoot, containerParameters)); + } else { + // Set empty metrics to clear any metrics that might be on a recycled layer. + root->SetFrameMetrics(nsTArray()); } // NS_WARNING is debug-only, so don't even bother checking the conditions in diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index aedd782e4aed..db0519b6e67e 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1065,6 +1065,13 @@ nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult) return GetDisplayPortImpl(aContent, aResult, 1.0f); } +/* static */ bool +nsLayoutUtils::GetDisplayPortForVisibilityTesting(nsIContent* aContent, + nsRect* aResult) +{ + return GetDisplayPortImpl(aContent, aResult, 1.0f); +} + bool nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent, nsIPresShell* aPresShell, @@ -1103,6 +1110,10 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent, } } + // Display port margins changing means that the set of visible images may + // have drastically changed. Schedule an update. + aPresShell->ScheduleImageVisibilityUpdate(); + return true; } @@ -8218,6 +8229,13 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, metrics.SetIsRoot(aIsRoot); metrics.SetScrollParentId(aScrollParentId); + if (scrollId != FrameMetrics::NULL_SCROLL_ID && !presContext->GetParentPresContext()) { + if ((aScrollFrame && (aScrollFrame == presShell->GetRootScrollFrame())) || + aContent == presShell->GetDocument()->GetDocumentElement()) { + metrics.SetIsLayersIdRoot(true); + } + } + // Only the root scrollable frame for a given presShell should pick up // the presShell's resolution. All the other frames are 1.0. if (aScrollFrame == presShell->GetRootScrollFrame()) { @@ -8332,6 +8350,22 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, return metrics; } +/* static */ bool +nsLayoutUtils::ContainsMetricsWithId(const Layer* aLayer, const ViewID& aScrollId) +{ + for (uint32_t i = aLayer->GetFrameMetricsCount(); i > 0; i--) { + if (aLayer->GetFrameMetrics(i-1).GetScrollId() == aScrollId) { + return true; + } + } + for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { + if (ContainsMetricsWithId(child, aScrollId)) { + return true; + } + } + return false; +} + /* static */ uint32_t nsLayoutUtils::GetTouchActionFromFrame(nsIFrame* aFrame) { diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 42b174071864..e6592b06f174 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -165,6 +165,16 @@ public: */ static bool GetDisplayPort(nsIContent* aContent, nsRect *aResult = nullptr); + /** + * @return the display port for the given element which should be used for + * visibility testing purposes. + * + * If low-precision buffers are enabled, this is the critical display port; + * otherwise, it's the same display port returned by GetDisplayPort(). + */ + static bool GetDisplayPortForVisibilityTesting(nsIContent* aContent, + nsRect* aResult = nullptr); + enum class RepaintMode : uint8_t { Repaint, DoNotRepaint @@ -2650,6 +2660,12 @@ public: */ static nsMargin ScrollbarAreaToExcludeFromCompositionBoundsFor(nsIFrame* aScrollFrame); + /** + * Looks in the layer subtree rooted at aLayer for a metrics with scroll id + * aScrollId. Returns true if such is found. + */ + static bool ContainsMetricsWithId(const Layer* aLayer, const ViewID& aScrollId); + private: static uint32_t sFontSizeInflationEmPerLine; static uint32_t sFontSizeInflationMinTwips; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 7ca1e72ca316..fd7de8f05e07 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5940,7 +5940,9 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect) nsIScrollableFrame* scrollFrame = do_QueryFrame(aFrame); if (scrollFrame) { nsRect displayPort; - bool usingDisplayport = nsLayoutUtils::GetDisplayPort(aFrame->GetContent(), &displayPort); + bool usingDisplayport = + nsLayoutUtils::GetDisplayPortForVisibilityTesting(aFrame->GetContent(), + &displayPort); if (usingDisplayport) { rect = displayPort; } else { diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index cf052519adeb..5380c238fd56 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -78,6 +78,7 @@ static PRLogModuleInfo *gLog = nullptr; #define DEFAULT_FRAME_RATE 60 #define DEFAULT_THROTTLED_FRAME_RATE 1 +#define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000 // after 10 minutes, stop firing off inactive timers #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600 @@ -970,6 +971,17 @@ nsRefreshDriver::GetThrottledTimerInterval() return 1000.0 / rate; } +/* static */ mozilla::TimeDuration +nsRefreshDriver::GetMinRecomputeVisibilityInterval() +{ + int32_t interval = + Preferences::GetInt("layout.visibility.min-recompute-interval-ms", -1); + if (interval <= 0) { + interval = DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS; + } + return TimeDuration::FromMilliseconds(interval); +} + double nsRefreshDriver::GetRefreshTimerInterval() const { @@ -1016,7 +1028,9 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext) mFreezeCount(0), mThrottledFrameRequestInterval(TimeDuration::FromMilliseconds( GetThrottledTimerInterval())), + mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()), mThrottled(false), + mNeedToRecomputeVisibility(false), mTestControllingRefreshes(false), mViewManagerFlushIsPending(false), mRequestedHighPrecision(false), @@ -1028,6 +1042,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext) mMostRecentRefresh = TimeStamp::Now(); mMostRecentTick = mMostRecentRefresh; mNextThrottledFrameRequestTick = mMostRecentTick; + mNextRecomputeVisibilityTick = mMostRecentTick; } nsRefreshDriver::~nsRefreshDriver() @@ -1676,6 +1691,8 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime) NS_RELEASE(shell); } + mNeedToRecomputeVisibility = true; + if (tracingStyleFlush) { profiler_tracing("Paint", "Styles", TRACING_INTERVAL_END); } @@ -1721,6 +1738,8 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime) NS_RELEASE(shell); } + mNeedToRecomputeVisibility = true; + if (tracingLayoutFlush) { profiler_tracing("Paint", "Reflow", TRACING_INTERVAL_END); } @@ -1728,6 +1747,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime) } } + // Recompute image visibility if it's necessary and enough time has passed + // since the last time we did it. + if (mNeedToRecomputeVisibility && !mThrottled && + aNowTime >= mNextRecomputeVisibilityTick && + !presShell->IsPaintingSuppressed()) { + mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval; + mNeedToRecomputeVisibility = false; + + presShell->ScheduleImageVisibilityUpdate(); + } + /* * Perform notification to imgIRequests subscribed to listen * for refresh events. diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index 3dc86c4031a4..9f051a764120 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -339,6 +339,8 @@ private: double GetRegularTimerInterval(bool *outIsDefault = nullptr) const; static double GetThrottledTimerInterval(); + static mozilla::TimeDuration GetMinRecomputeVisibilityInterval(); + bool HaveFrameRequestCallbacks() const { return mFrameRequestCallbackDocs.Length() != 0; } @@ -367,7 +369,14 @@ private: // non-visible) documents registered with a non-throttled refresh driver. const mozilla::TimeDuration mThrottledFrameRequestInterval; + // How long we wait, at a minimum, before recomputing image visibility + // information. This is a minimum because, regardless of this interval, we + // only recompute visibility when we've seen a layout or style flush since the + // last time we did it. + const mozilla::TimeDuration mMinRecomputeVisibilityInterval; + bool mThrottled; + bool mNeedToRecomputeVisibility; bool mTestControllingRefreshes; bool mViewManagerFlushIsPending; bool mRequestedHighPrecision; @@ -386,6 +395,7 @@ private: mozilla::TimeStamp mMostRecentTick; mozilla::TimeStamp mTickStart; mozilla::TimeStamp mNextThrottledFrameRequestTick; + mozilla::TimeStamp mNextRecomputeVisibilityTick; // separate arrays for each flush type we support ObserverArray mObservers[3]; diff --git a/layout/generic/crashtests/1169420-1.html b/layout/generic/crashtests/1169420-1.html new file mode 100644 index 000000000000..73b8f8d64eb8 --- /dev/null +++ b/layout/generic/crashtests/1169420-1.html @@ -0,0 +1,8 @@ + + + +
+
+
+ + diff --git a/layout/generic/crashtests/1169420-2.html b/layout/generic/crashtests/1169420-2.html new file mode 100644 index 000000000000..e096ed7f9250 --- /dev/null +++ b/layout/generic/crashtests/1169420-2.html @@ -0,0 +1,8 @@ + + + +
+
+
+ + diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 3de7b00f946d..1660a837a22b 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -585,3 +585,5 @@ load 1146107.html load 1146114.html load 1156222.html load 1157011.html +load 1169420-1.html +load 1169420-2.html diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 7e2ef310fdc7..db7c1cadfff8 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3092,7 +3092,15 @@ ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer, parentLayerClip = Some(clip); } - if (!gfxPrefs::AsyncPanZoomEnabled()) { + bool thisScrollFrameUsesAsyncScrolling = nsLayoutUtils::UsesAsyncScrolling(); +#if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_ANDROID_APZ) + // Android without apzc (aka the java pan zoom code) only uses async scrolling + // for the root scroll frame of the root content document. + if (!isRoot) { + thisScrollFrameUsesAsyncScrolling = false; + } +#endif + if (!thisScrollFrameUsesAsyncScrolling) { if (parentLayerClip) { // If APZ is not enabled, we still need the displayport to be clipped // in the compositor. diff --git a/layout/reftests/bidi/1161752-1-embed.html b/layout/reftests/bidi/1161752-1-embed.html new file mode 100644 index 000000000000..f8bae49cc41f --- /dev/null +++ b/layout/reftests/bidi/1161752-1-embed.html @@ -0,0 +1,34 @@ + + + + + + + + + + + +
+ + + diff --git a/layout/reftests/bidi/1161752-2-embed.html b/layout/reftests/bidi/1161752-2-embed.html new file mode 100644 index 000000000000..b6a24ce01db3 --- /dev/null +++ b/layout/reftests/bidi/1161752-2-embed.html @@ -0,0 +1,34 @@ + + + + + + + + + + + +
+ + + diff --git a/layout/reftests/bidi/1161752-3-embed.html b/layout/reftests/bidi/1161752-3-embed.html new file mode 100644 index 000000000000..d18228fe026d --- /dev/null +++ b/layout/reftests/bidi/1161752-3-embed.html @@ -0,0 +1,32 @@ + + + + + + + + + + + +
+ + + diff --git a/layout/reftests/bidi/1161752-4-embed.html b/layout/reftests/bidi/1161752-4-embed.html new file mode 100644 index 000000000000..78e0e27a1f34 --- /dev/null +++ b/layout/reftests/bidi/1161752-4-embed.html @@ -0,0 +1,34 @@ + + + + + + + + + + + +
+ + + diff --git a/layout/reftests/bidi/1161752-5-embed-ref.html b/layout/reftests/bidi/1161752-5-embed-ref.html new file mode 100644 index 000000000000..138bc0327aa9 --- /dev/null +++ b/layout/reftests/bidi/1161752-5-embed-ref.html @@ -0,0 +1,30 @@ + + + + Testcase for bug 1161752 + + + + +
+ + + diff --git a/layout/reftests/bidi/1161752-5-embed.html b/layout/reftests/bidi/1161752-5-embed.html new file mode 100644 index 000000000000..4dffa00682a2 --- /dev/null +++ b/layout/reftests/bidi/1161752-5-embed.html @@ -0,0 +1,34 @@ + + + + + + + + + + + +
+ + + diff --git a/layout/reftests/bidi/1161752-ref.html b/layout/reftests/bidi/1161752-ref.html new file mode 100644 index 000000000000..c6ffce33fbd0 --- /dev/null +++ b/layout/reftests/bidi/1161752-ref.html @@ -0,0 +1,35 @@ + + + + Testcase for bug 1161752 + + + + +

Test passes if there are three filled green rectangles and no red.

+ + +
+
+
+
+
+ + + diff --git a/layout/reftests/bidi/1161752.html b/layout/reftests/bidi/1161752.html new file mode 100644 index 000000000000..e3fa245f56f0 --- /dev/null +++ b/layout/reftests/bidi/1161752.html @@ -0,0 +1,73 @@ + + + + Testcase for bug 1161752 + + + + + +

Test passes if there are three filled green rectangles and no red.

+ + +
This test requires a browser with capability to embed an HTML document thanks to the HTML <object> element.
+
+ +
This test requires a browser with capability to embed an HTML document thanks to the HTML <object> element.
+
+ +
This test requires a browser with capability to embed an HTML document thanks to the HTML <object> element.
+
+ +
This test requires a browser with capability to embed an HTML document thanks to the HTML <object> element.
+
+ +
This test requires a browser with capability to embed an HTML document thanks to the HTML <object> element.
+
+ + + diff --git a/layout/reftests/bidi/817406-1-ref.html b/layout/reftests/bidi/817406-1-ref.html new file mode 100644 index 000000000000..1e3e74702817 --- /dev/null +++ b/layout/reftests/bidi/817406-1-ref.html @@ -0,0 +1,4 @@ + + +
+ diff --git a/layout/reftests/bidi/817406-1.html b/layout/reftests/bidi/817406-1.html new file mode 100644 index 000000000000..d01fa724e540 --- /dev/null +++ b/layout/reftests/bidi/817406-1.html @@ -0,0 +1,4 @@ + + +
+ diff --git a/layout/reftests/bidi/817406-2-ref.html b/layout/reftests/bidi/817406-2-ref.html new file mode 100644 index 000000000000..fd1f20e0cff5 --- /dev/null +++ b/layout/reftests/bidi/817406-2-ref.html @@ -0,0 +1,4 @@ + + +
+ diff --git a/layout/reftests/bidi/817406-2.html b/layout/reftests/bidi/817406-2.html new file mode 100644 index 000000000000..ebc3ebb98c09 --- /dev/null +++ b/layout/reftests/bidi/817406-2.html @@ -0,0 +1,4 @@ + + +
+ diff --git a/layout/reftests/bidi/817406-3.html b/layout/reftests/bidi/817406-3.html new file mode 100644 index 000000000000..90313bde4a67 --- /dev/null +++ b/layout/reftests/bidi/817406-3.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/reftests/bidi/817406-4-ref.html b/layout/reftests/bidi/817406-4-ref.html new file mode 100644 index 000000000000..e6e1c8d1fa47 --- /dev/null +++ b/layout/reftests/bidi/817406-4-ref.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/reftests/bidi/817406-4.html b/layout/reftests/bidi/817406-4.html new file mode 100644 index 000000000000..3e90274248b0 --- /dev/null +++ b/layout/reftests/bidi/817406-4.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/reftests/bidi/reftest.list b/layout/reftests/bidi/reftest.list index 61ded927a9f8..40a1c196491d 100644 --- a/layout/reftests/bidi/reftest.list +++ b/layout/reftests/bidi/reftest.list @@ -139,6 +139,10 @@ skip-if(B2G||Mulet) == 726420-1.html 726420-1-ref.html # Initial mulet triage: p == 746987-4.html 746987-4-ref.html == 779003-1.html 779003-1-ref.html == 779003-1-dynamic.html 779003-1-ref.html +== 817406-1.html 817406-1-ref.html +== 817406-2.html 817406-2-ref.html +== 817406-3.html 817406-1-ref.html +== 817406-4.html 817406-4-ref.html == 847242-1.html 847242-1-ref.html skip-if((B2G&&browserIsRemote)||Mulet) == 869833-1.xul 869833-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop == 922530-1.html 922530-1-ref.html @@ -148,3 +152,5 @@ skip-if((B2G&&browserIsRemote)||Mulet) == 869833-1.xul 869833-1-ref.xul # Initia == 1069941-inline-bidi-margin-1.html 1069941-inline-bidi-margin-1-ref.html skip-if(B2G||Mulet) != 1155359-1.xul 1155359-1-ref.xul == 1157726-1.html 1157726-1-ref.html +== 1161752.html 1161752-ref.html +== 1161752-5-embed.html 1161752-5-embed-ref.html diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index d5e4badb8264..3fd0122a6c21 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1902,11 +1902,11 @@ skip-if(!asyncPanZoom) == 1133905-3-rtl.html 1133905-ref-rtl.html skip-if(!asyncPanZoom) == 1133905-4-rtl.html 1133905-ref-rtl.html skip-if(!asyncPanZoom) == 1133905-5-rtl.html 1133905-ref-rtl.html skip-if(!asyncPanZoom) == 1133905-6-rtl.html 1133905-ref-rtl.html -skip-if(!asyncPanZoom) fuzzy-if(B2G,22,175) == 1133905-1-v-rtl.html 1133905-ref-v-rtl.html +skip-if(!asyncPanZoom) fuzzy-if(B2G,23,175) == 1133905-1-v-rtl.html 1133905-ref-v-rtl.html skip-if(!asyncPanZoom) fuzzy-if(B2G,18,175) == 1133905-2-v-rtl.html 1133905-ref-v-rtl.html -skip-if(!asyncPanZoom) fuzzy-if(B2G,64,180) == 1133905-3-v-rtl.html 1133905-ref-v-rtl.html +skip-if(!asyncPanZoom) fuzzy-if(B2G,64,181) == 1133905-3-v-rtl.html 1133905-ref-v-rtl.html skip-if(!asyncPanZoom) == 1133905-4-v-rtl.html 1133905-ref-v-rtl.html -skip-if(!asyncPanZoom) fuzzy-if(B2G,32,146) == 1133905-5-v-rtl.html 1133905-ref-v-rtl.html +skip-if(!asyncPanZoom) fuzzy-if(B2G,33,180) == 1133905-5-v-rtl.html 1133905-ref-v-rtl.html skip-if(!asyncPanZoom) fuzzy-if(B2G,62,222) == 1133905-6-v-rtl.html 1133905-ref-v-rtl.html skip-if(!asyncPanZoom) == 1133905-1-h-rtl.html 1133905-ref-h-rtl.html skip-if(!asyncPanZoom) == 1133905-2-h-rtl.html 1133905-ref-h-rtl.html @@ -1914,11 +1914,11 @@ skip-if(!asyncPanZoom) == 1133905-3-h-rtl.html 1133905-ref-h-rtl.html skip-if(!asyncPanZoom) == 1133905-4-h-rtl.html 1133905-ref-h-rtl.html skip-if(!asyncPanZoom) == 1133905-5-h-rtl.html 1133905-ref-h-rtl.html skip-if(!asyncPanZoom) == 1133905-6-h-rtl.html 1133905-ref-h-rtl.html -skip-if(!asyncPanZoom) fuzzy-if(B2G,22,175) == 1133905-1-vh-rtl.html 1133905-ref-vh-rtl.html +skip-if(!asyncPanZoom) fuzzy-if(B2G,23,175) == 1133905-1-vh-rtl.html 1133905-ref-vh-rtl.html skip-if(!asyncPanZoom) fuzzy-if(B2G,62,175) == 1133905-2-vh-rtl.html 1133905-ref-vh-rtl.html -skip-if(!asyncPanZoom) fuzzy-if(B2G,23,174) == 1133905-3-vh-rtl.html 1133905-ref-vh-rtl.html +skip-if(!asyncPanZoom) fuzzy-if(B2G,23,176) == 1133905-3-vh-rtl.html 1133905-ref-vh-rtl.html skip-if(!asyncPanZoom) == 1133905-4-vh-rtl.html 1133905-ref-vh-rtl.html -skip-if(!asyncPanZoom) fuzzy-if(B2G,102,545) == 1133905-5-vh-rtl.html 1133905-ref-vh-rtl.html +skip-if(!asyncPanZoom) fuzzy-if(B2G,102,577) == 1133905-5-vh-rtl.html 1133905-ref-vh-rtl.html skip-if(!asyncPanZoom) fuzzy-if(B2G,101,887) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html skip-if(B2G||Mulet) == 1150021-1.xul 1150021-1-ref.xul == 1151145-1.html 1151145-1-ref.html diff --git a/layout/reftests/canvas/1107096-invisibles-ref.html b/layout/reftests/canvas/1107096-invisibles-ref.html new file mode 100644 index 000000000000..4c6698d28268 --- /dev/null +++ b/layout/reftests/canvas/1107096-invisibles-ref.html @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/layout/reftests/canvas/1107096-invisibles.html b/layout/reftests/canvas/1107096-invisibles.html new file mode 100644 index 000000000000..78f3b98562fc --- /dev/null +++ b/layout/reftests/canvas/1107096-invisibles.html @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/layout/reftests/canvas/reftest.list b/layout/reftests/canvas/reftest.list index 77546e57d2d4..1ff7f122566e 100644 --- a/layout/reftests/canvas/reftest.list +++ b/layout/reftests/canvas/reftest.list @@ -103,5 +103,5 @@ fails-if(azureQuartz&&OSX==1006) == 672646-alpha-radial-gradient.html 672646-alp fuzzy-if(azureQuartz,2,128) fuzzy-if(d2d,12,21) == 784573-1.html 784573-1-ref.html == 802658-1.html 802658-1-ref.html - +fuzzy-if(Mulet,45,2) == 1107096-invisibles.html 1107096-invisibles-ref.html == 1151821-1.html 1151821-1-ref.html diff --git a/layout/reftests/marquee/reftest.list b/layout/reftests/marquee/reftest.list index da1d2f118b9a..298a02b0b5d8 100644 --- a/layout/reftests/marquee/reftest.list +++ b/layout/reftests/marquee/reftest.list @@ -1,6 +1,6 @@ random-if((B2G&&browserIsRemote)||Mulet) == 166591-dynamic-1.html 166591-dynamic-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop -fuzzy-if(Android&&AndroidVersion>=15,8,50) == 336736-1a.html 336736-1-ref.html -fuzzy-if(Android&&AndroidVersion>=15,8,50) == 336736-1b.html 336736-1-ref.html +skip-if(B2G) fuzzy-if(Android&&AndroidVersion>=15,8,50) == 336736-1a.html 336736-1-ref.html # Bug 1168747 for random b2g timeouts +skip-if(B2G) fuzzy-if(Android&&AndroidVersion>=15,8,50) == 336736-1b.html 336736-1-ref.html # Bug 1168747 for random b2g timeouts == 406073-1.html 406073-1-ref.html == 407016-2.html 407016-2-ref.html fuzzy-if(Android&&AndroidVersion>=15,8,220) == 413027-4.html 413027-4-ref.html diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index 7746215c9273..0205b37abed1 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -448,6 +448,7 @@ nsStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID) UNIQUE_CASE(Display) UNIQUE_CASE(Text) UNIQUE_CASE(TextReset) + UNIQUE_CASE(Visibility) #undef UNIQUE_CASE @@ -593,6 +594,23 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) mBits |= NS_STYLE_HAS_PSEUDO_ELEMENT_DATA; } + // CSS 2.1 10.1: Propagate the root element's 'direction' to the ICB. + // (PageContentFrame/CanvasFrame etc will inherit 'direction') + if (mPseudoTag == nsCSSAnonBoxes::viewport) { + nsPresContext* presContext = PresContext(); + mozilla::dom::Element* docElement = presContext->Document()->GetRootElement(); + if (docElement) { + nsRefPtr rootStyle = + presContext->StyleSet()->ResolveStyleFor(docElement, nullptr); + auto dir = rootStyle->StyleVisibility()->mDirection; + if (dir != StyleVisibility()->mDirection) { + nsStyleVisibility* uniqueVisibility = + (nsStyleVisibility*)GetUniqueStyleData(eStyleStruct_Visibility); + uniqueVisibility->mDirection = dir; + } + } + } + // Correct tables. const nsStyleDisplay* disp = StyleDisplay(); if (disp->mDisplay == NS_STYLE_DISPLAY_TABLE) { diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index 0374184a510a..839c0bc48555 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -991,9 +991,8 @@ void MediaPipelineTransmit::PipelineListener::ProcessAudioChunk( memset(samples, 0, chunk.mDuration * sizeof(samples[0])); break; default: - MOZ_ASSERT(PR_FALSE); + MOZ_ASSERT_UNREACHABLE("Unexpected AudioSampleFormat"); return; - break; } } else { // This means silence. diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c index 860e3d4f3341..577cb63772a2 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c @@ -5163,13 +5163,11 @@ sdp_result_e sdp_parse_attr_setup(sdp_t *sdp_p, "%s Warning: Unknown setup attribute", sdp_p->debug_str); return SDP_INVALID_PARAMETER; - break; default: /* This is an internal error, not a parsing error */ CSFLogError(logTag, "%s Error: Invalid setup enum (%d)", sdp_p->debug_str, attr_p->attr.setup); return SDP_FAILURE; - break; } return SDP_SUCCESS; @@ -5223,13 +5221,11 @@ sdp_result_e sdp_parse_attr_connection(sdp_t *sdp_p, "%s Warning: Unknown connection attribute", sdp_p->debug_str); return SDP_INVALID_PARAMETER; - break; default: /* This is an internal error, not a parsing error */ CSFLogError(logTag, "%s Error: Invalid connection enum (%d)", sdp_p->debug_str, attr_p->attr.connection); return SDP_FAILURE; - break; } return SDP_SUCCESS; } diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c index dd6fb7273acc..0f0b889286a4 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c @@ -1625,8 +1625,6 @@ sdp_result_e sdp_attr_get_ice_attribute (sdp_t *sdp_p, uint16_t level, sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER); } - - return (SDP_FAILURE); } /* Function: sdp_attr_is_present @@ -1693,8 +1691,6 @@ sdp_result_e sdp_attr_get_rtcp_mux_attribute (sdp_t *sdp_p, uint16_t level, sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER); } - - return (SDP_FAILURE); } /* Function: sdp_attr_get_setup_attribute @@ -1798,8 +1794,6 @@ sdp_result_e sdp_attr_get_dtls_fingerprint_attribute (sdp_t *sdp_p, uint16_t lev sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER); } - - return (SDP_FAILURE); } /* Function: sdp_attr_sprtmap_payload_valid diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index 7cecb35e1e73..49d716235edd 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -91,6 +91,7 @@ XPIDL_SOURCES += [ 'nsIRequestObserverProxy.idl', 'nsIResponseHeadProvider.idl', 'nsIResumableChannel.idl', + 'nsISecCheckWrapChannel.idl', 'nsISecretDecoderRing.idl', 'nsISecureBrowserUI.idl', 'nsISecurityEventSink.idl', @@ -213,6 +214,7 @@ UNIFIED_SOURCES += [ 'nsProtocolProxyService.cpp', 'nsProxyInfo.cpp', 'nsRequestObserverProxy.cpp', + 'nsSecCheckWrapChannel.cpp', 'nsSerializationHelper.cpp', 'nsServerSocket.cpp', 'nsSimpleNestedURI.cpp', diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index 0d443024de97..692b4c97a213 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -22,6 +22,7 @@ #include "nsEscape.h" #include "nsNetCID.h" #include "nsCRT.h" +#include "nsSecCheckWrapChannel.h" #include "nsSimpleNestedURI.h" #include "nsNetUtil.h" #include "nsTArray.h" @@ -661,43 +662,43 @@ nsIOService::NewChannelFromURIWithProxyFlagsInternal(nsIURI* aURI, // Keep in mind that Addons can implement their own Protocolhandlers, hence // NewChannel2() might *not* be implemented. // We do not want to break those addons, therefore we first try to create a channel - // calling NewChannel2(); if that fails we fall back to creating a channel by calling - // NewChannel(); - - bool newChannel2Succeeded = true; - + // calling NewChannel2(); if that fails: + // * we fall back to creating a channel by calling NewChannel() + // * wrap the addon channel + // * and attach the loadInfo to the channel wrapper + nsCOMPtr channel; nsCOMPtr pph = do_QueryInterface(handler); if (pph) { rv = pph->NewProxiedChannel2(aURI, nullptr, aProxyFlags, aProxyURI, - aLoadInfo, result); + aLoadInfo, getter_AddRefs(channel)); // if calling NewProxiedChannel2() fails we try to fall back to // creating a new proxied channel by calling NewProxiedChannel(). if (NS_FAILED(rv)) { - newChannel2Succeeded = false; rv = pph->NewProxiedChannel(aURI, nullptr, aProxyFlags, aProxyURI, - result); + getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, rv); + // we have to wrap that channel + channel = new nsSecCheckWrapChannel(channel, aLoadInfo); } } else { - rv = handler->NewChannel2(aURI, aLoadInfo, result); + rv = handler->NewChannel2(aURI, aLoadInfo, getter_AddRefs(channel)); // if calling newChannel2() fails we try to fall back to // creating a new channel by calling NewChannel(). if (NS_FAILED(rv)) { - newChannel2Succeeded = false; - rv = handler->NewChannel(aURI, result); + rv = handler->NewChannel(aURI, getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, rv); + // we have to wrap that channel + channel = new nsSecCheckWrapChannel(channel, aLoadInfo); } } - NS_ENSURE_SUCCESS(rv, rv); - if (aLoadInfo && newChannel2Succeeded) { - // Make sure that all the individual protocolhandlers attach - // a loadInfo within it's implementation of ::newChannel2(). - // Once Bug 1087720 lands, we should remove the surrounding - // if-clause here and always assert that we indeed have a - // loadinfo on the newly created channel. - nsCOMPtr loadInfo; - (*result)->GetLoadInfo(getter_AddRefs(loadInfo)); + // Make sure that all the individual protocolhandlers attach a loadInfo. + if (aLoadInfo) { // make sure we have the same instance of loadInfo on the newly created channel + nsCOMPtr loadInfo; + channel->GetLoadInfo(getter_AddRefs(loadInfo)); + if (aLoadInfo != loadInfo) { MOZ_ASSERT(false, "newly created channel must have a loadinfo attached"); return NS_ERROR_UNEXPECTED; @@ -706,7 +707,7 @@ nsIOService::NewChannelFromURIWithProxyFlagsInternal(nsIURI* aURI, // If we're sandboxed, make sure to clear any owner the channel // might already have. if (loadInfo->GetLoadingSandboxed()) { - (*result)->SetOwner(nullptr); + channel->SetOwner(nullptr); } } @@ -718,7 +719,7 @@ nsIOService::NewChannelFromURIWithProxyFlagsInternal(nsIURI* aURI, // implement the new interface. // See bug 529041 if (!gHasWarnedUploadChannel2 && scheme.EqualsLiteral("http")) { - nsCOMPtr uploadChannel2 = do_QueryInterface(*result); + nsCOMPtr uploadChannel2 = do_QueryInterface(channel); if (!uploadChannel2) { nsCOMPtr consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID); @@ -731,6 +732,7 @@ nsIOService::NewChannelFromURIWithProxyFlagsInternal(nsIURI* aURI, } } + channel.forget(result); return NS_OK; } diff --git a/netwerk/base/nsISecCheckWrapChannel.idl b/netwerk/base/nsISecCheckWrapChannel.idl new file mode 100644 index 000000000000..21f4d0c290c5 --- /dev/null +++ b/netwerk/base/nsISecCheckWrapChannel.idl @@ -0,0 +1,24 @@ +/* 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 "nsISupports.idl" + +interface nsIChannel; + +/** + * nsISecCheckWrapChannel + * Describes an XPCOM component used to wrap channels for performing + * security checks. Channels wrapped inside this class can use + * this interface to query the wrapped inner channel. + */ + +[scriptable, uuid(9446c5d5-c9fb-4a6e-acf9-ca4fc666efe0)] +interface nsISecCheckWrapChannel : nsISupports +{ + /** + * Returns the wrapped channel inside this class. + */ + readonly attribute nsIChannel innerChannel; + +}; diff --git a/netwerk/base/nsSecCheckWrapChannel.cpp b/netwerk/base/nsSecCheckWrapChannel.cpp new file mode 100644 index 000000000000..09a7f6fce750 --- /dev/null +++ b/netwerk/base/nsSecCheckWrapChannel.cpp @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSecCheckWrapChannel.h" +#include "nsHttpChannel.h" +#include "nsCOMPtr.h" + +#ifdef PR_LOGGING +static PRLogModuleInfo* +GetChannelWrapperLog() +{ + static PRLogModuleInfo* gChannelWrapperPRLog; + if (!gChannelWrapperPRLog) { + gChannelWrapperPRLog = PR_NewLogModule("ChannelWrapper"); + } + return gChannelWrapperPRLog; +} +#endif + +#define CHANNELWRAPPERLOG(args) PR_LOG(GetChannelWrapperLog(), 4, args) + +NS_IMPL_ADDREF(nsSecCheckWrapChannelBase) +NS_IMPL_RELEASE(nsSecCheckWrapChannelBase) + +NS_INTERFACE_MAP_BEGIN(nsSecCheckWrapChannelBase) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIHttpChannel, mHttpChannel) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIHttpChannelInternal, mHttpChannelInternal) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIHttpChannel) + NS_INTERFACE_MAP_ENTRY(nsIRequest) + NS_INTERFACE_MAP_ENTRY(nsIChannel) + NS_INTERFACE_MAP_ENTRY(nsISecCheckWrapChannel) +NS_INTERFACE_MAP_END + +//--------------------------------------------------------- +// nsSecCheckWrapChannelBase implementation +//--------------------------------------------------------- + +nsSecCheckWrapChannelBase::nsSecCheckWrapChannelBase(nsIChannel* aChannel) + : mChannel(aChannel) + , mHttpChannel(do_QueryInterface(aChannel)) + , mHttpChannelInternal(do_QueryInterface(aChannel)) + , mRequest(do_QueryInterface(aChannel)) +{ + MOZ_ASSERT(mChannel, "can not create a channel wrapper without a channel"); +} + +nsSecCheckWrapChannelBase::~nsSecCheckWrapChannelBase() +{ +} + +//--------------------------------------------------------- +// nsISecCheckWrapChannel implementation +//--------------------------------------------------------- + +NS_IMETHODIMP +nsSecCheckWrapChannelBase::GetInnerChannel(nsIChannel **aInnerChannel) +{ + NS_IF_ADDREF(*aInnerChannel = mChannel); + return NS_OK; +} + +//--------------------------------------------------------- +// nsSecCheckWrapChannel implementation +//--------------------------------------------------------- + +nsSecCheckWrapChannel::nsSecCheckWrapChannel(nsIChannel* aChannel, + nsILoadInfo* aLoadInfo) + : nsSecCheckWrapChannelBase(aChannel) + , mLoadInfo(aLoadInfo) +{ +#ifdef PR_LOGGING + { + nsCOMPtr uri; + mChannel->GetURI(getter_AddRefs(uri)); + nsAutoCString spec; + if (uri) { + uri->GetSpec(spec); + } + CHANNELWRAPPERLOG(("nsSecCheckWrapChannel::nsSecCheckWrapChannel [%p] (%s)",this, spec.get())); + } +#endif +} + +nsSecCheckWrapChannel::~nsSecCheckWrapChannel() +{ +} + +//--------------------------------------------------------- +// nsIChannel implementation +//--------------------------------------------------------- + +NS_IMETHODIMP +nsSecCheckWrapChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) +{ + CHANNELWRAPPERLOG(("nsSecCheckWrapChannel::GetLoadInfo() [%p]",this)); + NS_IF_ADDREF(*aLoadInfo = mLoadInfo); + return NS_OK; +} + +NS_IMETHODIMP +nsSecCheckWrapChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) +{ + CHANNELWRAPPERLOG(("nsSecCheckWrapChannel::SetLoadInfo() [%p]", this)); + mLoadInfo = aLoadInfo; + return NS_OK; +} diff --git a/netwerk/base/nsSecCheckWrapChannel.h b/netwerk/base/nsSecCheckWrapChannel.h new file mode 100644 index 000000000000..9ba164ca61ce --- /dev/null +++ b/netwerk/base/nsSecCheckWrapChannel.h @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsSecCheckWrapChannel_h__ +#define nsSecCheckWrapChannel_h__ + +#include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" +#include "nsISecCheckWrapChannel.h" +#include "nsIWyciwygChannel.h" +#include "mozilla/LoadInfo.h" + +/* + * The nsSecCheckWrapChannelBase wraps channels that do *not* + * * provide a newChannel2() implementation + * * provide get/setLoadInfo functions + * + * In order to perform security checks for channels + * a) before opening the channel, and + * b) after redirects + * we are attaching a loadinfo object to every channel which + * provides information about the content-type of the channel, + * who initiated the load, etc. + * + * Addon created channels might *not* provide that loadInfo object for + * some transition time before we mark the NewChannel-API as deprecated. + * We do not want to break those addons hence we wrap such channels + * using the provided wrapper in this class. + * + * Please note that the wrapper only forwards calls for + * * nsIRequest + * * nsIChannel + * * nsIHttpChannel + * * nsIHttpChannelInternal + * + * In case any addon needs to query the inner channel this class + * provides a readonly function to query the wrapped channel. + * + */ + +class nsSecCheckWrapChannelBase : public nsIHttpChannel + , public nsIHttpChannelInternal + , public nsISecCheckWrapChannel +{ +public: + NS_FORWARD_NSIHTTPCHANNEL(mHttpChannel->) + NS_FORWARD_NSIHTTPCHANNELINTERNAL(mHttpChannelInternal->) + NS_FORWARD_NSICHANNEL(mChannel->) + NS_FORWARD_NSIREQUEST(mRequest->) + NS_DECL_NSISECCHECKWRAPCHANNEL + NS_DECL_ISUPPORTS + + explicit nsSecCheckWrapChannelBase(nsIChannel* aChannel); + +protected: + virtual ~nsSecCheckWrapChannelBase(); + + nsCOMPtr mChannel; + // We do a QI in the constructor to set the following pointers. + nsCOMPtr mHttpChannel; + nsCOMPtr mHttpChannelInternal; + nsCOMPtr mRequest; +}; + +/* We define a separate class here to make it clear that we're + * overriding Get/SetLoadInfo, rather that using the forwarded + * implementations provided by NS_FORWARD_NSICHANNEL" + */ +class nsSecCheckWrapChannel : public nsSecCheckWrapChannelBase +{ +public: + NS_IMETHOD GetLoadInfo(nsILoadInfo **aLoadInfo); + NS_IMETHOD SetLoadInfo(nsILoadInfo *aLoadInfo); + + nsSecCheckWrapChannel(nsIChannel* aChannel, nsILoadInfo* aLoadInfo); + +protected: + virtual ~nsSecCheckWrapChannel(); + + nsCOMPtr mLoadInfo; +}; + +#endif // nsSecCheckWrapChannel_h__ diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index 974607dce4c0..856c397f4082 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -2228,13 +2228,6 @@ WebSocketChannel::StopSession(nsresult reason) // normally this should be called on socket thread, but it is ok to call it // from OnStartRequest before the socket thread machine has gotten underway - if (NS_IsMainThread()) { - MOZ_DIAGNOSTIC_ASSERT(!mDataStarted); - } else { - MOZ_DIAGNOSTIC_ASSERT(PR_GetCurrentThread() == gSocketThread, - "Called on unexpected thread!"); - MOZ_DIAGNOSTIC_ASSERT(mDataStarted); - } mStopped = 1; @@ -2341,13 +2334,6 @@ WebSocketChannel::AbortSession(nsresult reason) // normally this should be called on socket thread, but it is ok to call it // from the main thread before StartWebsocketData() has completed - if (NS_IsMainThread()) { - MOZ_DIAGNOSTIC_ASSERT(!mDataStarted); - } else { - MOZ_DIAGNOSTIC_ASSERT(PR_GetCurrentThread() == gSocketThread, - "Called on unexpected thread!"); - MOZ_DIAGNOSTIC_ASSERT(mDataStarted); - } // When we are failing we need to close the TCP connection immediately // as per 7.1.1 diff --git a/python/mach/mach/dispatcher.py b/python/mach/mach/dispatcher.py index 076ac1f902f8..03206ea58a98 100644 --- a/python/mach/mach/dispatcher.py +++ b/python/mach/mach/dispatcher.py @@ -109,14 +109,17 @@ class CommandAction(argparse.Action): # Command suggestion if command not in self._mach_registrar.command_handlers: + # Make sure we don't suggest any deprecated commands. + names = [h.name for h in self._mach_registrar.command_handlers.values() + if h.cls.__name__ == 'DeprecatedCommands'] # We first try to look for a valid command that is very similar to the given command. - suggested_commands = difflib.get_close_matches(command, self._mach_registrar.command_handlers.keys(), cutoff=0.8) + suggested_commands = difflib.get_close_matches(command, names, cutoff=0.8) # If we find more than one matching command, or no command at all, we give command suggestions instead # (with a lower matching threshold). All commands that start with the given command (for instance: 'mochitest-plain', # 'mochitest-chrome', etc. for 'mochitest-') are also included. if len(suggested_commands) != 1: - suggested_commands = set(difflib.get_close_matches(command, self._mach_registrar.command_handlers.keys(), cutoff=0.5)) - suggested_commands |= {cmd for cmd in self._mach_registrar.command_handlers if cmd.startswith(command)} + suggested_commands = set(difflib.get_close_matches(command, names, cutoff=0.5)) + suggested_commands |= {cmd for cmd in names if cmd.startswith(command)} raise UnknownCommandError(command, 'run', suggested_commands) sys.stderr.write("We're assuming the '%s' command is '%s' and we're executing it for you.\n\n" % (command, suggested_commands[0])) command = suggested_commands[0] diff --git a/python/mozbuild/mozbuild/testing.py b/python/mozbuild/mozbuild/testing.py index 01b893732dfa..f625ec93426c 100644 --- a/python/mozbuild/mozbuild/testing.py +++ b/python/mozbuild/mozbuild/testing.py @@ -163,6 +163,10 @@ class TestResolver(MozbuildObject): 'mochitest', 'chrome'), 'mochitest': os.path.join(self.topobjdir, '_tests', 'testing', 'mochitest', 'tests'), + 'webapprt-chrome': os.path.join(self.topobjdir, '_tests', 'testing', + 'mochitest', 'webapprtChrome'), + 'webapprt-content': os.path.join(self.topobjdir, '_tests', 'testing', + 'mochitest', 'webapprtContent'), 'xpcshell': os.path.join(self.topobjdir, '_tests', 'xpcshell'), } diff --git a/security/manager/ssl/nsCertTree.cpp b/security/manager/ssl/nsCertTree.cpp index 7610ce399411..0085c492213a 100644 --- a/security/manager/ssl/nsCertTree.cpp +++ b/security/manager/ssl/nsCertTree.cpp @@ -149,7 +149,10 @@ nsCertTreeDispInfo::GetHostPort(nsAString &aHostPort) NS_IMPL_ISUPPORTS(nsCertTree, nsICertTree, nsITreeView) -nsCertTree::nsCertTree() : mTreeArray(nullptr) +nsCertTree::nsCertTree() + : mTreeArray(nullptr) + , mCompareCache(&gMapOps, sizeof(CompareCacheHashEntryPtr), + kInitialCacheLength) { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); @@ -165,22 +168,11 @@ nsCertTree::nsCertTree() : mTreeArray(nullptr) void nsCertTree::ClearCompareHash() { - if (mCompareCache.IsInitialized()) { - PL_DHashTableFinish(&mCompareCache); - } -} - -nsresult nsCertTree::InitCompareHash() -{ - ClearCompareHash(); - PL_DHashTableInit(&mCompareCache, &gMapOps, - sizeof(CompareCacheHashEntryPtr), 64); - return NS_OK; + mCompareCache.ClearAndPrepareForLength(kInitialCacheLength); } nsCertTree::~nsCertTree() { - ClearCompareHash(); delete [] mTreeArray; } @@ -661,11 +653,11 @@ nsCertTree::LoadCertsFromCache(nsIX509CertList *aCache, uint32_t aType) mTreeArray = nullptr; mNumRows = 0; } - nsresult rv = InitCompareHash(); - if (NS_FAILED(rv)) return rv; + ClearCompareHash(); - rv = GetCertsByTypeFromCache(aCache, aType, - GetCompareFuncFromCertType(aType), &mCompareCache); + nsresult rv = GetCertsByTypeFromCache(aCache, aType, + GetCompareFuncFromCertType(aType), + &mCompareCache); if (NS_FAILED(rv)) return rv; return UpdateUIContents(); } @@ -679,11 +671,10 @@ nsCertTree::LoadCerts(uint32_t aType) mTreeArray = nullptr; mNumRows = 0; } - nsresult rv = InitCompareHash(); - if (NS_FAILED(rv)) return rv; + ClearCompareHash(); - rv = GetCertsByType(aType, - GetCompareFuncFromCertType(aType), &mCompareCache); + nsresult rv = GetCertsByType(aType, GetCompareFuncFromCertType(aType), + &mCompareCache); if (NS_FAILED(rv)) return rv; return UpdateUIContents(); } diff --git a/security/manager/ssl/nsCertTree.h b/security/manager/ssl/nsCertTree.h index e9518e3682cc..419e7b020fe9 100644 --- a/security/manager/ssl/nsCertTree.h +++ b/security/manager/ssl/nsCertTree.h @@ -90,7 +90,6 @@ public: protected: virtual ~nsCertTree(); - nsresult InitCompareHash(); void ClearCompareHash(); void RemoveCacheEntry(void *key); @@ -117,13 +116,15 @@ protected: nsresult GetCertsByTypeFromCache(nsIX509CertList *aCache, uint32_t aType, nsCertCompareFunc aCertCmpFn, void *aCertCmpFnArg); private: + static const uint32_t kInitialCacheLength = 64; + nsTArray< mozilla::RefPtr > mDispInfo; nsCOMPtr mTree; nsCOMPtr mSelection; treeArrayEl *mTreeArray; int32_t mNumOrgs; int32_t mNumRows; - PLDHashTable mCompareCache; + PLDHashTable2 mCompareCache; nsCOMPtr mNSSComponent; nsCOMPtr mOverrideService; mozilla::RefPtr mOriginalOverrideService; diff --git a/security/manager/ssl/nsSecureBrowserUIImpl.cpp b/security/manager/ssl/nsSecureBrowserUIImpl.cpp index 0f542990fd05..441d98e5bbca 100644 --- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp +++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp @@ -111,20 +111,14 @@ nsSecureBrowserUIImpl::nsSecureBrowserUIImpl() #ifdef DEBUG , mOnStateLocationChangeReentranceDetection(0) #endif + , mTransferringRequests(&gMapOps, sizeof(RequestHashEntry)) { ResetStateTracking(); - + if (!gSecureDocLog) gSecureDocLog = PR_NewLogModule("nsSecureBrowserUI"); } -nsSecureBrowserUIImpl::~nsSecureBrowserUIImpl() -{ - if (mTransferringRequests.IsInitialized()) { - PL_DHashTableFinish(&mTransferringRequests); - } -} - NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl, nsISecureBrowserUI, nsIWebProgressListener, @@ -379,10 +373,7 @@ void nsSecureBrowserUIImpl::ResetStateTracking() ReentrantMonitorAutoEnter lock(mReentrantMonitor); mDocumentRequestsInProgress = 0; - if (mTransferringRequests.IsInitialized()) { - PL_DHashTableFinish(&mTransferringRequests); - } - PL_DHashTableInit(&mTransferringRequests, &gMapOps, sizeof(RequestHashEntry)); + mTransferringRequests.Clear(); } void diff --git a/security/manager/ssl/nsSecureBrowserUIImpl.h b/security/manager/ssl/nsSecureBrowserUIImpl.h index 4dd0792b6ed2..8432270901b1 100644 --- a/security/manager/ssl/nsSecureBrowserUIImpl.h +++ b/security/manager/ssl/nsSecureBrowserUIImpl.h @@ -48,7 +48,7 @@ public: NS_DECL_NSISSLSTATUSPROVIDER protected: - virtual ~nsSecureBrowserUIImpl(); + virtual ~nsSecureBrowserUIImpl() {}; mozilla::ReentrantMonitor mReentrantMonitor; @@ -100,7 +100,7 @@ protected: nsCOMPtr mSSLStatus; nsCOMPtr mCurrentToplevelSecurityInfo; - PLDHashTable mTransferringRequests; + PLDHashTable2 mTransferringRequests; }; diff --git a/testing/mach_commands.py b/testing/mach_commands.py index f3bcdb092c58..6aee62135343 100644 --- a/testing/mach_commands.py +++ b/testing/mach_commands.py @@ -68,7 +68,7 @@ TEST_SUITES = { }, 'mochitest-browser': { 'aliases': ('bc', 'BC', 'Bc'), - 'mach_command': 'mochitest-browser', + 'mach_command': 'mochitest', 'kwargs': {'flavor': 'browser-chrome', 'test_paths': None}, }, 'mochitest-chrome': { @@ -77,7 +77,7 @@ TEST_SUITES = { }, 'mochitest-devtools': { 'aliases': ('dt', 'DT', 'Dt'), - 'mach_command': 'mochitest-browser', + 'mach_command': 'mochitest', 'kwargs': {'subsuite': 'devtools', 'test_paths': None}, }, 'mochitest-ipcplugins': { @@ -85,7 +85,7 @@ TEST_SUITES = { }, 'mochitest-plain': { 'mach_command': 'mochitest', - 'kwargs': {'flavor': 'mochitest', 'test_paths': None}, + 'kwargs': {'flavor': 'plain', 'test_paths': None}, }, 'luciddream': { 'mach_command': 'luciddream', @@ -151,6 +151,7 @@ for i in range(1, MOCHITEST_TOTAL_CHUNKS + 1): 'mach_command': 'mochitest', 'kwargs': { 'flavor': 'mochitest', + 'subsuite': 'default', 'chunk_by_dir': MOCHITEST_CHUNK_BY_DIR, 'total_chunks': MOCHITEST_TOTAL_CHUNKS, 'this_chunk': i, diff --git a/testing/marionette/client/requirements.txt b/testing/marionette/client/requirements.txt index 1a602c6d3d1f..9f02a667072a 100644 --- a/testing/marionette/client/requirements.txt +++ b/testing/marionette/client/requirements.txt @@ -1,5 +1,5 @@ marionette-transport >= 0.4 -marionette-driver >= 0.7 +marionette-driver >= 0.8 browsermob-proxy >= 0.6.0 manifestparser >= 1.1 mozhttpd >= 0.7 diff --git a/testing/marionette/client/setup.py b/testing/marionette/client/setup.py index 8f51f52dbc00..5199dfb3af78 100644 --- a/testing/marionette/client/setup.py +++ b/testing/marionette/client/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = '0.14' +version = '0.15' # dependencies with open('requirements.txt') as f: diff --git a/testing/marionette/driver/setup.py b/testing/marionette/driver/setup.py index a87c072d2f7f..ee3e108b0161 100644 --- a/testing/marionette/driver/setup.py +++ b/testing/marionette/driver/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = '0.7' +version = '0.8' # dependencies with open('requirements.txt') as f: diff --git a/testing/mochitest/mach_commands.py b/testing/mochitest/mach_commands.py index 741dd5436ef2..9890a727293b 100644 --- a/testing/mochitest/mach_commands.py +++ b/testing/mochitest/mach_commands.py @@ -5,8 +5,9 @@ from __future__ import unicode_literals from argparse import Namespace +from collections import defaultdict +from itertools import chain import logging -import mozpack.path as mozpath import os import shutil import sys @@ -24,20 +25,21 @@ from mach.decorators import ( CommandProvider, Command, ) +import mozpack.path as mozpath here = os.path.abspath(os.path.dirname(__file__)) ADB_NOT_FOUND = ''' -The %s command requires the adb binary to be on your path. +The mochitest command requires the adb binary to be on your path. If you have a B2G build, this can be found in -'%s/out/host//bin'. +'{}/out/host//bin'. '''.lstrip() GAIA_PROFILE_NOT_FOUND = ''' -The %s command requires a non-debug gaia profile. Either pass in --profile, -or set the GAIA_PROFILE environment variable. +The mochitest command requires a non-debug gaia profile. Either +pass in --profile, or set the GAIA_PROFILE environment variable. If you do not have a non-debug gaia profile, you can build one: $ git clone https://github.com/mozilla-b2g/gaia @@ -48,8 +50,8 @@ The profile should be generated in a directory called 'profile'. '''.lstrip() GAIA_PROFILE_IS_DEBUG = ''' -The %s command requires a non-debug gaia profile. The specified profile, -%s, is a debug profile. +The mochitest command requires a non-debug gaia profile. The +specified profile, {}, is a debug profile. If you do not have a non-debug gaia profile, you can build one: $ git clone https://github.com/mozilla-b2g/gaia @@ -60,26 +62,114 @@ The profile should be generated in a directory called 'profile'. '''.lstrip() ENG_BUILD_REQUIRED = ''' -The %s command requires an engineering build. It may be the case that +The mochitest command requires an engineering build. It may be the case that VARIANT=user or PRODUCTION=1 were set. Try re-building with VARIANT=eng: $ VARIANT=eng ./build.sh There should be an app called 'test-container.gaiamobile.org' located in -%s. +{}. '''.lstrip() -# Maps test flavors to mochitest suite type. -FLAVORS = { - 'mochitest': 'plain', - 'chrome': 'chrome', - 'browser-chrome': 'browser', - 'jetpack-package': 'jetpack-package', - 'jetpack-addon': 'jetpack-addon', - 'a11y': 'a11y', - 'webapprt-chrome': 'webapprt-chrome', +SUPPORTED_TESTS_NOT_FOUND = ''' +The mochitest command could not find any supported tests to run! The +following flavors and subsuites were found, but are either not supported on +{} builds, or were excluded on the command line: + +{} + +Double check the command line you used, and make sure you are running in +context of the proper build. To switch build contexts, either run |mach| +from the appropriate objdir, or export the correct mozconfig: + + $ export MOZCONFIG=path/to/mozconfig +'''.lstrip() + +TESTS_NOT_FOUND = ''' +The mochitest command could not find any mochitests under the following +test path(s): + +{} + +Please check spelling and make sure there are mochitests living there. +'''.lstrip() + +NOW_RUNNING = ''' +###### +### Now running mochitest-{}. +###### +''' + + +# Maps test flavors to data needed to run them +ALL_FLAVORS = { + 'mochitest': { + 'suite': 'plain', + 'aliases': ('plain', 'mochitest'), + 'enabled_apps': ('firefox', 'b2g', 'android', 'mulet', 'b2g_desktop'), + }, + 'chrome': { + 'suite': 'chrome', + 'aliases': ('chrome', 'mochitest-chrome'), + 'enabled_apps': ('firefox', 'mulet', 'b2g', 'android'), + 'extra_args': { + 'chrome': True, + } + }, + 'browser-chrome': { + 'suite': 'browser', + 'aliases': ('browser', 'browser-chrome', 'mochitest-browser-chrome', 'bc'), + 'enabled_apps': ('firefox',), + 'extra_args': { + 'browserChrome': True, + } + }, + 'jetpack-package': { + 'suite': 'jetpack-package', + 'aliases': ('jetpack-package', 'mochitest-jetpack-package', 'jpp'), + 'enabled_apps': ('firefox',), + 'extra_args': { + 'jetpackPackage': True, + } + }, + 'jetpack-addon': { + 'suite': 'jetpack-addon', + 'aliases': ('jetpack-addon', 'mochitest-jetpack-addon', 'jpa'), + 'enabled_apps': ('firefox',), + 'extra_args': { + 'jetpackAddon': True, + } + }, + 'a11y': { + 'suite': 'a11y', + 'aliases': ('a11y', 'mochitest-a11y', 'accessibility'), + 'enabled_apps': ('firefox',), + 'extra_args': { + 'a11y': True, + } + }, + 'webapprt-chrome': { + 'suite': 'webapprt-chrome', + 'aliases': ('webapprt-chrome', 'mochitest-webapprt-chrome'), + 'enabled_apps': ('firefox',), + 'extra_args': { + 'webapprtChrome': True, + } + }, + 'webapprt-content': { + 'suite': 'webapprt-content', + 'aliases': ('webapprt-content', 'mochitest-webapprt-content'), + 'enabled_apps': ('firefox',), + 'extra_args': { + 'webapprtContent': True, + } + }, } +SUPPORTED_APPS = ['firefox', 'b2g', 'android', 'mulet', 'b2g_desktop'] +SUPPORTED_FLAVORS = list(chain.from_iterable([f['aliases'] for f in ALL_FLAVORS.values()])) +CANONICAL_FLAVORS = sorted([f['aliases'][0] for f in ALL_FLAVORS.values()]) + class MochitestRunner(MozbuildObject): @@ -142,19 +232,37 @@ class MochitestRunner(MozbuildObject): 'mochitest') self.bin_dir = os.path.join(self.topobjdir, 'dist', 'bin') - def run_b2g_test(self, test_paths=None, **kwargs): - """Runs a b2g mochitest. + def resolve_tests(self, test_paths, test_objects=None, cwd=None): + if test_objects: + return test_objects - test_paths is an enumerable of paths to tests. It can be a relative path - from the top source directory, an absolute filename, or a directory - containing test files. - """ - # Need to call relpath before os.chdir() below. - test_path = '' - if test_paths: - if len(test_paths) > 1: - print('Warning: Only the first test path will be used.') - test_path = self._wrap_path_argument(test_paths[0]).relpath() + # Ensure test paths are relative to topobjdir or topsrcdir. + test_paths = test_paths or [] + test_paths = [self._wrap_path_argument(tp).relpath() for tp in test_paths] + + from mozbuild.testing import TestResolver + resolver = self._spawn(TestResolver) + tests = list(resolver.resolve_tests(paths=test_paths, cwd=cwd)) + return tests + + def run_b2g_test(self, context, tests=None, suite='mochitest', **kwargs): + """Runs a b2g mochitest.""" + if kwargs.get('desktop'): + kwargs['profile'] = kwargs.get('profile') or os.environ.get('GAIA_PROFILE') + if not kwargs['profile'] or not os.path.isdir(kwargs['profile']): + print(GAIA_PROFILE_NOT_FOUND) + sys.exit(1) + + if os.path.isfile(os.path.join(kwargs['profile'], 'extensions', + 'httpd@gaiamobile.org')): + print(GAIA_PROFILE_IS_DEBUG.format(kwargs['profile'])) + sys.exit(1) + elif context.target_out: + host_webapps_dir = os.path.join(context.target_out, 'data', 'local', 'webapps') + if not os.path.isdir(os.path.join( + host_webapps_dir, 'test-container.gaiamobile.org')): + print(ENG_BUILD_REQUIRED.format(host_webapps_dir)) + sys.exit(1) # TODO without os.chdir, chained imports fail below os.chdir(self.mochitest_dir) @@ -174,23 +282,10 @@ class MochitestRunner(MozbuildObject): options = Namespace(**kwargs) - if test_path: - if options.chrome: - test_root_file = mozpath.join( - self.mochitest_dir, - 'chrome', - test_path) - else: - test_root_file = mozpath.join( - self.mochitest_dir, - 'tests', - test_path) - if not os.path.exists(test_root_file): - print( - 'Specified test path does not exist: %s' % - test_root_file) - return 1 - options.testPath = test_path + from manifestparser import TestManifest + manifest = TestManifest() + manifest.tests.extend(tests) + options.manifestFile = manifest if options.desktop: return mochitest.run_desktop_mochitests(options) @@ -199,26 +294,18 @@ class MochitestRunner(MozbuildObject): which.which('adb') except which.WhichError: # TODO Find adb automatically if it isn't on the path - print(ADB_NOT_FOUND % ('mochitest-remote', options.b2gPath)) + print(ADB_NOT_FOUND.format(options.b2gPath)) return 1 return mochitest.run_remote_mochitests(options) - def run_desktop_test(self, context, suite=None, test_paths=None, **kwargs): + def run_desktop_test(self, context, tests=None, suite=None, **kwargs): """Runs a mochitest. suite is the type of mochitest to run. It can be one of ('plain', - 'chrome', 'browser', 'metro', 'a11y', 'jetpack-package', 'jetpack-addon'). - - test_paths are path to tests. They can be a relative path from the - top source directory, an absolute filename, or a directory containing - test files. + 'chrome', 'browser', 'a11y', 'jetpack-package', 'jetpack-addon', + 'webapprt-chrome', 'webapprt-content'). """ - # Make absolute paths relative before calling os.chdir() below. - if test_paths: - test_paths = [self._wrap_path_argument( - p).relpath() if os.path.isabs(p) else p for p in test_paths] - # runtests.py is ambiguous, so we load the file/module manually. if 'mochitest' not in sys.modules: import imp @@ -228,8 +315,6 @@ class MochitestRunner(MozbuildObject): ('.py', 'r', imp.PY_SOURCE)) import mochitest - from manifestparser import TestManifest - from mozbuild.testing import TestResolver # This is required to make other components happy. Sad, isn't it? os.chdir(self.topobjdir) @@ -243,64 +328,24 @@ class MochitestRunner(MozbuildObject): options = Namespace(**kwargs) - flavor = suite - - if suite == 'plain': - # Don't need additional options for plain. - flavor = 'mochitest' - elif suite == 'chrome': - options.chrome = True - elif suite == 'browser': - options.browserChrome = True - flavor = 'browser-chrome' - elif suite == 'devtools': - options.browserChrome = True - options.subsuite = 'devtools' - elif suite == 'jetpack-package': - options.jetpackPackage = True - elif suite == 'jetpack-addon': - options.jetpackAddon = True - elif suite == 'metro': - options.immersiveMode = True - options.browserChrome = True - elif suite == 'a11y': - options.a11y = True - elif suite == 'webapprt-content': - options.webapprtContent = True + if suite == 'webapprt-content': if not options.app or options.app == self.get_binary_path(): options.app = self.get_webapp_runtime_path() options.xrePath = self.get_webapp_runtime_xre_path() elif suite == 'webapprt-chrome': - options.webapprtChrome = True options.browserArgs.append("-test-mode") if not options.app or options.app == self.get_binary_path(): options.app = self.get_webapp_runtime_path() options.xrePath = self.get_webapp_runtime_xre_path() - else: - raise Exception('None or unrecognized mochitest suite type.') - if test_paths: - resolver = self._spawn(TestResolver) + from manifestparser import TestManifest + manifest = TestManifest() + manifest.tests.extend(tests) + options.manifestFile = manifest - tests = list( - resolver.resolve_tests( - paths=test_paths, - flavor=flavor)) - - if not tests: - print('No tests could be found in the path specified. Please ' - 'specify a path that is a test file or is a directory ' - 'containing tests.') - return 1 - - manifest = TestManifest() - manifest.tests.extend(tests) - - # XXX why is this such a special case? - if len(tests) == 1 and options.closeWhenDone and suite == 'plain': - options.closeWhenDone = False - - options.manifestFile = manifest + # XXX why is this such a special case? + if len(tests) == 1 and options.closeWhenDone and suite == 'plain': + options.closeWhenDone = False # We need this to enable colorization of output. self.log_manager.enable_unstructured() @@ -308,9 +353,11 @@ class MochitestRunner(MozbuildObject): self.log_manager.disable_unstructured() return result - def run_android_test(self, test_path, **kwargs): - self.tests_dir = os.path.join(self.topobjdir, '_tests') - self.mochitest_dir = os.path.join(self.tests_dir, 'testing', 'mochitest') + def run_android_test(self, context, tests, suite=None, **kwargs): + host_ret = verify_host_bin() + if host_ret != 0: + return host_ret + import imp path = os.path.join(self.mochitest_dir, 'runtestsremote.py') with open(path, 'r') as fh: @@ -319,21 +366,17 @@ class MochitestRunner(MozbuildObject): import runtestsremote options = Namespace(**kwargs) - if test_path: - options.testPath = test_path - sys.exit(runtestsremote.run_test_harness(options)) + from manifestparser import TestManifest + manifest = TestManifest() + manifest.tests.extend(tests) + options.manifestFile = manifest + + return runtestsremote.run_test_harness(options) # parser -def TestPathArg(func): - test_paths = CommandArgument('test_paths', nargs='*', metavar='TEST', default=None, - help='Test to run. Can be a single test file or a directory of tests to ' - '(run recursively). If omitted, the entire suite is run.') - return test_paths(func) - - def setup_argument_parser(): build_obj = MozbuildObject.from_environment(cwd=here) @@ -359,17 +402,17 @@ def setup_argument_parser(): # condition filters -def is_platform_in(*platforms): - def is_platform_supported(cls): - for p in platforms: - c = getattr(conditions, 'is_{}'.format(p), None) +def is_buildapp_in(*apps): + def is_buildapp_supported(cls): + for a in apps: + c = getattr(conditions, 'is_{}'.format(a), None) if c and c(cls): return True return False - is_platform_supported.__doc__ = 'Must have a {} build.'.format( - ' or '.join(platforms)) - return is_platform_supported + is_buildapp_supported.__doc__ = 'Must have a {} build.'.format( + ' or '.join(apps)) + return is_buildapp_supported def verify_host_bin(): @@ -389,263 +432,144 @@ def verify_host_bin(): @CommandProvider class MachCommands(MachCommandBase): - - def __init__(self, context): - MachCommandBase.__init__(self, context) - - for attr in ('device_name', 'target_out'): - setattr(self, attr, getattr(context, attr, None)) - - @Command( - 'mochitest-plain', - category='testing', - conditions=[is_platform_in('firefox', 'mulet', 'b2g', 'b2g_desktop', 'android')], - description='Run a plain mochitest (integration test, plain web page).', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_plain(self, test_paths, **kwargs): - if is_platform_in('firefox', 'mulet')(self): - return self.run_mochitest(test_paths, 'plain', **kwargs) - elif conditions.is_emulator(self): - return self.run_mochitest_remote(test_paths, **kwargs) - elif conditions.is_b2g_desktop(self): - return self.run_mochitest_b2g_desktop(test_paths, **kwargs) - elif conditions.is_android(self): - return self.run_mochitest_android(test_paths, **kwargs) - - @Command( - 'mochitest-chrome', - category='testing', - conditions=[is_platform_in('firefox', 'emulator', 'android')], - description='Run a chrome mochitest (integration test with some XUL).', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_chrome(self, test_paths, **kwargs): - kwargs['chrome'] = True - if conditions.is_firefox(self): - return self.run_mochitest(test_paths, 'chrome', **kwargs) - elif conditions.is_b2g(self) and conditions.is_emulator(self): - return self.run_mochitest_remote(test_paths, **kwargs) - elif conditions.is_android(self): - return self.run_mochitest_android(test_paths, **kwargs) - - @Command( - 'mochitest-browser', - category='testing', - conditions=[conditions.is_firefox], - description='Run a mochitest with browser chrome (integration test with a standard browser).', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_browser(self, test_paths, **kwargs): - return self.run_mochitest(test_paths, 'browser', **kwargs) - - @Command( - 'mochitest-devtools', - category='testing', - conditions=[conditions.is_firefox], - description='Run a devtools mochitest with browser chrome (integration test with a standard browser with the devtools frame).', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_devtools(self, test_paths, **kwargs): - return self.run_mochitest(test_paths, 'devtools', **kwargs) - - @Command('jetpack-package', category='testing', - conditions=[conditions.is_firefox], - description='Run a jetpack package test.', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_jetpack_package(self, test_paths, **kwargs): - return self.run_mochitest(test_paths, 'jetpack-package', **kwargs) - - @Command('jetpack-addon', category='testing', - conditions=[conditions.is_firefox], - description='Run a jetpack addon test.', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_jetpack_addon(self, test_paths, **kwargs): - return self.run_mochitest(test_paths, 'jetpack-addon', **kwargs) - - @Command( - 'mochitest-metro', - category='testing', - conditions=[conditions.is_firefox], - description='Run a mochitest with metro browser chrome (tests for Windows touch interface).', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_metro(self, test_paths, **kwargs): - return self.run_mochitest(test_paths, 'metro', **kwargs) - - @Command('mochitest-a11y', category='testing', - conditions=[conditions.is_firefox], - description='Run an a11y mochitest (accessibility tests).', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_a11y(self, test_paths, **kwargs): - return self.run_mochitest(test_paths, 'a11y', **kwargs) - - @Command( - 'webapprt-test-chrome', - category='testing', - conditions=[conditions.is_firefox], - description='Run a webapprt chrome mochitest (Web App Runtime with the browser chrome).', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_webapprt_chrome(self, test_paths, **kwargs): - return self.run_mochitest(test_paths, 'webapprt-chrome', **kwargs) - - @Command( - 'webapprt-test-content', - category='testing', - conditions=[conditions.is_firefox], - description='Run a webapprt content mochitest (Content rendering of the Web App Runtime).', - parser=setup_argument_parser) - @TestPathArg - def run_mochitest_webapprt_content(self, test_paths, **kwargs): - return self.run_mochitest(test_paths, 'webapprt-content', **kwargs) - @Command('mochitest', category='testing', - conditions=[conditions.is_firefox], + conditions=[is_buildapp_in(*SUPPORTED_APPS)], description='Run any flavor of mochitest (integration test).', parser=setup_argument_parser) - @CommandArgument('-f', '--flavor', choices=FLAVORS.keys(), + @CommandArgument('-f', '--flavor', + metavar='{{{}}}'.format(', '.join(CANONICAL_FLAVORS)), + choices=SUPPORTED_FLAVORS, help='Only run tests of this flavor.') - @TestPathArg + @CommandArgument('test_paths', nargs='*', metavar='TEST', default=None, + help='Test to run. Can be a single test file or a directory of tests ' + '(to run recursively). If omitted, the entire suite is run.') def run_mochitest_general(self, test_paths, flavor=None, test_objects=None, **kwargs): - self._preruntest() - from mozbuild.testing import TestResolver + buildapp = None + for app in SUPPORTED_APPS: + if is_buildapp_in(app)(self): + buildapp = app + break - if test_objects: - tests = test_objects + flavors = None + if flavor: + for fname, fobj in ALL_FLAVORS.iteritems(): + if flavor in fobj['aliases']: + if buildapp not in fobj['enabled_apps']: + continue + flavors = [fname] + break else: - resolver = self._spawn(TestResolver) - tests = list(resolver.resolve_tests(paths=test_paths, - cwd=self._mach_context.cwd)) + flavors = [f for f, v in ALL_FLAVORS.iteritems() if buildapp in v['enabled_apps']] - # Our current approach is to group the tests by suite and then perform - # an invocation for each suite. Ideally, this would be done - # automatically inside of core mochitest code. But it wasn't designed - # to do that. - # - # This does mean our output is less than ideal. When running tests from - # multiple suites, we see redundant summary lines. Hopefully once we - # have better machine readable output coming from mochitest land we can - # aggregate that here and improve the output formatting. + from mozbuild.controller.building import BuildDriver + self._ensure_state_subdir_exists('.') - suites = {} - for test in tests: - # Filter out non-mochitests. - if test['flavor'] not in FLAVORS: - continue + driver = self._spawn(BuildDriver) + driver.install_tests(remove=False) - if flavor and test['flavor'] != flavor: - continue - - suite = FLAVORS[test['flavor']] - suites.setdefault(suite, []).append(test) + if test_paths and buildapp == 'b2g': + # In B2G there is often a 'gecko' directory, though topsrcdir is actually + # elsewhere. This little hack makes test paths like 'gecko/dom' work, even if + # GECKO_PATH is set in the .userconfig + gecko_path = mozpath.abspath(mozpath.join(kwargs['b2gPath'], 'gecko')) + if gecko_path != self.topsrcdir: + old_paths = test_paths[:] + test_paths = [] + for tp in old_paths: + if mozpath.abspath(tp).startswith(gecko_path): + test_paths.append(mozpath.relpath(tp, gecko_path)) + else: + test_paths.append(tp) mochitest = self._spawn(MochitestRunner) + tests = mochitest.resolve_tests(test_paths, test_objects, cwd=self._mach_context.cwd) + + subsuite = kwargs.get('subsuite') + if subsuite == 'default': + kwargs['subsuite'] = None + + suites = defaultdict(list) + unsupported = set() + for test in tests: + # Filter out non-mochitests and unsupported flavors. + if test['flavor'] not in ALL_FLAVORS: + continue + + key = (test['flavor'], test['subsuite']) + if test['flavor'] not in flavors: + unsupported.add(key) + continue + + if subsuite == 'default': + # "--subsuite default" means only run tests that don't have a subsuite + if test['subsuite']: + unsupported.add(key) + continue + elif subsuite and test['subsuite'] != subsuite: + unsupported.add(key) + continue + + suites[key].append(test) + + if not suites: + # Make it very clear why no tests were found + if not unsupported: + print(TESTS_NOT_FOUND.format('\n'.join( + sorted(list(test_paths or test_objects))))) + return 1 + + msg = [] + for f, s in unsupported: + fobj = ALL_FLAVORS[f] + apps = fobj['enabled_apps'] + name = fobj['aliases'][0] + if s: + name = '{} --subsuite {}'.format(name, s) + + if buildapp not in apps: + reason = 'requires {}'.format(' or '.join(apps)) + else: + reason = 'excluded by the command line' + msg.append(' mochitest -f {} ({})'.format(name, reason)) + print(SUPPORTED_TESTS_NOT_FOUND.format( + buildapp, '\n'.join(sorted(msg)))) + return 1 + + if buildapp in ('b2g', 'b2g_desktop'): + run_mochitest = mochitest.run_b2g_test + elif buildapp == 'android': + run_mochitest = mochitest.run_android_test + else: + run_mochitest = mochitest.run_desktop_test + overall = None - for suite, tests in sorted(suites.items()): - result = mochitest.run_desktop_test( + for (flavor, subsuite), tests in sorted(suites.items()): + fobj = ALL_FLAVORS[flavor] + msg = fobj['aliases'][0] + if subsuite: + msg = '{} with subsuite {}'.format(msg, subsuite) + print(NOW_RUNNING.format(msg)) + + harness_args = kwargs.copy() + harness_args.update(fobj.get('extra_args', {})) + + result = run_mochitest( self._mach_context, - test_paths=[ - test['file_relpath'] for test in tests], - suite=suite, - **kwargs) + tests=tests, + suite=fobj['suite'], + **harness_args) + if result: overall = result + # TODO consolidate summaries from all suites return overall - def _preruntest(self): - from mozbuild.controller.building import BuildDriver - - self._ensure_state_subdir_exists('.') - - driver = self._spawn(BuildDriver) - driver.install_tests(remove=False) - - def run_mochitest(self, test_paths, flavor, **kwargs): - self._preruntest() - - mochitest = self._spawn(MochitestRunner) - - return mochitest.run_desktop_test( - self._mach_context, - test_paths=test_paths, - suite=flavor, - **kwargs) - - def run_mochitest_remote(self, test_paths, **kwargs): - if self.target_out: - host_webapps_dir = os.path.join( - self.target_out, - 'data', - 'local', - 'webapps') - if not os.path.isdir( - os.path.join( - host_webapps_dir, - 'test-container.gaiamobile.org')): - print( - ENG_BUILD_REQUIRED % - ('mochitest-remote', host_webapps_dir)) - return 1 - - from mozbuild.controller.building import BuildDriver - - self._ensure_state_subdir_exists('.') - - driver = self._spawn(BuildDriver) - driver.install_tests(remove=False) - - mochitest = self._spawn(MochitestRunner) - return mochitest.run_b2g_test( - test_paths=test_paths, - **kwargs) - - def run_mochitest_b2g_desktop(self, test_paths, **kwargs): - kwargs['profile'] = kwargs.get( - 'profile') or os.environ.get('GAIA_PROFILE') - if not kwargs['profile'] or not os.path.isdir(kwargs['profile']): - print(GAIA_PROFILE_NOT_FOUND % 'mochitest-b2g-desktop') - return 1 - - if os.path.isfile(os.path.join(kwargs['profile'], 'extensions', - 'httpd@gaiamobile.org')): - print(GAIA_PROFILE_IS_DEBUG % ('mochitest-b2g-desktop', - kwargs['profile'])) - return 1 - - from mozbuild.controller.building import BuildDriver - - self._ensure_state_subdir_exists('.') - - driver = self._spawn(BuildDriver) - driver.install_tests(remove=False) - - mochitest = self._spawn(MochitestRunner) - return mochitest.run_b2g_test(test_paths=test_paths, **kwargs) - - def run_mochitest_android(self, test_paths, **kwargs): - host_ret = verify_host_bin() - if host_ret != 0: - return host_ret - - test_path = None - if test_paths: - if len(test_paths) > 1: - print('Warning: Only the first test path will be used.') - test_path = self._wrap_path_argument(test_paths[0]).relpath() - - mochitest = self._spawn(MochitestRunner) - return mochitest.run_android_test(test_path, **kwargs) - @CommandProvider -class AndroidCommands(MachCommandBase): +class RobocopCommands(MachCommandBase): @Command('robocop', category='testing', conditions=[conditions.is_android], @@ -659,10 +583,6 @@ class AndroidCommands(MachCommandBase): help='Test to run. Can be specified as a Robocop test name (like "testLoad"), ' 'or omitted. If omitted, the entire test suite is executed.') def run_robocop(self, test_path, **kwargs): - host_ret = verify_host_bin() - if host_ret != 0: - return host_ret - if not kwargs.get('robocopIni'): kwargs['robocopIni'] = os.path.join(self.topobjdir, '_tests', 'testing', 'mochitest', 'robocop.ini') @@ -670,5 +590,66 @@ class AndroidCommands(MachCommandBase): if not kwargs.get('robocopApk'): kwargs['robocopApk'] = os.path.join(self.topobjdir, 'build', 'mobile', 'robocop', 'robocop-debug.apk') + + if isinstance(test_path, basestring): + test_path = [test_path] + mochitest = self._spawn(MochitestRunner) - return mochitest.run_android_test(test_path, **kwargs) + tests = mochitest.resolve_tests(test_path, cwd=self._mach_context.cwd) + return mochitest.run_android_test(self._mach_context, tests, 'robocop', **kwargs) + + +def REMOVED(cls): + """Command no longer exists! Use |mach mochitest| instead. + + The |mach mochitest| command will automatically detect which flavors and + subsuites exist in a given directory. If desired, flavors and subsuites + can be restricted using `--flavor` and `--subsuite` respectively. E.g: + + $ ./mach mochitest dom/indexedDB + + will run all of the plain, chrome and browser-chrome mochitests in that + directory. To only run the plain mochitests: + + $ ./mach mochitest -f plain dom/indexedDB + """ + return False + + +@CommandProvider +class DeprecatedCommands(MachCommandBase): + @Command('mochitest-plain', category='testing', conditions=[REMOVED]) + def mochitest_plain(self): + pass + + @Command('mochitest-chrome', category='testing', conditions=[REMOVED]) + def mochitest_chrome(self): + pass + + @Command('mochitest-browser', category='testing', conditions=[REMOVED]) + def mochitest_browser(self): + pass + + @Command('mochitest-devtools', category='testing', conditions=[REMOVED]) + def mochitest_devtools(self): + pass + + @Command('mochitest-a11y', category='testing', conditions=[REMOVED]) + def mochitest_a11y(self): + pass + + @Command('jetpack-addon', category='testing', conditions=[REMOVED]) + def jetpack_addon(self): + pass + + @Command('jetpack-package', category='testing', conditions=[REMOVED]) + def jetpack_package(self): + pass + + @Command('webapprt-test-chrome', category='testing', conditions=[REMOVED]) + def webapprt_chrome(self): + pass + + @Command('webapprt-test-content', category='testing', conditions=[REMOVED]) + def webapprt_content(self): + pass diff --git a/testing/mochitest/runtestsb2g.py b/testing/mochitest/runtestsb2g.py index b4a1f634d678..788dcd79f5f2 100644 --- a/testing/mochitest/runtestsb2g.py +++ b/testing/mochitest/runtestsb2g.py @@ -73,12 +73,7 @@ class B2GMochitest(MochitestUtilsMixin): def buildTestPath(self, options, testsToFilter=None): if options.manifestFile != 'tests.json': - super( - B2GMochitest, - self).buildTestPath( - options, - testsToFilter, - disabled=False) + super(B2GMochitest, self).buildTestPath(options, testsToFilter, disabled=False) return self.buildTestURL(options) def build_profile(self, options): @@ -199,7 +194,17 @@ class B2GMochitest(MochitestUtilsMixin): self.killNamedOrphans('xpcshell') self.startServers(options, None) + + # In desktop mochitests buildTestPath is called before buildURLOptions. This + # means options.manifestFile has already been converted to the proper json + # style manifest. Not so with B2G, that conversion along with updating the URL + # option will happen later. So backup and restore options.manifestFile to + # prevent us from trying to pass in an instance of TestManifest via url param. + manifestFile = options.manifestFile + options.manifestFile = None self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'}) + options.manifestFile = manifestFile + self.test_script_args.append(not options.emulator) self.test_script_args.append(options.wifi) self.test_script_args.append(options.chrome) @@ -479,7 +484,7 @@ def run_remote_mochitests(options): mochitest.message_logger.finish() - sys.exit(retVal) + return retVal def run_desktop_mochitests(options): @@ -510,7 +515,7 @@ def run_desktop_mochitests(options): retVal = mochitest.runTests(options, onLaunch=mochitest.startTests) mochitest.message_logger.finish() - sys.exit(retVal) + return retVal def main(): @@ -518,9 +523,9 @@ def main(): options = parser.parse_args() if options.desktop: - run_desktop_mochitests(options) + return run_desktop_mochitests(options) else: - run_remote_mochitests(options) + return run_remote_mochitests(options) if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/testing/taskcluster/tasks/branches/base_job_flags.yml b/testing/taskcluster/tasks/branches/base_job_flags.yml index f08738d64a4f..e1abe7c62f10 100644 --- a/testing/taskcluster/tasks/branches/base_job_flags.yml +++ b/testing/taskcluster/tasks/branches/base_job_flags.yml @@ -27,6 +27,7 @@ flags: - aries-ota - aries-eng - android-api-11 + - linux64 tests: - cppunit diff --git a/testing/taskcluster/tasks/branches/try/job_flags.yml b/testing/taskcluster/tasks/branches/try/job_flags.yml index 686431667569..bdbe6498dbdd 100644 --- a/testing/taskcluster/tasks/branches/try/job_flags.yml +++ b/testing/taskcluster/tasks/branches/try/job_flags.yml @@ -122,6 +122,12 @@ builds: types: opt: task: tasks/builds/android_api_11.yml + linux64: + platforms: + - Opt Linux64 + types: + opt: + task: tasks/builds/opt_linux64.yml tests: cppunit: diff --git a/testing/taskcluster/tasks/builds/opt_linux64.yml b/testing/taskcluster/tasks/builds/opt_linux64.yml new file mode 100644 index 000000000000..fed5177c029e --- /dev/null +++ b/testing/taskcluster/tasks/builds/opt_linux64.yml @@ -0,0 +1,53 @@ +$inherits: + from: 'tasks/build.yml' + variables: + build_name: 'linux64' + build_type: 'opt' +task: + metadata: + name: '[TC] Linux64 Opt' + description: 'Linux64 Opt' + + workerType: b2g-desktop-opt + + routes: + - 'index.buildbot.branches.{{project}}.linux64' + - 'index.buildbot.revisions.{{head_rev}}.{{project}}.linux64' + + scopes: + - 'docker-worker:cache:build-linux64-workspace' + - 'docker-worker:cache:tooltool-cache' + + payload: + image: '{{#docker_image}}desktop-build{{/docker_image}}' + cache: + build-linux64-workspace: '/home/worker/workspace' + tooltool-cache: '/home/worker/tooltool-cache' + + env: + MOZHARNESS_SCRIPT: 'mozharness/scripts/fx_desktop_build.py' + MOZHARNESS_CONFIG: 'builds/releng_base_linux_64_builds.py balrog/production.py disable_sendchange.py' + MH_BRANCH: {{project}} + MH_BUILD_POOL: taskcluster + # image paths + TOOLTOOL_CACHE: '/home/worker/tooltool-cache' + RELENGAPI_TOKEN: 'TODO' # 1164612: encrypt this secret + + maxRunTime: 36000 + + command: ["/bin/bash", "bin/build.sh"] + + extra: + treeherderEnv: + - production + - staging + treeherder: + machine: + # see https://github.com/mozilla/treeherder/blob/master/ui/js/values.js + platform: linux64 + # Rather then enforcing particular conventions we require that all build + # tasks provide the "build" extra field to specify where the build and tests + # files are located. + locations: + build: 'public/build/target.linux-x86_64.tar.bz2' + tests: 'public/build/target.tests.zip' diff --git a/toolkit/components/telemetry/TelemetryEnvironment.jsm b/toolkit/components/telemetry/TelemetryEnvironment.jsm index 32d27520d25c..63b47669289b 100644 --- a/toolkit/components/telemetry/TelemetryEnvironment.jsm +++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm @@ -919,7 +919,7 @@ EnvironmentCache.prototype = { }, /** - * Determine if Firefox is the default browser. + * Determine if we're the default browser. * @returns null on error, true if we are the default browser, or false otherwise. */ _isDefaultBrowser: function () { @@ -927,7 +927,7 @@ EnvironmentCache.prototype = { return true; #else if (!("@mozilla.org/browser/shell-service;1" in Cc)) { - this._log.error("_isDefaultBrowser - Could not obtain shell service"); + this._log.info("_isDefaultBrowser - Could not obtain browser shell service"); return null; } @@ -940,14 +940,12 @@ EnvironmentCache.prototype = { return null; } - if (shellService) { - try { - // This uses the same set of flags used by the pref pane. - return shellService.isDefaultBrowser(false, true) ? true : false; - } catch (ex) { - this._log.error("_isDefaultBrowser - Could not determine if default browser", ex); - return null; - } + try { + // This uses the same set of flags used by the pref pane. + return shellService.isDefaultBrowser(false, true) ? true : false; + } catch (ex) { + this._log.error("_isDefaultBrowser - Could not determine if default browser", ex); + return null; } return null; diff --git a/widget/cocoa/nsPrintDialogX.mm b/widget/cocoa/nsPrintDialogX.mm index a3724b1c9153..61935e9e9b70 100644 --- a/widget/cocoa/nsPrintDialogX.mm +++ b/widget/cocoa/nsPrintDialogX.mm @@ -88,9 +88,14 @@ nsPrintDialogServiceX::Show(nsIDOMWindow *aParent, nsIPrintSettings *aSettings, int button = [panel runModal]; nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); - settingsX->SetCocoaPrintInfo([[[NSPrintOperation currentOperation] printInfo] copy]); + NSPrintInfo* copy = [[[NSPrintOperation currentOperation] printInfo] copy]; + if (!copy) { + return NS_ERROR_OUT_OF_MEMORY; + } + settingsX->SetCocoaPrintInfo(copy); + [copy release]; + [NSPrintOperation setCurrentOperation:nil]; - [printInfo release]; [tmpView release]; if (button != NSOKButton) diff --git a/widget/cocoa/nsPrintOptionsX.h b/widget/cocoa/nsPrintOptionsX.h index a87e763ba532..ad8313764440 100644 --- a/widget/cocoa/nsPrintOptionsX.h +++ b/widget/cocoa/nsPrintOptionsX.h @@ -8,11 +8,26 @@ #include "nsPrintOptionsImpl.h" +namespace mozilla +{ +namespace embedding +{ + class PrintData; +} // namespace embedding +} // namespace mozilla + class nsPrintOptionsX : public nsPrintOptions { public: nsPrintOptionsX(); virtual ~nsPrintOptionsX(); + + NS_IMETHODIMP SerializeToPrintData(nsIPrintSettings* aSettings, + nsIWebBrowserPrint* aWBP, + mozilla::embedding::PrintData* data); + NS_IMETHODIMP DeserializeToPrintSettings(const mozilla::embedding::PrintData& data, + nsIPrintSettings* settings); + protected: nsresult _CreatePrintSettings(nsIPrintSettings **_retval); nsresult ReadPrefs(nsIPrintSettings* aPS, const nsAString& aPrinterName, uint32_t aFlags); diff --git a/widget/cocoa/nsPrintOptionsX.mm b/widget/cocoa/nsPrintOptionsX.mm index db72392dc3b3..48cd3db16924 100644 --- a/widget/cocoa/nsPrintOptionsX.mm +++ b/widget/cocoa/nsPrintOptionsX.mm @@ -9,6 +9,8 @@ #include "nsPrintOptionsX.h" #include "nsPrintSettingsX.h" +using namespace mozilla::embedding; + nsPrintOptionsX::nsPrintOptionsX() { } @@ -17,6 +19,210 @@ nsPrintOptionsX::~nsPrintOptionsX() { } +NS_IMETHODIMP +nsPrintOptionsX::SerializeToPrintData(nsIPrintSettings* aSettings, + nsIWebBrowserPrint* aWBP, + PrintData* data) +{ + nsresult rv = nsPrintOptions::SerializeToPrintData(aSettings, aWBP, data); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (aWBP) { + // When serializing an nsIWebBrowserPrint, we need to pass up the first + // document name. We could pass up the entire collection of document + // names, but the OS X printing prompt code only really cares about + // the first one, so we just send the first to save IPC traffic. + char16_t** docTitles; + uint32_t titleCount; + nsresult rv = aWBP->EnumerateDocumentNames(&titleCount, &docTitles); + if (NS_SUCCEEDED(rv) && titleCount > 0) { + data->printJobName().Assign(docTitles[0]); + } + + for (int32_t i = titleCount - 1; i >= 0; i--) { + free(docTitles[i]); + } + free(docTitles); + docTitles = nullptr; + } + + nsRefPtr settingsX(do_QueryObject(aSettings)); + if (NS_WARN_IF(!settingsX)) { + return NS_ERROR_FAILURE; + } + + NSPrintInfo* printInfo = settingsX->GetCocoaPrintInfo(); + if (NS_WARN_IF(!printInfo)) { + return NS_ERROR_FAILURE; + } + + NSDictionary* dict = [printInfo dictionary]; + if (NS_WARN_IF(!dict)) { + return NS_ERROR_FAILURE; + } + + NSString* printerName = [dict objectForKey: NSPrintPrinterName]; + if (printerName) { + nsCocoaUtils::GetStringForNSString(printerName, data->printerName()); + } + + NSString* faxNumber = [dict objectForKey: NSPrintFaxNumber]; + if (faxNumber) { + nsCocoaUtils::GetStringForNSString(faxNumber, data->faxNumber()); + } + + NSURL* printToFileURL = [dict objectForKey: NSPrintJobSavingURL]; + if (printToFileURL) { + nsCocoaUtils::GetStringForNSString([printToFileURL absoluteString], + data->toFileName()); + } + + NSDate* printTime = [dict objectForKey: NSPrintTime]; + if (printTime) { + NSTimeInterval timestamp = [printTime timeIntervalSinceReferenceDate]; + data->printTime() = timestamp; + } + + NSString* disposition = [dict objectForKey: NSPrintJobDisposition]; + if (disposition) { + nsCocoaUtils::GetStringForNSString(disposition, data->disposition()); + } + + data->numCopies() = [[dict objectForKey: NSPrintCopies] intValue]; + data->printAllPages() = [[dict objectForKey: NSPrintAllPages] boolValue]; + data->startPageRange() = [[dict objectForKey: NSPrintFirstPage] intValue]; + data->endPageRange() = [[dict objectForKey: NSPrintLastPage] intValue]; + data->mustCollate() = [[dict objectForKey: NSPrintMustCollate] boolValue]; + data->printReversed() = [[dict objectForKey: NSPrintReversePageOrder] boolValue]; + data->pagesAcross() = [[dict objectForKey: NSPrintPagesAcross] unsignedShortValue]; + data->pagesDown() = [[dict objectForKey: NSPrintPagesDown] unsignedShortValue]; + data->detailedErrorReporting() = [[dict objectForKey: NSPrintDetailedErrorReporting] boolValue]; + data->addHeaderAndFooter() = [[dict objectForKey: NSPrintHeaderAndFooter] boolValue]; + data->fileNameExtensionHidden() = + [[dict objectForKey: NSPrintJobSavingFileNameExtensionHidden] boolValue]; + + bool printSelectionOnly = [[dict objectForKey: NSPrintSelectionOnly] boolValue]; + aSettings->SetPrintOptions(nsIPrintSettings::kEnableSelectionRB, + printSelectionOnly); + aSettings->GetPrintOptionsBits(&data->optionFlags()); + + return NS_OK; +} + +NS_IMETHODIMP +nsPrintOptionsX::DeserializeToPrintSettings(const PrintData& data, + nsIPrintSettings* settings) +{ + nsresult rv = nsPrintOptions::DeserializeToPrintSettings(data, settings); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsRefPtr settingsX(do_QueryObject(settings)); + if (NS_WARN_IF(!settingsX)) { + return NS_ERROR_FAILURE; + } + + NSPrintInfo* sharedPrintInfo = [NSPrintInfo sharedPrintInfo]; + if (NS_WARN_IF(!sharedPrintInfo)) { + return NS_ERROR_FAILURE; + } + + NSDictionary* sharedDict = [sharedPrintInfo dictionary]; + if (NS_WARN_IF(!sharedDict)) { + return NS_ERROR_FAILURE; + } + + // We need to create a new NSMutableDictionary to pass to NSPrintInfo with + // the values that we got from the other process. + NSMutableDictionary* newPrintInfoDict = + [NSMutableDictionary dictionaryWithDictionary:sharedDict]; + if (NS_WARN_IF(!newPrintInfoDict)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + NSString* printerName = nsCocoaUtils::ToNSString(data.printerName()); + if (printerName) { + NSPrinter* printer = [NSPrinter printerWithName: printerName]; + if (NS_WARN_IF(!printer)) { + return NS_ERROR_FAILURE; + } + [newPrintInfoDict setObject: printer forKey: NSPrintPrinter]; + [newPrintInfoDict setObject: printerName forKey: NSPrintPrinterName]; + } + + [newPrintInfoDict setObject: [NSNumber numberWithInt: data.numCopies()] + forKey: NSPrintCopies]; + [newPrintInfoDict setObject: [NSNumber numberWithBool: data.printAllPages()] + forKey: NSPrintAllPages]; + [newPrintInfoDict setObject: [NSNumber numberWithInt: data.startPageRange()] + forKey: NSPrintFirstPage]; + [newPrintInfoDict setObject: [NSNumber numberWithInt: data.endPageRange()] + forKey: NSPrintLastPage]; + [newPrintInfoDict setObject: [NSNumber numberWithBool: data.mustCollate()] + forKey: NSPrintMustCollate]; + [newPrintInfoDict setObject: [NSNumber numberWithBool: data.printReversed()] + forKey: NSPrintReversePageOrder]; + + [newPrintInfoDict setObject: nsCocoaUtils::ToNSString(data.disposition()) + forKey: NSPrintJobDisposition]; + + [newPrintInfoDict setObject: [NSNumber numberWithShort: data.pagesAcross()] + forKey: NSPrintPagesAcross]; + [newPrintInfoDict setObject: [NSNumber numberWithShort: data.pagesDown()] + forKey: NSPrintPagesDown]; + [newPrintInfoDict setObject: [NSNumber numberWithBool: data.detailedErrorReporting()] + forKey: NSPrintDetailedErrorReporting]; + [newPrintInfoDict setObject: nsCocoaUtils::ToNSString(data.faxNumber()) + forKey: NSPrintFaxNumber]; + [newPrintInfoDict setObject: [NSNumber numberWithBool: data.addHeaderAndFooter()] + forKey: NSPrintHeaderAndFooter]; + [newPrintInfoDict setObject: [NSNumber numberWithBool: data.fileNameExtensionHidden()] + forKey: NSPrintJobSavingFileNameExtensionHidden]; + + // At this point, the base class should have properly deserialized the print + // options bitfield for nsIPrintSettings, so that it holds the correct value + // for kEnableSelectionRB, which we use to set NSPrintSelectionOnly. + + bool printSelectionOnly = false; + rv = settings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB, &printSelectionOnly); + if (NS_SUCCEEDED(rv)) { + [newPrintInfoDict setObject: [NSNumber numberWithBool: printSelectionOnly] + forKey: NSPrintSelectionOnly]; + } else { + [newPrintInfoDict setObject: [NSNumber numberWithBool: NO] + forKey: NSPrintSelectionOnly]; + } + + NSURL* jobSavingURL = + [NSURL URLWithString:[nsCocoaUtils::ToNSString(data.toFileName()) + stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]; + if (jobSavingURL) { + [newPrintInfoDict setObject: jobSavingURL forKey: NSPrintJobSavingURL]; + } + + NSTimeInterval timestamp = data.printTime(); + NSDate* printTime = [NSDate dateWithTimeIntervalSinceReferenceDate: timestamp]; + if (printTime) { + [newPrintInfoDict setObject: printTime forKey: NSPrintTime]; + } + + // Next, we create a new NSPrintInfo with the values in our dictionary. + NSPrintInfo* newPrintInfo = + [[NSPrintInfo alloc] initWithDictionary: newPrintInfoDict]; + if (NS_WARN_IF(!newPrintInfo)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // And now swap in the new NSPrintInfo we've just populated. + settingsX->SetCocoaPrintInfo(newPrintInfo); + [newPrintInfo release]; + + return NS_OK; +} + nsresult nsPrintOptionsX::ReadPrefs(nsIPrintSettings* aPS, const nsAString& aPrinterName, uint32_t aFlags) { diff --git a/widget/cocoa/nsPrintSettingsX.mm b/widget/cocoa/nsPrintSettingsX.mm index d2306c87fd22..b3d381c40f6f 100644 --- a/widget/cocoa/nsPrintSettingsX.mm +++ b/widget/cocoa/nsPrintSettingsX.mm @@ -94,7 +94,10 @@ NS_IMETHODIMP nsPrintSettingsX::InitUnwriteableMargin() void nsPrintSettingsX::SetCocoaPrintInfo(NSPrintInfo* aPrintInfo) { - mPrintInfo = aPrintInfo; + if (mPrintInfo != aPrintInfo) { + [mPrintInfo release]; + mPrintInfo = [aPrintInfo retain]; + } } NS_IMETHODIMP nsPrintSettingsX::ReadPageFormatFromPrefs() diff --git a/widget/nsIPrintSettings.idl b/widget/nsIPrintSettings.idl index b57100959617..723dada0f5f7 100644 --- a/widget/nsIPrintSettings.idl +++ b/widget/nsIPrintSettings.idl @@ -22,7 +22,7 @@ interface nsIPrintSession; /** * Simplified graphics interface for JS rendering. */ -[scriptable, uuid(1bcfc611-8941-4c39-9e06-7116e564a1ce)] +[scriptable, uuid(11b86b46-12c9-4e63-8023-129dd239ace7)] interface nsIPrintSettings : nsISupports { @@ -137,10 +137,15 @@ interface nsIPrintSettings : nsISupports boolean GetPrintOptions(in int32_t aType); /** - * Set PrintOptions Bit field + * Get PrintOptions Bit field */ int32_t GetPrintOptionsBits(); + /** + * Set PrintOptions Bit field + */ + void SetPrintOptionsBits(in int32_t bits); + /** * Get the page size in twips, considering the * orientation (portrait or landscape). diff --git a/widget/nsPrintOptionsImpl.cpp b/widget/nsPrintOptionsImpl.cpp index f70d93595f5f..4689c04189ab 100644 --- a/widget/nsPrintOptionsImpl.cpp +++ b/widget/nsPrintOptionsImpl.cpp @@ -223,6 +223,8 @@ nsPrintOptions::SerializeToPrintData(nsIPrintSettings* aSettings, aSettings->GetIsInitializedFromPrefs(&data->isInitializedFromPrefs()); aSettings->GetPersistMarginBoxSettings(&data->persistMarginBoxSettings()); + aSettings->GetPrintOptionsBits(&data->optionFlags()); + // Initialize the platform-specific values that don't // default-initialize, so that we don't send uninitialized data over // IPC (which leads to valgrind warnings, and, for bools, fatal @@ -234,6 +236,17 @@ nsPrintOptions::SerializeToPrintData(nsIPrintSettings* aSettings, data->isIFrameSelected() = false; data->isRangeSelection() = false; // data->GTKPrintSettings() default-initializes + // data->printJobName() default-initializes + data->printAllPages() = true; + data->mustCollate() = false; + // data->disposition() default-initializes + data->pagesAcross() = 1; + data->pagesDown() = 1; + data->printTime() = 0; + data->detailedErrorReporting() = true; + // data->faxNumber() default-initializes + data->addHeaderAndFooter() = false; + data->fileNameExtensionHidden() = false; return NS_OK; } @@ -324,6 +337,8 @@ nsPrintOptions::DeserializeToPrintSettings(const PrintData& data, settings->SetIsInitializedFromPrefs(data.isInitializedFromPrefs()); settings->SetPersistMarginBoxSettings(data.persistMarginBoxSettings()); + settings->SetPrintOptionsBits(data.optionFlags()); + return NS_OK; } diff --git a/widget/nsPrintSettingsImpl.cpp b/widget/nsPrintSettingsImpl.cpp index 9a24706fca8a..c4ab73f3251c 100644 --- a/widget/nsPrintSettingsImpl.cpp +++ b/widget/nsPrintSettingsImpl.cpp @@ -693,7 +693,7 @@ nsPrintSettings::SetPrintOptions(int32_t aType, bool aTurnOnOff) * See documentation in nsPrintSettingsImpl.h * @update 1/12/01 rods */ -NS_IMETHODIMP +NS_IMETHODIMP nsPrintSettings::GetPrintOptionsBits(int32_t *aBits) { NS_ENSURE_ARG_POINTER(aBits); @@ -701,6 +701,13 @@ nsPrintSettings::GetPrintOptionsBits(int32_t *aBits) return NS_OK; } +NS_IMETHODIMP +nsPrintSettings::SetPrintOptionsBits(int32_t aBits) +{ + mPrintOptions = aBits; + return NS_OK; +} + /* attribute wstring docTitle; */ nsresult nsPrintSettings::GetMarginStrs(char16_t * *aTitle, diff --git a/xpcom/threads/ThreadStackHelper.cpp b/xpcom/threads/ThreadStackHelper.cpp index 0ee31add4d46..ba40cd8f9244 100644 --- a/xpcom/threads/ThreadStackHelper.cpp +++ b/xpcom/threads/ThreadStackHelper.cpp @@ -44,6 +44,7 @@ #include #include +#include #ifdef XP_LINUX #ifdef ANDROID