From edbe43df4faf870f00b607bdfd76f89fb7caddf0 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 12 Dec 2013 18:10:04 +0900 Subject: [PATCH 001/459] Bug 939367 - Part 2: Move requirements.txt into tools/docs Trivial change. No review. DONTBUILD (NPOTB) --HG-- rename : build/docs/requirements.txt => tools/docs/requirements.txt --- {build => tools}/docs/requirements.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {build => tools}/docs/requirements.txt (100%) diff --git a/build/docs/requirements.txt b/tools/docs/requirements.txt similarity index 100% rename from build/docs/requirements.txt rename to tools/docs/requirements.txt From e627be4fe169246ed24236bad0b6250018c66104 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Thu, 12 Dec 2013 18:39:04 +0900 Subject: [PATCH 002/459] Bug 948839 Mark special flag of drag over event true if it's consumed on content r=smaug --- content/events/src/nsDOMEvent.cpp | 21 +++++++++++++++++++++ content/events/src/nsEventStateManager.cpp | 5 +++-- widget/MouseEvents.h | 5 ++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index 52369931bbf1..171f9844af12 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -487,6 +487,27 @@ nsDOMEvent::PreventDefaultInternal(bool aCalledByDefaultHandler) if (!aCalledByDefaultHandler) { mEvent->mFlags.mDefaultPreventedByContent = true; } + + if (!IsTrusted()) { + return; + } + + WidgetDragEvent* dragEvent = mEvent->AsDragEvent(); + if (!dragEvent) { + return; + } + + nsCOMPtr node = do_QueryInterface(mEvent->currentTarget); + if (!node) { + nsCOMPtr win = do_QueryInterface(mEvent->currentTarget); + if (!win) { + return; + } + node = win->GetExtantDoc(); + } + if (!nsContentUtils::IsChromeDoc(node->OwnerDoc())) { + dragEvent->mDefaultPreventedOnContent = true; + } } void diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index d6b33c7e695b..67edf1722fe3 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -3571,9 +3571,10 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext, // For now, do this only for dragover. //XXXsmaug dragenter needs some more work. if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) { - // Someone has called preventDefault(), check whether is was content. + // Someone has called preventDefault(), check whether is was on + // content or chrome. dragSession->SetOnlyChromeDrop( - !aEvent->mFlags.mDefaultPreventedByContent); + !dragEvent->mDefaultPreventedOnContent); } } else if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) { // No one called preventDefault(), so handle drop only in chrome. diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index e4dd6bebd941..c49f4bbb5c42 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -250,7 +250,7 @@ public: WidgetDragEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget) : WidgetMouseEvent(aIsTrusted, aMessage, aWidget, NS_DRAG_EVENT, eReal), - userCancelled(false) + userCancelled(false), mDefaultPreventedOnContent(false) { mFlags.mCancelable = (aMessage != NS_DRAGDROP_EXIT_SYNTH && @@ -263,6 +263,8 @@ public: // If this is true, user has cancelled the drag operation. bool userCancelled; + // If this is true, the drag event's preventDefault() is called on content. + bool mDefaultPreventedOnContent; // XXX Not tested by test_assign_event_data.html void AssignDragEventData(const WidgetDragEvent& aEvent, bool aCopyTargets) @@ -272,6 +274,7 @@ public: dataTransfer = aEvent.dataTransfer; // XXX userCancelled isn't copied, is this instentionally? userCancelled = false; + mDefaultPreventedOnContent = aEvent.mDefaultPreventedOnContent; } }; From 539b6f395d58961f04784f8463043673f987b6f2 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 12 Dec 2013 10:18:37 +0000 Subject: [PATCH 003/459] Bug 938406 - DataStore - proper event sent when the first revision is created, r=ehsan --- dom/datastore/DataStore.jsm | 2 +- dom/datastore/DataStoreCursor.jsm | 2 +- dom/datastore/DataStoreDB.jsm | 2 +- dom/datastore/DataStoreService.js | 20 ++++++++++++-------- dom/datastore/DataStoreServiceInternal.jsm | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/dom/datastore/DataStore.jsm b/dom/datastore/DataStore.jsm index afe1a2645f1d..e43df1e92b8e 100644 --- a/dom/datastore/DataStore.jsm +++ b/dom/datastore/DataStore.jsm @@ -9,7 +9,7 @@ this.EXPORTED_SYMBOLS = ["DataStore"]; function debug(s) { - // dump('DEBUG DataStore: ' + s + '\n'); + //dump('DEBUG DataStore: ' + s + '\n'); } const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; diff --git a/dom/datastore/DataStoreCursor.jsm b/dom/datastore/DataStoreCursor.jsm index ef88c8186fcf..96f0cd3e7cf0 100644 --- a/dom/datastore/DataStoreCursor.jsm +++ b/dom/datastore/DataStoreCursor.jsm @@ -9,7 +9,7 @@ this.EXPORTED_SYMBOLS = ['DataStoreCursor']; function debug(s) { - // dump('DEBUG DataStoreCursor: ' + s + '\n'); + //dump('DEBUG DataStoreCursor: ' + s + '\n'); } const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; diff --git a/dom/datastore/DataStoreDB.jsm b/dom/datastore/DataStoreDB.jsm index bf5383550754..ec563875e543 100644 --- a/dom/datastore/DataStoreDB.jsm +++ b/dom/datastore/DataStoreDB.jsm @@ -9,7 +9,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; this.EXPORTED_SYMBOLS = ['DataStoreDB']; function debug(s) { - // dump('DEBUG DataStoreDB: ' + s + '\n'); + //dump('DEBUG DataStoreDB: ' + s + '\n'); } const DATASTOREDB_VERSION = 1; diff --git a/dom/datastore/DataStoreService.js b/dom/datastore/DataStoreService.js index 2e5485c43a77..24842bdd3fad 100644 --- a/dom/datastore/DataStoreService.js +++ b/dom/datastore/DataStoreService.js @@ -9,7 +9,7 @@ /* static functions */ function debug(s) { - // dump('DEBUG DataStoreService: ' + s + '\n'); + //dump('DEBUG DataStoreService: ' + s + '\n'); } const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; @@ -137,17 +137,13 @@ DataStoreService.prototype = { request.onsuccess = function(aEvent) { let cursor = aEvent.target.result; if (cursor) { - ppmm.broadcastAsyncMessage('datastore-first-revision-created', - { name: aName, owner: aOwner }); + debug("First revision already created."); + self.enableDataStore(aAppId, aName, aOwner); } else { // If the revision doesn't exist, let's create the first one. db.addRevision(aRevisionStore, 0, REVISION_VOID, function() { debug("First revision created."); - if (aName in self.stores && aAppId in self.stores[aName]) { - self.stores[aName][aAppId].enabled = true; - ppmm.broadcastAsyncMessage('datastore-first-revision-created', - { name: aName, owner: aOwner }); - } + self.enableDataStore(aAppId, aName, aOwner); }); } }; @@ -155,6 +151,14 @@ DataStoreService.prototype = { ); }, + enableDataStore: function(aAppId, aName, aOwner) { + if (aName in this.stores && aAppId in this.stores[aName]) { + this.stores[aName][aAppId].enabled = true; + ppmm.broadcastAsyncMessage('datastore-first-revision-created', + { name: aName, owner: aOwner }); + } + }, + addPermissions: function(aAppId, aName, aOrigin, aOwner, aReadOnly) { // When a new DataStore is installed, the permissions must be set for the // owner app. diff --git a/dom/datastore/DataStoreServiceInternal.jsm b/dom/datastore/DataStoreServiceInternal.jsm index e7816f8db6ab..f301a3793e30 100644 --- a/dom/datastore/DataStoreServiceInternal.jsm +++ b/dom/datastore/DataStoreServiceInternal.jsm @@ -9,7 +9,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; this.EXPORTED_SYMBOLS = ["DataStoreServiceInternal"]; function debug(s) { - // dump('DEBUG DataStoreServiceInternal: ' + s + '\n'); + //dump('DEBUG DataStoreServiceInternal: ' + s + '\n'); } Cu.import("resource://gre/modules/XPCOMUtils.jsm"); From cc0161739c645b925694074d5b93d721286c31e4 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 12 Dec 2013 11:21:59 +0000 Subject: [PATCH 004/459] Bug 949038 - Fix windows browser build error in jsweakmap.h with generational GC r=terrence --- js/src/jsweakmap.cpp | 17 +++++++++++++---- js/src/vm/ScopeObject.cpp | 13 +++++++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index 8fb35a416116..5d6bb8f013c4 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -269,17 +269,26 @@ TryPreserveReflector(JSContext *cx, HandleObject obj) } static inline void -WeakMapPostWriteBarrier(JSRuntime *rt, ObjectValueMap *map, JSObject *key) +WeakMapPostWriteBarrier(JSRuntime *rt, ObjectValueMap *weakMap, JSObject *key) { #ifdef JSGC_GENERATIONAL /* * Strip the barriers from the type before inserting into the store buffer. * This will automatically ensure that barriers do not fire during GC. + * + * Some compilers complain about instantiating the WeakMap class for + * unbarriered type arguments, so we cast to a HashMap instead. Because of + * WeakMap's multiple inheritace, We need to do this in two stages, first to + * the HashMap base class and then to the unbarriered version. */ - typedef WeakMap UnbarrieredObjectValueMap; - typedef gc::HashKeyRef Ref; + ObjectValueMap::Base *baseHashMap = static_cast(weakMap); + + typedef HashMap UnbarrieredMap; + UnbarrieredMap *unbarrieredMap = reinterpret_cast(baseHashMap); + + typedef gc::HashKeyRef Ref; if (key && IsInsideNursery(rt, key)) - rt->gcStoreBuffer.putGeneric(Ref(reinterpret_cast(map), key)); + rt->gcStoreBuffer.putGeneric(Ref((unbarrieredMap), key)); #endif } diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 55e844f8410f..b3a069b1c397 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -1535,11 +1535,20 @@ DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map, /* * Strip the barriers from the type before inserting into the store buffer. * This will automatically ensure that barriers do not fire during GC. + * + * Some compilers complain about instantiating the WeakMap class for + * unbarriered type arguments, so we cast to a HashMap instead. Because of + * WeakMap's multiple inheritace, We need to do this in two stages, first to + * the HashMap base class and then to the unbarriered version. */ - typedef WeakMap UnbarrieredMap; + ObjectWeakMap::Base *baseHashMap = static_cast(map); + + typedef HashMap UnbarrieredMap; + UnbarrieredMap *unbarrieredMap = reinterpret_cast(baseHashMap); + typedef gc::HashKeyRef Ref; if (key && IsInsideNursery(rt, key)) - rt->gcStoreBuffer.putGeneric(Ref(reinterpret_cast(map), key.get())); + rt->gcStoreBuffer.putGeneric(Ref(unbarrieredMap, key.get())); #endif } From 717525839f9d3c9804045ad3566747f1b6386c21 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 12 Dec 2013 11:22:00 +0000 Subject: [PATCH 005/459] Bug 948423 - Use relookupOrAdd() to insert into has type representation hash map following possible mutation by GC r=sfink --- js/src/builtin/TypeRepresentation.cpp | 2 +- js/src/jit-test/tests/gc/bug-948423.js | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/gc/bug-948423.js diff --git a/js/src/builtin/TypeRepresentation.cpp b/js/src/builtin/TypeRepresentation.cpp index 78cd8237b0f5..346a87502841 100644 --- a/js/src/builtin/TypeRepresentation.cpp +++ b/js/src/builtin/TypeRepresentation.cpp @@ -383,7 +383,7 @@ TypeRepresentation::addToTableOrFree(JSContext *cx, return nullptr; // Next, attempt to add the type representation to the table. - if (!comp->typeReprs.add(p, this)) { + if (!comp->typeReprs.relookupOrAdd(p, this, this)) { js_ReportOutOfMemory(cx); js_free(this); // do not finalize, not present in the table return nullptr; diff --git a/js/src/jit-test/tests/gc/bug-948423.js b/js/src/jit-test/tests/gc/bug-948423.js new file mode 100644 index 000000000000..d9470c7d2b5f --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-948423.js @@ -0,0 +1,20 @@ +var ArrayType = TypedObject.ArrayType; +var StructType = TypedObject.StructType; +var uint8 = TypedObject.uint8; +var uint32 = TypedObject.uint32; +var ObjectType = TypedObject.Object; +function runTests() { + (function DimensionLinkedToUndimension() { + var UintsA = uint32.array(); + var FiveUintsA = UintsA.dimension(5); + var FiveUintsB = uint32.array(5); + assertEq(true, + FiveUintsA.equivalent(FiveUintsB) + ); + })(); + (function PrototypeHierarchy() { + schedulegc(3); + var Uint8s = uint8.array(); + })(); +} +runTests(); From 2eeb665fa5ae42fb2e184d1b144fe963cee4c8ed Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Thu, 12 Dec 2013 20:39:19 +0900 Subject: [PATCH 006/459] Bug 948914 - Remove redundant default style for
 element from quirk.css. r=dbaron

---
 layout/style/quirk.css | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/layout/style/quirk.css b/layout/style/quirk.css
index 0665f6cd8129..e658bb70acb0 100644
--- a/layout/style/quirk.css
+++ b/layout/style/quirk.css
@@ -161,13 +161,6 @@ td > ol:-moz-only-whitespace:-moz-last-node, th > ol:-moz-only-whitespace:-moz-l
 }
 
 
-/* Quirk: support the ways of making PRE have wrapping */
-
-pre[wrap], pre[cols], pre[width] {
-  white-space: pre-wrap;
-}
-
-
 /* Quirk: DD not in DL has text-indent instead of margin (b=5119) */
 
 :not(dl) > dd {

From 182a8c2ebe79eb4e609819dfe0c14b01871cd8bc Mon Sep 17 00:00:00 2001
From: Robert O'Callahan 
Date: Fri, 13 Dec 2013 01:33:02 +1300
Subject: [PATCH 007/459] Bug 945634. The fixed-pos anchor point should be in
 the layer's coordinate system, whose 0,0 is the top-left of the fixed-pos
 frame. r=mattwoodrow

---
 gfx/layers/Layers.cpp             | 3 ++-
 layout/base/FrameLayerBuilder.cpp | 3 +--
 layout/base/nsDisplayList.cpp     | 5 ++---
 layout/base/nsLayoutUtils.cpp     | 3 +--
 layout/base/nsLayoutUtils.h       | 1 -
 5 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp
index 1aa0a3b90e17..541226feeeda 100644
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -1276,7 +1276,8 @@ Layer::PrintInfo(nsACString& aTo, const char* aPrefix)
     }
   }
   if (GetIsFixedPosition()) {
-    aTo.AppendPrintf(" [isFixedPosition anchor=%f,%f]", mAnchor.x, mAnchor.y);
+    aTo.AppendPrintf(" [isFixedPosition anchor=%f,%f margin=%f,%f,%f,%f]", mAnchor.x, mAnchor.y,
+                     mMargins.top, mMargins.right, mMargins.bottom, mMargins.left);
   }
   if (GetIsStickyPosition()) {
     aTo.AppendPrintf(" [isStickyPosition scrollId=%d outer=%f,%f %fx%f "
diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp
index 9bb95ac0a123..3c4d96df5782 100644
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1674,8 +1674,7 @@ ContainerState::SetFixedPositionLayerData(Layer* aLayer,
   }
 
   nsLayoutUtils::SetFixedPositionLayerData(aLayer,
-      viewportFrame, viewportSize, aFixedPosFrame, mContainerReferenceFrame,
-      presContext, mParameters);
+      viewportFrame, viewportSize, aFixedPosFrame, presContext, mParameters);
 }
 
 void
diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp
index b5a1495957b8..ae5ce6fdbeef 100644
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -3363,9 +3363,8 @@ nsDisplayStickyPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
   }
 
   nsLayoutUtils::SetFixedPositionLayerData(layer, scrollFrame, scrollFrameSize,
-                                           mStickyPosFrame, ReferenceFrame(),
-                                           presContext,
-                                           aContainerParameters);
+                                           mStickyPosFrame,
+                                           presContext, aContainerParameters);
 
   ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(
     stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent());
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
index 208dfb981906..aef7bb43b61a 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1204,14 +1204,13 @@ nsLayoutUtils::SetFixedPositionLayerData(Layer* aLayer,
                                          const nsIFrame* aViewportFrame,
                                          nsSize aViewportSize,
                                          const nsIFrame* aFixedPosFrame,
-                                         const nsIFrame* aReferenceFrame,
                                          nsPresContext* aPresContext,
                                          const ContainerLayerParameters& aContainerParameters) {
   // Find out the rect of the viewport frame relative to the reference frame.
   // This, in conjunction with the container scale, will correspond to the
   // coordinate-space of the built layer.
   float factor = aPresContext->AppUnitsPerDevPixel();
-  nsPoint origin = aViewportFrame->GetOffsetToCrossDoc(aReferenceFrame);
+  nsPoint origin = aViewportFrame->GetOffsetToCrossDoc(aFixedPosFrame);
   LayerRect anchorRect(NSAppUnitsToFloatPixels(origin.x, factor) *
                          aContainerParameters.mXScale,
                        NSAppUnitsToFloatPixels(origin.y, factor) *
diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h
index aa036d88604c..4224503b9c38 100644
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -378,7 +378,6 @@ public:
   static void SetFixedPositionLayerData(Layer* aLayer, const nsIFrame* aViewportFrame,
                                         nsSize aViewportSize,
                                         const nsIFrame* aFixedPosFrame,
-                                        const nsIFrame* aReferenceFrame,
                                         nsPresContext* aPresContext,
                                         const ContainerLayerParameters& aContainerParameters);
 

From ef518dc5119735c0628c5a75d82eae6c7ad2f72d Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Thu, 12 Dec 2013 08:25:29 -0500
Subject: [PATCH 008/459] Bug 948777 - Build netwerk/sctp/src in unified mode;
 r=jesup

---
 netwerk/sctp/src/moz.build | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/netwerk/sctp/src/moz.build b/netwerk/sctp/src/moz.build
index 1d7f90da74de..b4186410660e 100644
--- a/netwerk/sctp/src/moz.build
+++ b/netwerk/sctp/src/moz.build
@@ -10,7 +10,7 @@ EXPORTS.mozilla.net += [
     'usrsctp.h',
 ]
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'netinet/sctp_asconf.c',
     'netinet/sctp_auth.c',
     'netinet/sctp_bsd_addr.c',
@@ -20,14 +20,12 @@ SOURCES += [
     'netinet/sctp_hashdriver.c',
     'netinet/sctp_indata.c',
     'netinet/sctp_input.c',
-    'netinet/sctp_output.c',
     'netinet/sctp_pcb.c',
     'netinet/sctp_peeloff.c',
     'netinet/sctp_sha1.c',
     'netinet/sctp_ss_functions.c',
     'netinet/sctp_sysctl.c',
     'netinet/sctp_timer.c',
-    'netinet/sctp_userspace.c',
     'netinet/sctp_usrreq.c',
     'netinet/sctputil.c',
     'netinet6/sctp6_usrreq.c',
@@ -35,9 +33,19 @@ SOURCES += [
     'user_mbuf.c',
     'user_recv_thread.c',
     'user_sctp_timer_iterate.c',
+]
+
+# These files cannot be built in unified mode because they rely on __FAVOR_BSD in udp.h.
+SOURCES += [
+    'netinet/sctp_output.c',
     'user_socket.c',
 ]
 
+# This file cannot be built in unified mode because of compilation failures on Windows.
+SOURCES += [
+    'netinet/sctp_userspace.c',
+]
+
 if CONFIG['OS_TARGET'] == 'Android':
     SOURCES += [
         'ifaddrs_android.cpp',

From 8b72ea8324379dc6b40e39522558817e8afaba96 Mon Sep 17 00:00:00 2001
From: Chris Peterson 
Date: Tue, 10 Dec 2013 22:58:51 -0800
Subject: [PATCH 009/459] Bug 949360 - Check for Image AllocateBuffer()
 failures. r=nical

---
 gfx/layers/ImageContainer.cpp             | 16 ++++++++++------
 gfx/layers/ipc/SharedPlanarYCbCrImage.cpp | 20 ++++++++++++--------
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp
index 8d51ce7c6944..e977f69143be 100644
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -490,14 +490,17 @@ PlanarYCbCrImage::CopyData(const Data& aData)
   mData = aData;
 
   // update buffer size
-  mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
+  size_t size = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
                 mData.mYStride * mData.mYSize.height;
 
   // get new buffer
-  mBuffer = AllocateBuffer(mBufferSize);
+  mBuffer = AllocateBuffer(size);
   if (!mBuffer)
     return;
 
+  // update buffer size
+  mBufferSize = size;
+
   mData.mYChannel = mBuffer;
   mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
   mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
@@ -536,11 +539,12 @@ PlanarYCbCrImage::SetDataNoCopy(const Data &aData)
 uint8_t*
 PlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
 {
-  // update buffer size
-  mBufferSize = aSize;
-
   // get new buffer
-  mBuffer = AllocateBuffer(mBufferSize); 
+  mBuffer = AllocateBuffer(aSize);
+  if (mBuffer) {
+    // update buffer size
+    mBufferSize = aSize;
+  }
   return mBuffer;
 }
 
diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
index a842f5d7ee00..3f7e969a1cf6 100644
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -126,14 +126,16 @@ SharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
 {
   NS_ABORT_IF_FALSE(!mTextureClient->IsAllocated(), "This image already has allocated data");
   size_t size = YCbCrImageDataSerializer::ComputeMinBufferSize(aSize);
+
+  // get new buffer _without_ setting mBuffer.
+  if (!mTextureClient->Allocate(size)) {
+    return nullptr;
+  }
+
   // update buffer size
   mBufferSize = size;
 
-  // get new buffer _without_ setting mBuffer.
-  bool status = mTextureClient->Allocate(mBufferSize);
-  MOZ_ASSERT(status);
   YCbCrImageDataSerializer serializer(mTextureClient->GetBuffer());
-
   return serializer.GetData();
 }
 
@@ -248,17 +250,19 @@ DeprecatedSharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
 {
   NS_ABORT_IF_FALSE(!mAllocated, "This image already has allocated data");
   size_t size = YCbCrImageDataSerializer::ComputeMinBufferSize(aSize);
+
+  // get new buffer _without_ setting mBuffer.
+  if (!AllocateBuffer(size)) {
+    return nullptr;
+  }
+
   // update buffer size
   mBufferSize = size;
 
-  // get new buffer _without_ setting mBuffer.
-  AllocateBuffer(mBufferSize);
   YCbCrImageDataSerializer serializer(mShmem.get());
-
   return serializer.GetData();
 }
 
-
 void
 DeprecatedSharedPlanarYCbCrImage::SetDataNoCopy(const Data &aData)
 {

From d36297d1a529925f2155e167179de9576e6fdd20 Mon Sep 17 00:00:00 2001
From: Hannes Verschore 
Date: Thu, 12 Dec 2013 15:14:12 +0100
Subject: [PATCH 010/459] Bug 939614: IonMonkey: Add recompile check, r=jandem

---
 js/src/jit/CodeGenerator.cpp          | 19 ++++++++++++++++
 js/src/jit/CodeGenerator.h            |  2 ++
 js/src/jit/IonBuilder.cpp             | 14 ++++++++++++
 js/src/jit/IonBuilder.h               |  2 ++
 js/src/jit/LIR-Common.h               | 17 ++++++++++++++
 js/src/jit/LOpcodes.h                 |  1 +
 js/src/jit/Lowering.cpp               |  9 ++++++++
 js/src/jit/Lowering.h                 |  1 +
 js/src/jit/MIR.h                      | 32 +++++++++++++++++++++++++++
 js/src/jit/MOpcodes.h                 |  3 ++-
 js/src/jit/ParallelSafetyAnalysis.cpp |  1 +
 11 files changed, 100 insertions(+), 1 deletion(-)

diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 7cbe48d16495..5a6bff5dab95 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7928,5 +7928,24 @@ CodeGenerator::visitAssertRangeV(LAssertRangeV *ins)
     return true;
 }
 
+typedef bool (*RecompileFn)(JSContext *);
+static const VMFunction RecompileFnInfo = FunctionInfo(Recompile);
+
+bool
+CodeGenerator::visitRecompileCheck(LRecompileCheck *ins)
+{
+    Register useCount = ToRegister(ins->scratch());
+
+    masm.movePtr(ImmPtr(ins->mir()->script()->addressOfUseCount()), useCount);
+    Address ptr(useCount, 0);
+    masm.add32(Imm32(1), ptr);
+
+    OutOfLineCode *ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(useCount));
+    masm.branch32(Assembler::Above, ptr, Imm32(ins->mir()->useCount()), ool->entry());
+    masm.bind(ool->rejoin());
+
+    return true;
+}
+
 } // namespace jit
 } // namespace js
diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h
index e522f27da234..5a42247fd604 100644
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -336,6 +336,8 @@ class CodeGenerator : public CodeGeneratorSpecific
     bool visitAssertRangeF(LAssertRangeF *ins);
     bool visitAssertRangeV(LAssertRangeV *ins);
 
+    bool visitRecompileCheck(LRecompileCheck *ins);
+
     IonScriptCounts *extractUnassociatedScriptCounts() {
         IonScriptCounts *counts = unassociatedScriptCounts_;
         unassociatedScriptCounts_ = nullptr;  // prevent delete in dtor
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 28b2285a8fdc..c45572433eb7 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -691,6 +691,8 @@ IonBuilder::build()
         current->add(lazyArguments_);
     }
 
+    insertRecompileCheck();
+
     if (!traverseBytecode())
         return false;
 
@@ -848,6 +850,8 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
         current->add(lazyArguments_);
     }
 
+    insertRecompileCheck();
+
     if (!traverseBytecode())
         return false;
 
@@ -3339,6 +3343,7 @@ IonBuilder::jsop_loophead(jsbytecode *pc)
     assertValidLoopHeadOp(pc);
 
     current->add(MInterruptCheck::New(alloc()));
+    insertRecompileCheck();
 
     return true;
 }
@@ -5991,6 +5996,15 @@ ClassHasResolveHook(CompileCompartment *comp, const Class *clasp, PropertyName *
     return true;
 }
 
+void
+IonBuilder::insertRecompileCheck()
+{
+    if (info().executionMode() != SequentialExecution)
+        return;
+
+    return;
+}
+
 JSObject *
 IonBuilder::testSingletonProperty(JSObject *obj, PropertyName *name)
 {
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index 2404e147d2df..a844910be45a 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -325,6 +325,8 @@ class IonBuilder : public MIRGenerator
     bool resumeAfter(MInstruction *ins);
     bool maybeInsertResume();
 
+    void insertRecompileCheck();
+
     bool initParameters();
     void rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex);
     void rewriteParameters();
diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h
index 13c803502d6c..7ed29e1ac609 100644
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -5768,6 +5768,23 @@ class LAssertRangeV : public LInstructionHelper<0, BOX_PIECES, 3>
     }
 };
 
+class LRecompileCheck : public LInstructionHelper<0, 0, 1>
+{
+  public:
+    LIR_HEADER(RecompileCheck)
+
+    LRecompileCheck(const LDefinition &scratch) {
+        setTemp(0, scratch);
+    }
+
+    const LDefinition *scratch() {
+        return getTemp(0);
+    }
+    MRecompileCheck *mir() {
+        return mir_->toRecompileCheck();
+    }
+};
+
 } // namespace jit
 } // namespace js
 
diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h
index 44d9db360964..2b13b1ad1f66 100644
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -280,6 +280,7 @@
     _(AsmJSCall)                    \
     _(AsmJSCheckOverRecursed)       \
     _(CheckInterruptPar)            \
+    _(RecompileCheck)               \
     _(AssertRangeI)                 \
     _(AssertRangeD)                 \
     _(AssertRangeF)                 \
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index 3209d31927ff..6e2800c04241 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3421,6 +3421,15 @@ LIRGenerator::visitGetDOMMember(MGetDOMMember *ins)
     return defineBox(lir, ins);
 }
 
+bool
+LIRGenerator::visitRecompileCheck(MRecompileCheck *ins)
+{
+    LRecompileCheck *lir = new(alloc()) LRecompileCheck(temp());
+    if (!add(lir, ins))
+        return false;
+    return assignSafepoint(lir, ins);
+}
+
 static void
 SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint)
 {
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index 8302b9f16d68..c2a86f313d4f 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -261,6 +261,7 @@ class LIRGenerator : public LIRGeneratorSpecific
     bool visitSetDOMProperty(MSetDOMProperty *ins);
     bool visitGetDOMProperty(MGetDOMProperty *ins);
     bool visitGetDOMMember(MGetDOMMember *ins);
+    bool visitRecompileCheck(MRecompileCheck *ins);
 };
 
 } // namespace jit
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 1cf9adfa4365..cd4cba1d0c7e 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9204,6 +9204,38 @@ class MHaveSameClass
     }
 };
 
+class MRecompileCheck : public MNullaryInstruction
+{
+    JSScript *script_;
+    uint32_t recompileThreshold_;
+
+    MRecompileCheck(JSScript *script_, uint32_t recompileThreshold)
+      : script_(script_),
+        recompileThreshold_(recompileThreshold)
+    {
+        setGuard();
+    }
+
+  public:
+    INSTRUCTION_HEADER(RecompileCheck);
+
+    static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_, uint32_t useCount) {
+        return new(alloc) MRecompileCheck(script_, useCount);
+    }
+
+    JSScript *script() const {
+        return script_;
+    }
+
+    uint32_t recompileThreshold() const {
+        return recompileThreshold_;
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+};
+
 class MAsmJSNeg : public MUnaryInstruction
 {
     MAsmJSNeg(MDefinition *op, MIRType type)
diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h
index 12cee893cefa..08b622ba6395 100644
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -211,7 +211,8 @@ namespace jit {
     _(RestPar)                                                              \
     _(ForkJoinSlice)                                                        \
     _(GuardThreadLocalObject)                                               \
-    _(CheckInterruptPar)
+    _(CheckInterruptPar)                                                    \
+    _(RecompileCheck)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp
index 446e0f159f6c..a4d1e76bb6eb 100644
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -311,6 +311,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
     UNSAFE_OP(AsmJSParameter)
     UNSAFE_OP(AsmJSCall)
     UNSAFE_OP(AsmJSCheckOverRecursed)
+    DROP_OP(RecompileCheck)
 
     // It looks like this could easily be made safe:
     UNSAFE_OP(ConvertElementsToDoubles)

From 35bb78bfb0f5c06b4ef0c61ed7d040fcacc46f8c Mon Sep 17 00:00:00 2001
From: Hannes Verschore 
Date: Thu, 12 Dec 2013 15:14:12 +0100
Subject: [PATCH 011/459] Bug 939614: IonMonkey: Split IonOptions in IonOptions
 and IonOptimizations, r=jandem

---
 js/src/jit/AsmJS.cpp                      |   4 +-
 js/src/jit/BaselineCompiler.cpp           |   2 +-
 js/src/jit/CodeGenerator.cpp              |   7 +-
 js/src/jit/Ion.cpp                        | 119 +++++++----
 js/src/jit/Ion.h                          | 241 +---------------------
 js/src/jit/IonAnalysis.cpp                |   5 +-
 js/src/jit/IonBuilder.cpp                 |  69 ++++---
 js/src/jit/IonBuilder.h                   |  12 +-
 js/src/jit/IonCode.h                      |  10 +-
 js/src/jit/IonOptimizationLevels.cpp      | 110 ++++++++++
 js/src/jit/IonOptimizationLevels.h        | 203 ++++++++++++++++++
 js/src/jit/JitOptions.cpp                 | 160 ++++++++++++++
 js/src/jit/JitOptions.h                   |  76 +++++++
 js/src/jit/Lowering.cpp                   |   2 +-
 js/src/jit/MCallOptimize.cpp              |   3 +
 js/src/jit/MIR.h                          |   7 +-
 js/src/jit/MIRGenerator.h                 |   8 +-
 js/src/jit/MIRGraph.cpp                   |   4 +-
 js/src/jit/UnreachableCodeElimination.cpp |   4 +-
 js/src/jit/shared/Lowering-shared-inl.h   |   4 +-
 js/src/jsapi.cpp                          |  13 +-
 js/src/jsscript.h                         |   6 +-
 js/src/jsworkers.cpp                      |   1 -
 js/src/moz.build                          |   2 +
 js/src/shell/js.cpp                       |  53 ++---
 25 files changed, 762 insertions(+), 363 deletions(-)
 create mode 100644 js/src/jit/IonOptimizationLevels.cpp
 create mode 100644 js/src/jit/IonOptimizationLevels.h
 create mode 100644 js/src/jit/JitOptions.cpp
 create mode 100644 js/src/jit/JitOptions.h

diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp
index f7e70f9c1109..c58c82ea132b 100644
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1936,7 +1936,9 @@ class FunctionCompiler
 
         graph_  = lifo_.new_(alloc_);
         info_   = lifo_.new_(locals_.count(), SequentialExecution);
-        mirGen_ = lifo_.new_(CompileCompartment::get(cx()->compartment()), alloc_, graph_, info_);
+        const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS);
+        mirGen_ = lifo_.new_(CompileCompartment::get(cx()->compartment()), alloc_,
+                                           graph_, info_, optimizationInfo);
 
         if (!newBlock(/* pred = */ nullptr, &curBlock_, fn_))
             return false;
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index febad931dae9..3e78fff94a01 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -637,7 +637,7 @@ BaselineCompiler::emitUseCountIncrement()
 
     Label skipCall;
 
-    uint32_t minUses = UsesBeforeIonRecompile(script, pc);
+    uint32_t minUses = UsesBeforeIonCompile(script, pc);
     masm.branch32(Assembler::LessThan, countReg, Imm32(minUses), &skipCall);
 
     masm.branchPtr(Assembler::Equal,
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 5a6bff5dab95..cc1095ff392b 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -22,6 +22,7 @@
 #include "jit/ExecutionModeInlines.h"
 #include "jit/IonCaches.h"
 #include "jit/IonLinker.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/IonSpewer.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
@@ -5857,7 +5858,9 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
 {
     RootedScript script(cx, gen->info().script());
     ExecutionMode executionMode = gen->info().executionMode();
-    JS_ASSERT(!HasIonScript(script, executionMode));
+    OptimizationLevel optimizationLevel = gen->optimizationInfo().level();
+
+    JS_ASSERT_IF(HasIonScript(script, executionMode), executionMode == SequentialExecution);
 
     // Check to make sure we didn't have a mid-build invalidation. If so, we
     // will trickle to jit::Compile() and return Method_Skipped.
@@ -5885,7 +5888,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), runtimeData_.length(),
                      safepoints_.size(), callTargets.length(),
-                     patchableBackedges_.length());
+                     patchableBackedges_.length(), optimizationLevel);
     if (!ionScript) {
         recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
         return false;
diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp
index ba3ee46a154d..a13f7bac1eff 100644
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -27,6 +27,7 @@
 #include "jit/ExecutionModeInlines.h"
 #include "jit/IonAnalysis.h"
 #include "jit/IonBuilder.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/IonSpewer.h"
 #include "jit/JitCompartment.h"
 #include "jit/LICM.h"
@@ -49,9 +50,6 @@
 using namespace js;
 using namespace js::jit;
 
-// Global variables.
-IonOptions jit::js_IonOptions;
-
 // Assert that IonCode is gc::Cell aligned.
 JS_STATIC_ASSERT(sizeof(IonCode) % gc::CellSize == 0);
 
@@ -761,7 +759,8 @@ IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo,
                uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize,
                size_t bailoutEntries, size_t constants, size_t safepointIndices,
                size_t osiIndices, size_t cacheEntries, size_t runtimeSize,
-               size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries)
+               size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries,
+               OptimizationLevel optimizationLevel)
 {
     static const int DataAlignment = sizeof(void *);
 
@@ -848,6 +847,7 @@ IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo,
     script->frameSize_ = frameSize;
 
     script->recompileInfo_ = recompileInfo;
+    script->optimizationLevel_ = optimizationLevel;
 
     return script;
 }
@@ -1233,7 +1233,9 @@ OptimizeMIR(MIRGenerator *mir)
 
     // Alias analysis is required for LICM and GVN so that we don't move
     // loads across stores.
-    if (js_IonOptions.licm || js_IonOptions.gvn) {
+    if (mir->optimizationInfo().licmEnabled() ||
+        mir->optimizationInfo().gvnEnabled())
+    {
         AliasAnalysis analysis(mir, graph);
         if (!analysis.analyze())
             return false;
@@ -1253,8 +1255,8 @@ OptimizeMIR(MIRGenerator *mir)
             return false;
     }
 
-    if (js_IonOptions.gvn) {
-        ValueNumberer gvn(mir, graph, js_IonOptions.gvnIsOptimistic);
+    if (mir->optimizationInfo().gvnEnabled()) {
+        ValueNumberer gvn(mir, graph, mir->optimizationInfo().gvnKind() == GVN_Optimistic);
         if (!gvn.analyze())
             return false;
         IonSpewPass("GVN");
@@ -1264,7 +1266,7 @@ OptimizeMIR(MIRGenerator *mir)
             return false;
     }
 
-    if (js_IonOptions.uce) {
+    if (mir->optimizationInfo().uceEnabled()) {
         UnreachableCodeElimination uce(mir, graph);
         if (!uce.analyze())
             return false;
@@ -1275,7 +1277,7 @@ OptimizeMIR(MIRGenerator *mir)
     if (mir->shouldCancel("UCE"))
         return false;
 
-    if (js_IonOptions.licm) {
+    if (mir->optimizationInfo().licmEnabled()) {
         // LICM can hoist instructions from conditional branches and trigger
         // repeated bailouts. Disable it if this script is known to bailout
         // frequently.
@@ -1292,7 +1294,7 @@ OptimizeMIR(MIRGenerator *mir)
         }
     }
 
-    if (js_IonOptions.rangeAnalysis) {
+    if (mir->optimizationInfo().rangeAnalysisEnabled()) {
         RangeAnalysis r(mir, graph);
         if (!r.addBetaNodes())
             return false;
@@ -1318,7 +1320,7 @@ OptimizeMIR(MIRGenerator *mir)
         if (mir->shouldCancel("RA De-Beta"))
             return false;
 
-        if (js_IonOptions.uce) {
+        if (mir->optimizationInfo().uceEnabled()) {
             bool shouldRunUCE = false;
             if (!r.prepareForUCE(&shouldRunUCE))
                 return false;
@@ -1350,7 +1352,7 @@ OptimizeMIR(MIRGenerator *mir)
             return false;
     }
 
-    if (js_IonOptions.eaa) {
+    if (mir->optimizationInfo().eaaEnabled()) {
         EffectiveAddressAnalysis eaa(graph);
         if (!eaa.analyze())
             return false;
@@ -1372,7 +1374,7 @@ OptimizeMIR(MIRGenerator *mir)
     // Passes after this point must not move instructions; these analyses
     // depend on knowing the final order in which instructions will execute.
 
-    if (js_IonOptions.edgeCaseAnalysis && !mir->compilingAsmJS()) {
+    if (mir->optimizationInfo().edgeCaseAnalysisEnabled()) {
         EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
         if (!edgeCaseAnalysis.analyzeLate())
             return false;
@@ -1383,7 +1385,7 @@ OptimizeMIR(MIRGenerator *mir)
             return false;
     }
 
-    if (!mir->compilingAsmJS()) {
+    if (mir->optimizationInfo().eliminateRedundantChecksEnabled()) {
         // Note: check elimination has to run after all other passes that move
         // instructions. Since check uses are replaced with the actual index,
         // code motion after this pass could incorrectly move a load or store
@@ -1416,7 +1418,7 @@ GenerateLIR(MIRGenerator *mir)
 
     AllocationIntegrityState integrity(*lir);
 
-    switch (js_IonOptions.registerAllocator) {
+    switch (mir->optimizationInfo().registerAllocator()) {
       case RegisterAllocator_LSRA: {
 #ifdef DEBUG
         if (!integrity.record())
@@ -1628,7 +1630,8 @@ TrackPropertiesForSingletonScopes(JSContext *cx, JSScript *script, BaselineFrame
 static AbortReason
 IonCompile(JSContext *cx, JSScript *script,
            BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing,
-           ExecutionMode executionMode, bool recompile)
+           ExecutionMode executionMode, bool recompile,
+           OptimizationLevel optimizationLevel)
 {
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
@@ -1636,6 +1639,8 @@ IonCompile(JSContext *cx, JSScript *script,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
+    JS_ASSERT(optimizationLevel > Optimization_DontCompile);
+
     TrackPropertiesForSingletonScopes(cx, script, baselineFrame);
 
     LifoAlloc *alloc = cx->new_(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
@@ -1683,10 +1688,13 @@ IonCompile(JSContext *cx, JSScript *script,
     if (!constraints)
         return AbortReason_Alloc;
 
+    const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(optimizationLevel);
+
     IonBuilder *builder = alloc->new_((JSContext *) nullptr,
                                                   CompileCompartment::get(cx->compartment()),
                                                   temp, graph, constraints,
-                                                  &inspector, info, baselineFrameInspector);
+                                                  &inspector, info, optimizationInfo,
+                                                  baselineFrameInspector);
     if (!builder)
         return AbortReason_Alloc;
 
@@ -1867,6 +1875,17 @@ CanIonCompileScript(JSContext *cx, HandleScript script, bool osr)
     return CheckScriptSize(cx, script) == Method_Compiled;
 }
 
+static OptimizationLevel
+GetOptimizationLevel(HandleScript script, ExecutionMode executionMode)
+{
+    if (executionMode == ParallelExecution)
+        return Optimization_Normal;
+
+    JS_ASSERT(executionMode == SequentialExecution);
+
+    return js_IonOptimizations.levelForUseCount(script->getUseCount());
+}
+
 static MethodStatus
 Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
         bool constructing, ExecutionMode executionMode)
@@ -1874,6 +1893,8 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
     JS_ASSERT(jit::IsIonEnabled(cx));
     JS_ASSERT(jit::IsBaselineEnabled(cx));
     JS_ASSERT_IF(osrPc != nullptr, (JSOp)*osrPc == JSOP_LOOPENTRY);
+    JS_ASSERT_IF(executionMode == ParallelExecution, !osrFrame && !osrPC);
+    JS_ASSERT_IF(executionMode == ParallelExecution, !HasIonScript(script, executionMode));
 
     if (!script->hasBaselineScript())
         return Method_Skipped;
@@ -1895,23 +1916,44 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
     }
 
     bool recompile = false;
+    OptimizationLevel optimizationLevel = GetOptimizationLevel(script, executionMode);
+    if (optimizationLevel == Optimization_DontCompile)
+        return Method_Skipped;
 
     IonScript *scriptIon = GetIonScript(script, executionMode);
     if (scriptIon) {
         if (!scriptIon->method())
             return Method_CantCompile;
-        return Method_Compiled;
-    }
 
-    if (executionMode == SequentialExecution) {
-        // Use getUseCount instead of incUseCount to avoid bumping the
-        // use count twice.
-        if (script->getUseCount() < UsesBeforeIonRecompile(script, osrPc ? osrPc : script->code()))
-            return Method_Skipped;
+        MethodStatus failedState = Method_Compiled;
+
+        // If we keep failing to enter the script due to an OSR pc mismatch,
+        // recompile with the right pc.
+        if (osrPc && script->ionScript()->osrPc() != osrPc) {
+            uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
+            if (count <= js_IonOptions.osrPcMismatchesBeforeRecompile)
+                return Method_Skipped;
+
+            failedState = Method_Skipped;
+        }
+
+        // Don't recompile/overwrite higher optimized code,
+        // with a lower optimization level.
+        if (optimizationLevel <= scriptIon->optimizationLevel())
+            return failedState;
+
+        // Don't start compiling if already compiling
+        if (scriptIon->isRecompiling())
+            return failedState;
+
+        if (osrPc)
+            script->ionScript()->resetOsrPcMismatchCounter();
+
+        recompile = true;
     }
 
     AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode,
-                                    recompile);
+                                    recompile, optimizationLevel);
     if (reason == AbortReason_Error)
         return Method_Error;
 
@@ -1961,7 +2003,11 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
         return Method_CantCompile;
     }
 
-    // Attempt compilation. Returns Method_Compiled if already compiled.
+    // Attempt compilation.
+    // - Returns Method_Compiled if the right ionscript is present
+    //   (Meaning it was present or a sequantial compile finished)
+    // - Returns Method_Skipped if pc doesn't match
+    //   (This means a background thread compilation with that pc could have started or not.)
     RootedScript rscript(cx, script);
     MethodStatus status = Compile(cx, rscript, osrFrame, pc, isConstructing, SequentialExecution);
     if (status != Method_Compiled) {
@@ -1970,20 +2016,6 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
         return status;
     }
 
-    if (script->ionScript()->osrPc() != pc) {
-        // If we keep failing to enter the script due to an OSR pc mismatch,
-        // invalidate the script to force a recompile.
-        uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
-
-        if (count > js_IonOptions.osrPcMismatchesBeforeRecompile) {
-            if (!Invalidate(cx, script, SequentialExecution, true))
-                return Method_Error;
-        }
-        return Method_Skipped;
-    }
-
-    script->ionScript()->resetOsrPcMismatchCounter();
-
     return Method_Compiled;
 }
 
@@ -2699,11 +2731,14 @@ jit::ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode)
 }
 
 uint32_t
-jit::UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc)
+jit::UsesBeforeIonCompile(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY);
 
-    uint32_t minUses = js_IonOptions.usesBeforeCompile;
+    OptimizationLevel level = js_IonOptimizations.nextLevel(Optimization_DontCompile);
+    const OptimizationInfo *info = js_IonOptimizations.get(level);
+
+    uint32_t minUses = info->usesBeforeCompile();
 
     // If the script is too large to compile on the main thread, we can still
     // compile it off thread. In these cases, increase the use count threshold
diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h
index 37ad4e778387..3052a540bab8 100644
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -16,248 +16,13 @@
 
 #include "jit/CompileInfo.h"
 #include "jit/CompileWrappers.h"
+#include "jit/IonOptions.h"
 
 namespace js {
 namespace jit {
 
 class TempAllocator;
 
-// Possible register allocators which may be used.
-enum IonRegisterAllocator {
-    RegisterAllocator_LSRA,
-    RegisterAllocator_Backtracking,
-    RegisterAllocator_Stupid
-};
-
-struct IonOptions
-{
-    // Toggles whether global value numbering is used.
-    //
-    // Default: true
-    bool gvn;
-
-    // Toggles whether global value numbering is optimistic (true) or
-    // pessimistic (false).
-    //
-    // Default: true
-    bool gvnIsOptimistic;
-
-    // Toggles whether loop invariant code motion is performed.
-    //
-    // Default: true
-    bool licm;
-
-    // Toggles whether functions may be entered at loop headers.
-    //
-    // Default: true
-    bool osr;
-
-    // Toggles whether large scripts are rejected.
-    //
-    // Default: true
-    bool limitScriptSize;
-
-    // Describes which register allocator to use.
-    //
-    // Default: LSRA
-    IonRegisterAllocator registerAllocator;
-
-    // Toggles whether inlining is performed.
-    //
-    // Default: true
-    bool inlining;
-
-    // Toggles whether Edge Case Analysis is used.
-    //
-    // Default: true
-    bool edgeCaseAnalysis;
-
-    // Toggles whether Range Analysis is used.
-    //
-    // Default: true
-    bool rangeAnalysis;
-
-    // Whether to enable extra code to perform dynamic validation of
-    // RangeAnalysis results.
-    //
-    // Default: false
-    bool checkRangeAnalysis;
-
-    // Whether to protect the GC heap during Ion compilation and ensure that
-    // only threadsafe operations are performed on it.
-    //
-    // Default: false
-    bool checkThreadSafety;
-
-    // Whether to perform expensive graph-consistency DEBUG-only assertions.
-    // It can be useful to disable this to reduce DEBUG-compile time of large
-    // asm.js programs.
-    //
-    // Default: true
-    bool checkGraphConsistency;
-
-    // Toggles whether Unreachable Code Elimination is performed.
-    //
-    // Default: true
-    bool uce;
-
-    // Toggles whether Effective Address Analysis is performed.
-    //
-    // Default: true
-    bool eaa;
-
-#ifdef CHECK_OSIPOINT_REGISTERS
-    // Emit extra code to verify live regs at the start of a VM call
-    // are not modified before its OsiPoint.
-    //
-    // Default: false
-    bool checkOsiPointRegisters;
-#endif
-
-    // How many invocations or loop iterations are needed before functions
-    // are compiled with the baseline compiler.
-    //
-    // Default: 10
-    uint32_t baselineUsesBeforeCompile;
-
-    // How many invocations or loop iterations are needed before functions
-    // are compiled.
-    //
-    // Default: 1,000
-    uint32_t usesBeforeCompile;
-
-    // How many invocations or loop iterations are needed before calls
-    // are inlined, as a fraction of usesBeforeCompile.
-    //
-    // Default: .125
-    double usesBeforeInliningFactor;
-
-    // How many times we will try to enter a script via OSR before
-    // invalidating the script.
-    //
-    // Default: 6,000
-    uint32_t osrPcMismatchesBeforeRecompile;
-
-    // Number of bailouts without invalidation before we set
-    // JSScript::hadFrequentBailouts and invalidate.
-    //
-    // Default: 10
-    uint32_t frequentBailoutThreshold;
-
-    // Number of exception bailouts (resuming into catch/finally block) before
-    // we invalidate and forbid Ion compilation.
-    //
-    // Default: 10
-    uint32_t exceptionBailoutThreshold;
-
-    // Whether Ion should compile try-catch statements.
-    //
-    // Default: true
-    bool compileTryCatch;
-
-    // How many actual arguments are accepted on the C stack.
-    //
-    // Default: 4,096
-    uint32_t maxStackArgs;
-
-    // The maximum inlining depth.
-    //
-    // Default: 3
-    uint32_t maxInlineDepth;
-
-    // The maximum inlining depth for functions.
-    //
-    // Inlining small functions has almost no compiling overhead
-    // and removes the otherwise needed call overhead.
-    // The value is currently very low.
-    // Actually it is only needed to make sure we don't blow out the stack.
-    //
-    // Default: 10
-    uint32_t smallFunctionMaxInlineDepth;
-
-    // The bytecode length limit for small function.
-    //
-    // The default for this was arrived at empirically via benchmarking.
-    // We may want to tune it further after other optimizations have gone
-    // in.
-    //
-    // Default: 100
-    uint32_t smallFunctionMaxBytecodeLength;
-
-    // The maximum number of functions to polymorphically inline at a call site.
-    //
-    // Default: 4
-    uint32_t polyInlineMax;
-
-    // The maximum total bytecode size of an inline call site.
-    //
-    // Default: 1000
-    uint32_t inlineMaxTotalBytecodeLength;
-
-    // Whether functions are compiled immediately.
-    //
-    // Default: false
-    bool eagerCompilation;
-
-    // How many uses of a parallel kernel before we attempt compilation.
-    //
-    // Default: 1
-    uint32_t usesBeforeCompilePar;
-
-    // The maximum bytecode length the caller may have,
-    // before we stop inlining large functions in that caller.
-    //
-    // Default: 10000
-    uint32_t inliningMaxCallerBytecodeLength;
-
-    void setEagerCompilation() {
-        eagerCompilation = true;
-        usesBeforeCompile = 0;
-        baselineUsesBeforeCompile = 0;
-    }
-
-    MOZ_CONSTEXPR IonOptions()
-      : gvn(true),
-        gvnIsOptimistic(true),
-        licm(true),
-        osr(true),
-        limitScriptSize(true),
-        registerAllocator(RegisterAllocator_LSRA),
-        inlining(true),
-        edgeCaseAnalysis(true),
-        rangeAnalysis(true),
-        checkRangeAnalysis(false),
-        checkThreadSafety(false),
-        checkGraphConsistency(true),
-        uce(true),
-        eaa(true),
-#ifdef CHECK_OSIPOINT_REGISTERS
-        checkOsiPointRegisters(false),
-#endif
-        baselineUsesBeforeCompile(10),
-        usesBeforeCompile(1000),
-        usesBeforeInliningFactor(.125),
-        osrPcMismatchesBeforeRecompile(6000),
-        frequentBailoutThreshold(10),
-        exceptionBailoutThreshold(10),
-        compileTryCatch(true),
-        maxStackArgs(4096),
-        maxInlineDepth(3),
-        smallFunctionMaxInlineDepth(10),
-        smallFunctionMaxBytecodeLength(100),
-        polyInlineMax(4),
-        inlineMaxTotalBytecodeLength(1000),
-        eagerCompilation(false),
-        usesBeforeCompilePar(1),
-        inliningMaxCallerBytecodeLength(10000)
-    {
-    }
-
-    uint32_t usesBeforeInlining() {
-        return usesBeforeCompile * usesBeforeInliningFactor;
-    }
-};
-
 enum MethodStatus
 {
     Method_Error,
@@ -308,8 +73,6 @@ class IonContext
     int assemblerCount_;
 };
 
-extern IonOptions js_IonOptions;
-
 // Initialize Ion statically for all JSRuntimes.
 bool InitializeIon();
 
@@ -416,7 +179,7 @@ TooManyArguments(unsigned nargs)
 
 void ForbidCompilation(JSContext *cx, JSScript *script);
 void ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode);
-uint32_t UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc);
+uint32_t UsesBeforeIonCompile(JSScript *script, jsbytecode *pc);
 
 void PurgeCaches(JSScript *script, JS::Zone *zone);
 size_t SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf);
diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp
index 839c3ecb2305..d91b5e49d5b9 100644
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -12,6 +12,7 @@
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonBuilder.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/LIR.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
@@ -2158,10 +2159,12 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
 
     AutoTempAllocatorRooter root(cx, &temp);
 
+    const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_Normal);
+
     types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp);
     BaselineInspector inspector(script);
     IonBuilder builder(cx, CompileCompartment::get(cx->compartment()), &temp, &graph, constraints,
-                       &inspector, &info, /* baselineFrame = */ nullptr);
+                       &inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr);
 
     if (!builder.build()) {
         if (builder.abortReason() == AbortReason_Alloc)
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index c45572433eb7..151c4ab38d22 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -18,6 +18,7 @@
 #include "jit/BaselineInspector.h"
 #include "jit/ExecutionModeInlines.h"
 #include "jit/Ion.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/IonSpewer.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
@@ -101,11 +102,13 @@ jit::NewBaselineFrameInspector(TempAllocator *temp, BaselineFrame *frame)
     return inspector;
 }
 
-IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, MIRGraph *graph,
-                       types::CompilerConstraintList *constraints,
-                       BaselineInspector *inspector, CompileInfo *info, BaselineFrameInspector *baselineFrame,
-                       size_t inliningDepth, uint32_t loopDepth)
-  : MIRGenerator(comp, temp, graph, info),
+IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp,
+                       MIRGraph *graph, types::CompilerConstraintList *constraints,
+                       BaselineInspector *inspector, CompileInfo *info,
+                       const OptimizationInfo *optimizationInfo,
+                       BaselineFrameInspector *baselineFrame, size_t inliningDepth,
+                       uint32_t loopDepth)
+  : MIRGenerator(comp, temp, graph, info, optimizationInfo),
     backgroundCodegen_(nullptr),
     analysisContext(analysisContext),
     baselineFrame_(baselineFrame),
@@ -327,6 +330,9 @@ IonBuilder::DontInline(JSScript *targetScript, const char *reason)
 IonBuilder::InliningDecision
 IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo)
 {
+    if (!optimizationInfo().inlineInterpreted())
+        return InliningDecision_DontInline;
+
     if (!target->isInterpreted())
         return DontInline(nullptr, "Non-interpreted target");
 
@@ -3851,9 +3857,9 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
     AutoAccumulateReturns aar(graph(), returns);
 
     // Build the graph.
-    IonBuilder inlineBuilder(analysisContext, compartment,
-                             &alloc(), &graph(), constraints(), &inspector, info, nullptr,
-                             inliningDepth_ + 1, loopDepth_);
+    IonBuilder inlineBuilder(analysisContext, compartment, &alloc(), &graph(), constraints(),
+                             &inspector, info, &optimizationInfo(), nullptr, inliningDepth_ + 1,
+                             loopDepth_);
     if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) {
         if (analysisContext && analysisContext->isExceptionPending()) {
             IonSpew(IonSpew_Abort, "Inline builder raised exception.");
@@ -3965,19 +3971,9 @@ IonBuilder::patchInlinedReturns(CallInfo &callInfo, MIRGraphReturns &returns, MB
     return phi;
 }
 
-static bool
-IsSmallFunction(JSScript *script)
-{
-    return script->length() <= js_IonOptions.smallFunctionMaxBytecodeLength;
-}
-
 IonBuilder::InliningDecision
 IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
 {
-    // Only inline when inlining is enabled.
-    if (!inliningEnabled())
-        return InliningDecision_DontInline;
-
     // When there is no target, inlining is impossible.
     if (target == nullptr)
         return InliningDecision_DontInline;
@@ -3997,30 +3993,30 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
     // Skip heuristics if we have an explicit hint to inline.
     if (!targetScript->shouldInline()) {
         // Cap the inlining depth.
-        if (IsSmallFunction(targetScript)) {
-            if (inliningDepth_ >= js_IonOptions.smallFunctionMaxInlineDepth)
+        if (js_IonOptions.isSmallFunction(targetScript)) {
+            if (inliningDepth_ >= optimizationInfo().smallFunctionMaxInlineDepth())
                 return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
         } else {
-            if (inliningDepth_ >= js_IonOptions.maxInlineDepth)
+            if (inliningDepth_ >= optimizationInfo().maxInlineDepth())
                 return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
 
             if (targetScript->hasLoops())
                 return DontInline(targetScript, "Vetoed: big function that contains a loop");
 
             // Caller must not be excessively large.
-            if (script()->length() >= js_IonOptions.inliningMaxCallerBytecodeLength)
+            if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength())
                 return DontInline(targetScript, "Vetoed: caller excessively large");
         }
 
         // Callee must not be excessively large.
         // This heuristic also applies to the callsite as a whole.
-        if (targetScript->length() > js_IonOptions.inlineMaxTotalBytecodeLength)
+        if (targetScript->length() > optimizationInfo().inlineMaxTotalBytecodeLength())
             return DontInline(targetScript, "Vetoed: callee excessively large");
 
         // Callee must have been called a few times to have somewhat stable
         // type information, except for definite properties analysis,
         // as the caller has not run yet.
-        if (targetScript->getUseCount() < js_IonOptions.usesBeforeInlining() &&
+        if (targetScript->getUseCount() < optimizationInfo().usesBeforeInlining() &&
             info().executionMode() != DefinitePropertiesAnalysis)
         {
             return DontInline(targetScript, "Vetoed: callee is insufficiently hot.");
@@ -4063,7 +4059,7 @@ IonBuilder::selectInliningTargets(ObjectVector &targets, CallInfo &callInfo, Boo
         // Enforce a maximum inlined bytecode limit at the callsite.
         if (inlineable && target->isInterpreted()) {
             totalSize += target->nonLazyScript()->length();
-            if (totalSize > js_IonOptions.inlineMaxTotalBytecodeLength)
+            if (totalSize > optimizationInfo().inlineMaxTotalBytecodeLength())
                 inlineable = false;
         }
 
@@ -4157,9 +4153,6 @@ IonBuilder::InliningStatus
 IonBuilder::inlineCallsite(ObjectVector &targets, ObjectVector &originals,
                            bool lambda, CallInfo &callInfo)
 {
-    if (!inliningEnabled())
-        return InliningStatus_NotInlined;
-
     if (targets.length() == 0)
         return InliningStatus_NotInlined;
 
@@ -5999,10 +5992,28 @@ ClassHasResolveHook(CompileCompartment *comp, const Class *clasp, PropertyName *
 void
 IonBuilder::insertRecompileCheck()
 {
+    // PJS doesn't recompile and doesn't need recompile checks.
     if (info().executionMode() != SequentialExecution)
         return;
 
-    return;
+    // No need for recompile checks if this is the highest optimization level.
+    OptimizationLevel curLevel = optimizationInfo().level();
+    if (js_IonOptimizations.isLastLevel(curLevel))
+        return;
+
+    // Add recompile check.
+
+    // Get the topmost builder. The topmost script will get recompiled when
+    // usecount is high enough to justify a higher optimization level.
+    IonBuilder *topBuilder = this;
+    while (topBuilder->callerBuilder_)
+        topBuilder = topBuilder->callerBuilder_;
+
+    // Add recompile check to recompile when the usecount reaches the usecount
+    // of the next optimization level.
+    OptimizationLevel nextLevel = js_IonOptimizations.nextLevel(curLevel);
+    const OptimizationInfo *info = js_IonOptimizations.get(nextLevel);
+    current->add(MRecompileCheck::New(alloc(), topBuilder->script(), info->usesBeforeCompile()));
 }
 
 JSObject *
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index a844910be45a..d2af9d5b8b89 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -13,6 +13,7 @@
 // JSScript.
 
 #include "jit/BytecodeAnalysis.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
@@ -211,9 +212,10 @@ class IonBuilder : public MIRGenerator
     static int CmpSuccessors(const void *a, const void *b);
 
   public:
-    IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, MIRGraph *graph,
-               types::CompilerConstraintList *constraints,
-               BaselineInspector *inspector, CompileInfo *info, BaselineFrameInspector *baselineFrame,
+    IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp,
+               MIRGraph *graph, types::CompilerConstraintList *constraints,
+               BaselineInspector *inspector, CompileInfo *info,
+               const OptimizationInfo *optimizationInfo, BaselineFrameInspector *baselineFrame,
                size_t inliningDepth = 0, uint32_t loopDepth = 0);
 
     bool build();
@@ -230,10 +232,6 @@ class IonBuilder : public MIRGenerator
     bool abort(const char *message, ...);
     void spew(const char *message);
 
-    static bool inliningEnabled() {
-        return js_IonOptions.inlining;
-    }
-
     JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes);
     bool getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing,
                             ObjectVector &targets, uint32_t maxTargets, bool *gotLambda);
diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h
index 8167d1fa80e9..d3ecaa94223e 100644
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -14,6 +14,7 @@
 #include "jstypes.h"
 
 #include "gc/Heap.h"
+#include "jit/IonOptimizationLevels.h"
 #include "jit/IonTypes.h"
 
 namespace JSC {
@@ -261,6 +262,9 @@ struct IonScript
     // Identifier of the compilation which produced this code.
     types::RecompileInfo recompileInfo_;
 
+    // The optimization level this script was compiled in.
+    OptimizationLevel optimizationLevel_;
+
     // Number of times we tried to enter this script via OSR but failed due to
     // a LOOPENTRY pc other than osrPc_.
     uint32_t osrPcMismatchCounter_;
@@ -335,7 +339,8 @@ struct IonScript
                           size_t snapshotsSize, size_t snapshotEntries,
                           size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries,
                           size_t cacheEntries, size_t runtimeSize, size_t safepointsSize,
-                          size_t callTargetEntries, size_t backedgeEntries);
+                          size_t callTargetEntries, size_t backedgeEntries,
+                          OptimizationLevel optimizationLevel);
     static void Trace(JSTracer *trc, IonScript *script);
     static void Destroy(FreeOp *fop, IonScript *script);
 
@@ -526,6 +531,9 @@ struct IonScript
     const types::RecompileInfo& recompileInfo() const {
         return recompileInfo_;
     }
+    OptimizationLevel optimizationLevel() const {
+        return optimizationLevel_;
+    }
     uint32_t incrOsrPcMismatchCounter() {
         return ++osrPcMismatchCounter_;
     }
diff --git a/js/src/jit/IonOptimizationLevels.cpp b/js/src/jit/IonOptimizationLevels.cpp
new file mode 100644
index 000000000000..e6679c8ccbf7
--- /dev/null
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jit/IonOptimizationLevels.h"
+
+#include "jsanalyze.h"
+#include "jsscript.h"
+
+using namespace js;
+using namespace js::jit;
+
+namespace js {
+namespace jit {
+
+OptimizationInfos js_IonOptimizations;
+
+void
+OptimizationInfo::initNormalOptimizationInfo()
+{
+    level_ = Optimization_Normal;
+
+    eaa_ = true;
+    edgeCaseAnalysis_ = true;
+    eliminateRedundantChecks_ = true;
+    inlineInterpreted_ = true;
+    inlineNative_ = true;
+    gvn_ = true;
+    gvnKind_ = GVN_Optimistic;
+    licm_ = true;
+    uce_ = true;
+    rangeAnalysis_ = true;
+    registerAllocator_ = RegisterAllocator_LSRA;
+
+    inlineMaxTotalBytecodeLength_ = 1000;
+    inliningMaxCallerBytecodeLength_ = 10000;
+    maxInlineDepth_ = 3;
+    smallFunctionMaxInlineDepth_ = 10;
+    usesBeforeCompile_ = 1000;
+    usesBeforeInliningFactor_ = 0.125;
+}
+
+void
+OptimizationInfo::initAsmjsOptimizationInfo()
+{
+    // The AsmJS optimization level
+    // Disables some passes that don't work well with asmjs.
+
+    // Take normal option values for not specified values.
+    initNormalOptimizationInfo();
+
+    level_ = Optimization_AsmJS;
+    edgeCaseAnalysis_ = false;
+    eliminateRedundantChecks_ = false;
+}
+
+OptimizationInfos::OptimizationInfos()
+{
+    infos_[Optimization_Normal - 1].initNormalOptimizationInfo();
+    infos_[Optimization_AsmJS - 1].initAsmjsOptimizationInfo();
+
+#ifdef DEBUG
+    OptimizationLevel prev = nextLevel(Optimization_DontCompile);
+    while (!isLastLevel(prev)) {
+        OptimizationLevel next = nextLevel(prev);
+        JS_ASSERT(get(prev)->usesBeforeCompile() < get(next)->usesBeforeCompile());
+        prev = next;
+    }
+#endif
+}
+
+OptimizationLevel
+OptimizationInfos::nextLevel(OptimizationLevel level)
+{
+    JS_ASSERT(!isLastLevel(level));
+    switch (level) {
+      case Optimization_DontCompile:
+        return Optimization_Normal;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Unknown optimization level.");
+    }
+}
+
+bool
+OptimizationInfos::isLastLevel(OptimizationLevel level)
+{
+    return level == Optimization_Normal;
+}
+
+OptimizationLevel
+OptimizationInfos::levelForUseCount(uint32_t useCount)
+{
+    OptimizationLevel prev = Optimization_DontCompile;
+
+    while (!isLastLevel(prev)) {
+        OptimizationLevel level = nextLevel(prev);
+        const OptimizationInfo *info = get(level);
+        if (useCount < info->usesBeforeCompile())
+            return prev;
+
+        prev = level;
+    }
+
+    return prev;
+}
+
+} // namespace jit
+} // namespace js
diff --git a/js/src/jit/IonOptimizationLevels.h b/js/src/jit/IonOptimizationLevels.h
new file mode 100644
index 000000000000..012a07968706
--- /dev/null
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_IonOptimizationLevels_h
+#define jit_IonOptimizationLevels_h
+
+#include "jsbytecode.h"
+#include "jstypes.h"
+
+#include "jit/IonOptions.h"
+#include "js/TypeDecls.h"
+
+namespace js {
+namespace jit {
+
+enum OptimizationLevel
+{
+    Optimization_DontCompile,
+    Optimization_Normal,
+    Optimization_AsmJS,
+    Optimization_Count
+};
+
+class OptimizationInfo
+{
+  public:
+    OptimizationLevel level_;
+
+    // Toggles whether Effective Address Analysis is performed.
+    bool eaa_;
+
+    // Toggles whether Edge Case Analysis is used.
+    bool edgeCaseAnalysis_;
+
+    // Toggles whether redundant checks get removed.
+    bool eliminateRedundantChecks_;
+
+    // Toggles whether interpreted scripts get inlined.
+    bool inlineInterpreted_;
+
+    // Toggles whether native scripts get inlined.
+    bool inlineNative_;
+
+    // Toggles whether global value numbering is used.
+    bool gvn_;
+
+    // Toggles whether global value numbering is optimistic or pessimistic.
+    IonGvnKind gvnKind_;
+
+    // Toggles whether loop invariant code motion is performed.
+    bool licm_;
+
+    // Toggles whether Unreachable Code Elimination is performed.
+    bool uce_;
+
+    // Toggles whether Range Analysis is used.
+    bool rangeAnalysis_;
+
+    // Describes which register allocator to use.
+    IonRegisterAllocator registerAllocator_;
+
+    // The maximum total bytecode size of an inline call site.
+    uint32_t inlineMaxTotalBytecodeLength_;
+
+    // The maximum bytecode length the caller may have,
+    // before we stop inlining large functions in that caller.
+    uint32_t inliningMaxCallerBytecodeLength_;
+
+    // The maximum inlining depth.
+    uint32_t maxInlineDepth_;
+
+    // The maximum inlining depth for functions.
+    //
+    // Inlining small functions has almost no compiling overhead
+    // and removes the otherwise needed call overhead.
+    // The value is currently very low.
+    // Actually it is only needed to make sure we don't blow out the stack.
+    uint32_t smallFunctionMaxInlineDepth_;
+
+    // How many invocations or loop iterations are needed before functions
+    // are compiled.
+    uint32_t usesBeforeCompile_;
+
+    // How many invocations or loop iterations are needed before calls
+    // are inlined, as a fraction of usesBeforeCompile.
+    double usesBeforeInliningFactor_;
+
+    OptimizationInfo()
+    { }
+
+    void initNormalOptimizationInfo();
+    void initAsmjsOptimizationInfo();
+
+    OptimizationLevel level() const {
+        return level_;
+    }
+
+    bool inlineInterpreted() const {
+        return inlineInterpreted_ && !js_IonOptions.disableInlining;
+    }
+
+    bool inlineNative() const {
+        return inlineNative_ && !js_IonOptions.disableInlining;
+    }
+
+    uint32_t usesBeforeCompile() const {
+        if (js_IonOptions.forceDefaultIonUsesBeforeCompile)
+            return js_IonOptions.forcedDefaultIonUsesBeforeCompile;
+        return usesBeforeCompile_;
+    }
+
+    bool gvnEnabled() const {
+        return gvn_ && !js_IonOptions.disableGvn;
+    }
+
+    bool licmEnabled() const {
+        return licm_ && !js_IonOptions.disableLicm;
+    }
+
+    bool uceEnabled() const {
+        return uce_ && !js_IonOptions.disableUce;
+    }
+
+    bool rangeAnalysisEnabled() const {
+        return rangeAnalysis_ && !js_IonOptions.disableRangeAnalysis;
+    }
+
+    bool eaaEnabled() const {
+        return eaa_ && !js_IonOptions.disableEaa;
+    }
+
+    bool edgeCaseAnalysisEnabled() const {
+        return edgeCaseAnalysis_ && !js_IonOptions.disableEdgeCaseAnalysis;
+    }
+
+    bool eliminateRedundantChecksEnabled() const {
+        return eliminateRedundantChecks_;
+    }
+
+    IonGvnKind gvnKind() const {
+        if (!js_IonOptions.forceGvnKind)
+            return gvnKind_;
+        return js_IonOptions.forcedGvnKind;
+    }
+
+    IonRegisterAllocator registerAllocator() const {
+        if (!js_IonOptions.forceRegisterAllocator)
+            return registerAllocator_;
+        return js_IonOptions.forcedRegisterAllocator;
+    }
+
+    uint32_t smallFunctionMaxInlineDepth() const {
+        return smallFunctionMaxInlineDepth_;
+    }
+
+    bool isSmallFunction(JSScript *script) const;
+
+    uint32_t maxInlineDepth() const {
+        return maxInlineDepth_;
+    }
+
+    uint32_t inlineMaxTotalBytecodeLength() const {
+        return inlineMaxTotalBytecodeLength_;
+    }
+
+    uint32_t inliningMaxCallerBytecodeLength() const {
+        return inlineMaxTotalBytecodeLength_;
+    }
+
+    uint32_t usesBeforeInlining() const {
+        return usesBeforeCompile() * usesBeforeInliningFactor_;
+    }
+};
+
+class OptimizationInfos
+{
+  private:
+    OptimizationInfo infos_[Optimization_Count - 1];
+
+  public:
+    OptimizationInfos();
+
+    const OptimizationInfo *get(OptimizationLevel level) {
+        JS_ASSERT(level < Optimization_Count);
+        JS_ASSERT(level != Optimization_DontCompile);
+
+        return &infos_[level - 1];
+    }
+
+    OptimizationLevel nextLevel(OptimizationLevel level);
+    bool isLastLevel(OptimizationLevel level);
+    OptimizationLevel levelForUseCount(uint32_t useCount);
+};
+
+extern OptimizationInfos js_IonOptimizations;
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_IonOptimizationLevels_h */
diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp
new file mode 100644
index 000000000000..3c3bf41d8dfb
--- /dev/null
+++ b/js/src/jit/JitOptions.cpp
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jit/IonOptions.h"
+
+using namespace js;
+using namespace js::jit;
+
+namespace js {
+namespace jit {
+
+IonOptions js_IonOptions;
+
+IonOptions::IonOptions()
+{
+    // Whether to perform expensive graph-consistency DEBUG-only assertions.
+    // It can be useful to disable this to reduce DEBUG-compile time of large
+    // asm.js programs.
+    checkGraphConsistency = true;
+
+#ifdef CHECK_OSIPOINT_REGISTERS
+    // Emit extra code to verify live regs at the start of a VM call
+    // are not modified before its OsiPoint.
+    checkOsiPointRegisters = false;
+#endif
+
+    // Whether to enable extra code to perform dynamic validation of
+    // RangeAnalysis results.
+    checkRangeAnalysis = false;
+
+    // Whether to protect the GC heap during Ion compilation and ensure that
+    // only threadsafe operations are performed on it.
+    checkThreadSafety = false;
+
+    // Whether Ion should compile try-catch statements.
+    compileTryCatch = true;
+
+    // Toggle whether global value numbering is globally disabled.
+    disableGvn = false;
+
+    // Toggles whether loop invariant code motion is globally disabled.
+    disableLicm = false;
+
+    // Toggles whether inlining is globally disabled.
+    disableInlining = false;
+
+    // Toggles whether Edge Case Analysis is gobally disabled.
+    disableEdgeCaseAnalysis = false;
+
+    // Toggles whether Range Analysis is globally disabled.
+    disableRangeAnalysis = false;
+
+    // Toggles whether Unreachable Code Elimination is globally disabled.
+    disableUce = false;
+
+    // Toggles whether Effective Address Analysis is globally disabled.
+    disableEaa = false;
+
+    // Whether functions are compiled immediately.
+    eagerCompilation = false;
+
+    // Force how many invocation or loop iterations are needed before compiling
+    // a function with the highest ionmonkey optimization level.
+    // (i.e. OptimizationLevel_Normal)
+    forceDefaultIonUsesBeforeCompile = false;
+    forcedDefaultIonUsesBeforeCompile = 1000;
+
+    // Force the GVN kind to be optimistic or pessimistic instead of letting
+    // the optimization pass decide.
+    forceGvnKind = false;
+    forcedGvnKind = GVN_Optimistic;
+
+    // Force the used register allocator instead of letting the
+    // optimization pass decide.
+    forceRegisterAllocator = false;
+    forcedRegisterAllocator = RegisterAllocator_LSRA;
+
+    // Toggles whether large scripts are rejected.
+    limitScriptSize = true;
+
+    // Toggles whether functions may be entered at loop headers.
+    osr = true;
+
+    // How many invocations or loop iterations are needed before functions
+    // are compiled with the baseline compiler.
+    baselineUsesBeforeCompile = 10;
+
+    // Number of exception bailouts (resuming into catch/finally block) before
+    // we invalidate and forbid Ion compilation.
+    exceptionBailoutThreshold = 10;
+
+    // Number of bailouts without invalidation before we set
+    // JSScript::hadFrequentBailouts and invalidate.
+    frequentBailoutThreshold = 10;
+
+    // How many actual arguments are accepted on the C stack.
+    maxStackArgs = 4096;
+
+    // How many times we will try to enter a script via OSR before
+    // invalidating the script.
+    osrPcMismatchesBeforeRecompile = 6000;
+
+    // The bytecode length limit for small function.
+    //
+    // The default for this was arrived at empirically via benchmarking.
+    // We may want to tune it further after other optimizations have gone
+    // in.
+    smallFunctionMaxBytecodeLength_ = 100;
+
+    // How many uses of a parallel kernel before we attempt compilation.
+    usesBeforeCompilePar = 1;
+}
+
+bool
+IonOptions::isSmallFunction(JSScript *script) const
+{
+    return script->length() <= smallFunctionMaxBytecodeLength_;
+}
+
+void
+IonOptions::setEagerCompilation()
+{
+    eagerCompilation = true;
+    baselineUsesBeforeCompile = 0;
+    forceDefaultIonUsesBeforeCompile = true;
+    forcedDefaultIonUsesBeforeCompile = 0;
+}
+
+void
+IonOptions::setUsesBeforeCompile(uint32_t useCount)
+{
+    forceDefaultIonUsesBeforeCompile = true;
+    forcedDefaultIonUsesBeforeCompile = useCount;
+
+    // Undo eager compilation
+    if (eagerCompilation && useCount != 0) {
+        jit::IonOptions defaultValues;
+        eagerCompilation = false;
+        baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile;
+    }
+}
+
+void
+IonOptions::resetUsesBeforeCompile()
+{
+    forceDefaultIonUsesBeforeCompile = false;
+
+    // Undo eager compilation
+    if (eagerCompilation) {
+        jit::IonOptions defaultValues;
+        eagerCompilation = false;
+        baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile;
+    }
+}
+
+} // namespace jit
+} // namespace js
diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h
new file mode 100644
index 000000000000..2f3d3d65cd18
--- /dev/null
+++ b/js/src/jit/JitOptions.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_IonOptions_h
+#define jit_IonOptions_h
+
+#include "jit/IonTypes.h"
+
+#ifdef JS_ION
+
+namespace js {
+namespace jit {
+
+// Possible register allocators which may be used.
+enum IonRegisterAllocator {
+    RegisterAllocator_LSRA,
+    RegisterAllocator_Backtracking,
+    RegisterAllocator_Stupid
+};
+
+enum IonGvnKind {
+    GVN_Optimistic,
+    GVN_Pessimistic
+};
+
+struct IonOptions
+{
+    bool checkGraphConsistency;
+#ifdef CHECK_OSIPOINT_REGISTERS
+    bool checkOsiPointRegisters;
+#endif
+    bool checkRangeAnalysis;
+    bool checkThreadSafety;
+    bool compileTryCatch;
+    bool disableGvn;
+    bool disableLicm;
+    bool disableInlining;
+    bool disableEdgeCaseAnalysis;
+    bool disableRangeAnalysis;
+    bool disableUce;
+    bool disableEaa;
+    bool eagerCompilation;
+    bool forceDefaultIonUsesBeforeCompile;
+    uint32_t forcedDefaultIonUsesBeforeCompile;
+    bool forceGvnKind;
+    IonGvnKind forcedGvnKind;
+    bool forceRegisterAllocator;
+    IonRegisterAllocator forcedRegisterAllocator;
+    bool limitScriptSize;
+    bool osr;
+    uint32_t baselineUsesBeforeCompile;
+    uint32_t exceptionBailoutThreshold;
+    uint32_t frequentBailoutThreshold;
+    uint32_t maxStackArgs;
+    uint32_t osrPcMismatchesBeforeRecompile;
+    uint32_t smallFunctionMaxBytecodeLength_;
+    uint32_t usesBeforeCompilePar;
+
+    IonOptions();
+    bool isSmallFunction(JSScript *script) const;
+    void setEagerCompilation();
+    void setUsesBeforeCompile(uint32_t useCount);
+    void resetUsesBeforeCompile();
+};
+
+extern IonOptions js_IonOptions;
+
+} // namespace jit
+} // namespace js
+
+#endif // JS_ION
+
+#endif /* jit_IonOptions_h */
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index 6e2800c04241..5f65fe844778 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3550,7 +3550,7 @@ LIRGenerator::visitBlock(MBasicBlock *block)
     if (!definePhis())
         return false;
 
-    if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) {
+    if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) {
         if (!add(new(alloc()) LLabel()))
             return false;
     }
diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
index 6292fc1ab510..d5e4dde8fc8b 100644
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -24,6 +24,9 @@ namespace jit {
 IonBuilder::InliningStatus
 IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native)
 {
+    if (!optimizationInfo().inlineNative())
+        return InliningStatus_NotInlined;
+
     // Array natives.
     if (native == js_Array)
         return inlineArray(callInfo);
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index cd4cba1d0c7e..c35b7ab9b1ed 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9204,13 +9204,16 @@ class MHaveSameClass
     }
 };
 
+// Increase the usecount of the provided script upon execution and test if
+// the usecount surpasses the threshold. Upon hit it will recompile the
+// outermost script (i.e. not the inlined script).
 class MRecompileCheck : public MNullaryInstruction
 {
     JSScript *script_;
     uint32_t recompileThreshold_;
 
-    MRecompileCheck(JSScript *script_, uint32_t recompileThreshold)
-      : script_(script_),
+    MRecompileCheck(JSScript *script, uint32_t recompileThreshold)
+      : script_(script),
         recompileThreshold_(recompileThreshold)
     {
         setGuard();
diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h
index 93fe32a795c5..bce7c0c963e0 100644
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -29,11 +29,13 @@ namespace jit {
 class MBasicBlock;
 class MIRGraph;
 class MStart;
+class OptimizationInfo;
 
 class MIRGenerator
 {
   public:
-    MIRGenerator(CompileCompartment *compartment, TempAllocator *alloc, MIRGraph *graph, CompileInfo *info);
+    MIRGenerator(CompileCompartment *compartment, TempAllocator *alloc, MIRGraph *graph,
+                 CompileInfo *info, const OptimizationInfo *optimizationInfo);
 
     TempAllocator &alloc() {
         return *alloc_;
@@ -50,6 +52,9 @@ class MIRGenerator
     CompileInfo &info() {
         return *info_;
     }
+    const OptimizationInfo &optimizationInfo() const {
+        return *optimizationInfo_;
+    }
 
     template 
     T * allocate(size_t count = 1) {
@@ -127,6 +132,7 @@ class MIRGenerator
 
   protected:
     CompileInfo *info_;
+    const OptimizationInfo *optimizationInfo_;
     TempAllocator *alloc_;
     JSFunction *fun_;
     uint32_t nslots_;
diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp
index ac4034f6a536..3965afec7f6d 100644
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -17,9 +17,11 @@ using namespace js;
 using namespace js::jit;
 
 MIRGenerator::MIRGenerator(CompileCompartment *compartment,
-                           TempAllocator *alloc, MIRGraph *graph, CompileInfo *info)
+                           TempAllocator *alloc, MIRGraph *graph, CompileInfo *info,
+                           const OptimizationInfo *optimizationInfo)
   : compartment(compartment),
     info_(info),
+    optimizationInfo_(optimizationInfo),
     alloc_(alloc),
     graph_(graph),
     error_(false),
diff --git a/js/src/jit/UnreachableCodeElimination.cpp b/js/src/jit/UnreachableCodeElimination.cpp
index ea37feb5d541..dec3d1caf8a3 100644
--- a/js/src/jit/UnreachableCodeElimination.cpp
+++ b/js/src/jit/UnreachableCodeElimination.cpp
@@ -82,8 +82,8 @@ UnreachableCodeElimination::removeUnmarkedBlocksAndCleanup()
 
     // Pass 5: It's important for optimizations to re-run GVN (and in
     // turn alias analysis) after UCE if we eliminated branches.
-    if (rerunAliasAnalysis_ && js_IonOptions.gvn) {
-        ValueNumberer gvn(mir_, graph_, js_IonOptions.gvnIsOptimistic);
+    if (rerunAliasAnalysis_ && mir_->optimizationInfo().gvnEnabled()) {
+        ValueNumberer gvn(mir_, graph_, mir_->optimizationInfo().gvnKind() == GVN_Optimistic);
         if (!gvn.clear() || !gvn.analyze())
             return false;
         IonSpewPass("GVN-after-UCE");
diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h
index 940d346cae9d..fdce3c69c6f8 100644
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -76,7 +76,7 @@ LIRGeneratorShared::defineFixed(LInstructionHelper<1, X, Y> *lir, MDefinition *m
     if (!define(lir, mir, def))
         return false;
 
-    if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) {
+    if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) {
         if (!add(new(alloc()) LNop))
             return false;
     }
@@ -163,7 +163,7 @@ LIRGeneratorShared::defineReturn(LInstruction *lir, MDefinition *mir)
     if (!add(lir))
         return false;
 
-    if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) {
+    if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) {
         if (!add(new(alloc()) LNop))
             return false;
     }
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 38996d6f9ae1..c6739f38d2a7 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5980,18 +5980,21 @@ JS_PUBLIC_API(void)
 JS_SetGlobalJitCompilerOption(JSContext *cx, JSJitCompilerOption opt, uint32_t value)
 {
 #ifdef JS_ION
-    jit::IonOptions defaultValues;
 
     switch (opt) {
       case JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER:
-        if (value == uint32_t(-1))
+        if (value == uint32_t(-1)) {
+            jit::IonOptions defaultValues;
             value = defaultValues.baselineUsesBeforeCompile;
+        }
         jit::js_IonOptions.baselineUsesBeforeCompile = value;
         break;
       case JSJITCOMPILER_ION_USECOUNT_TRIGGER:
-        if (value == uint32_t(-1))
-            value = defaultValues.usesBeforeCompile;
-        jit::js_IonOptions.usesBeforeCompile = value;
+        if (value == uint32_t(-1)) {
+            jit::js_IonOptions.resetUsesBeforeCompile();
+            break;
+        }
+        jit::js_IonOptions.setUsesBeforeCompile(value);
         if (value == 0)
             jit::js_IonOptions.setEagerCompilation();
         break;
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
index 544f3798767f..2f8616f056f2 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -590,8 +590,10 @@ class JSScript : public js::gc::BarrieredCell
     uint32_t        sourceEnd_;
 
     uint32_t        useCount;   /* Number of times the script has been called
-                                 * or has had backedges taken. Reset if the
-                                 * script's JIT code is forcibly discarded. */
+                                 * or has had backedges taken. When running in
+                                 * ion, also increased for any inlined scripts.
+                                 * Reset if the script's JIT code is forcibly
+                                 * discarded. */
 
 #ifdef DEBUG
     // Unique identifier within the compartment for this script, used for
diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp
index 61cf70e8eca8..ce332a9f0163 100644
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -746,7 +746,6 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
     ionBuilder = state.ionWorklist.popCopy();
 
     DebugOnly executionMode = ionBuilder->info().executionMode();
-    JS_ASSERT(jit::GetIonScript(ionBuilder->script(), executionMode) == ION_COMPILING_SCRIPT);
 
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::ION_BACKGROUND_COMPILER),
diff --git a/js/src/moz.build b/js/src/moz.build
index 31769c7439b5..51468c1b5f5f 100644
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -257,6 +257,8 @@ if CONFIG['ENABLE_ION']:
         'jit/IonCaches.cpp',
         'jit/IonFrames.cpp',
         'jit/IonMacroAssembler.cpp',
+        'jit/IonOptimizationLevels.cpp',
+        'jit/IonOptions.cpp',
         'jit/IonSpewer.cpp',
         'jit/JSONSpewer.cpp',
         'jit/LICM.cpp',
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 501bc02295ff..7079590b3eac 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5440,39 +5440,42 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
     }
 
     if (const char *str = op->getStringOption("ion-gvn")) {
-        if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.gvn = false;
-        else if (strcmp(str, "pessimistic") == 0)
-            jit::js_IonOptions.gvnIsOptimistic = false;
-        else if (strcmp(str, "optimistic") == 0)
-            jit::js_IonOptions.gvnIsOptimistic = true;
-        else
+        if (strcmp(str, "off") == 0) {
+            jit::js_IonOptions.disableGvn = true;
+        } else if (strcmp(str, "pessimistic") == 0) {
+            jit::js_IonOptions.forceGvnKind = true;
+            jit::js_IonOptions.forcedGvnKind = jit::GVN_Pessimistic;
+        } else if (strcmp(str, "optimistic") == 0) {
+            jit::js_IonOptions.forceGvnKind = true;
+            jit::js_IonOptions.forcedGvnKind = jit::GVN_Optimistic;
+        } else {
             return OptionFailure("ion-gvn", str);
+        }
     }
 
     if (const char *str = op->getStringOption("ion-licm")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.licm = true;
+            jit::js_IonOptions.disableLicm = false;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.licm = false;
+            jit::js_IonOptions.disableLicm = true;
         else
             return OptionFailure("ion-licm", str);
     }
 
     if (const char *str = op->getStringOption("ion-edgecase-analysis")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.edgeCaseAnalysis = true;
+            jit::js_IonOptions.disableEdgeCaseAnalysis = false;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.edgeCaseAnalysis = false;
+            jit::js_IonOptions.disableEdgeCaseAnalysis = true;
         else
             return OptionFailure("ion-edgecase-analysis", str);
     }
 
      if (const char *str = op->getStringOption("ion-range-analysis")) {
          if (strcmp(str, "on") == 0)
-             jit::js_IonOptions.rangeAnalysis = true;
+             jit::js_IonOptions.disableRangeAnalysis = false;
          else if (strcmp(str, "off") == 0)
-             jit::js_IonOptions.rangeAnalysis = false;
+             jit::js_IonOptions.disableRangeAnalysis = true;
          else
              return OptionFailure("ion-range-analysis", str);
      }
@@ -5485,9 +5488,9 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
 
     if (const char *str = op->getStringOption("ion-inlining")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.inlining = true;
+            jit::js_IonOptions.disableInlining = false;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.inlining = false;
+            jit::js_IonOptions.disableInlining = true;
         else
             return OptionFailure("ion-inlining", str);
     }
@@ -5512,7 +5515,7 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
 
     int32_t useCount = op->getIntOption("ion-uses-before-compile");
     if (useCount >= 0)
-        jit::js_IonOptions.usesBeforeCompile = useCount;
+        jit::js_IonOptions.setUsesBeforeCompile(useCount);
 
     useCount = op->getIntOption("baseline-uses-before-compile");
     if (useCount >= 0)
@@ -5522,14 +5525,18 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
         jit::js_IonOptions.baselineUsesBeforeCompile = 0;
 
     if (const char *str = op->getStringOption("ion-regalloc")) {
-        if (strcmp(str, "lsra") == 0)
-            jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_LSRA;
-        else if (strcmp(str, "backtracking") == 0)
-            jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_Backtracking;
-        else if (strcmp(str, "stupid") == 0)
-            jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_Stupid;
-        else
+        if (strcmp(str, "lsra") == 0) {
+            jit::js_IonOptions.forceRegisterAllocator = true;
+            jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_LSRA;
+        } else if (strcmp(str, "backtracking") == 0) {
+            jit::js_IonOptions.forceRegisterAllocator = true;
+            jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_Backtracking;
+        } else if (strcmp(str, "stupid") == 0) {
+            jit::js_IonOptions.forceRegisterAllocator = true;
+            jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_Stupid;
+        } else {
             return OptionFailure("ion-regalloc", str);
+        }
     }
 
     if (op->getBoolOption("ion-eager"))

From 513653733ac9f71f4c816f847b92b4ea8fb15da5 Mon Sep 17 00:00:00 2001
From: Hannes Verschore 
Date: Thu, 12 Dec 2013 15:14:13 +0100
Subject: [PATCH 012/459] Bug 939614: IonMonkey: Rename IonOption to JitOption,
 r=jandem

---
 js/src/builtin/TestingFunctions.cpp        |  4 +-
 js/src/jit/Bailouts.cpp                    |  2 +-
 js/src/jit/BaselineJIT.cpp                 |  2 +-
 js/src/jit/Ion.cpp                         | 14 ++---
 js/src/jit/Ion.h                           |  4 +-
 js/src/jit/IonAnalysis.cpp                 |  4 +-
 js/src/jit/IonBuilder.cpp                  |  6 +--
 js/src/jit/IonFrames.cpp                   |  2 +-
 js/src/jit/IonOptimizationLevels.h         | 30 +++++------
 js/src/jit/JitOptions.cpp                  | 20 ++++----
 js/src/jit/JitOptions.h                    | 13 ++---
 js/src/jit/LinearScan.cpp                  |  4 +-
 js/src/jit/LiveRangeAllocator.h            |  2 +-
 js/src/jit/Lowering.cpp                    |  2 +-
 js/src/jit/RangeAnalysis.cpp               |  2 +-
 js/src/jit/shared/CodeGenerator-shared.cpp |  2 +-
 js/src/jsapi.cpp                           | 10 ++--
 js/src/moz.build                           |  2 +-
 js/src/shell/js.cpp                        | 60 +++++++++++-----------
 js/src/vm/ForkJoin.cpp                     |  4 +-
 20 files changed, 96 insertions(+), 93 deletions(-)

diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
index 8d1a8b1a91f2..c6261995b272 100644
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -956,7 +956,7 @@ static bool
 EnableOsiPointRegisterChecks(JSContext *, unsigned, jsval *vp)
 {
 #if defined(JS_ION) && defined(CHECK_OSIPOINT_REGISTERS)
-    jit::js_IonOptions.checkOsiPointRegisters = true;
+    jit::js_JitOptions.checkOsiPointRegisters = true;
 #endif
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
@@ -1125,7 +1125,7 @@ SetIonCheckGraphCoherency(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 #ifdef JS_ION
-    jit::js_IonOptions.checkGraphConsistency = ToBoolean(args.get(0));
+    jit::js_JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
 #endif
     args.rval().setUndefined();
     return true;
diff --git a/js/src/jit/Bailouts.cpp b/js/src/jit/Bailouts.cpp
index fe49af0216ca..c00f3a893936 100644
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -196,7 +196,7 @@ jit::CheckFrequentBailouts(JSContext *cx, JSScript *script)
         // we compile this script LICM will be disabled.
         IonScript *ionScript = script->ionScript();
 
-        if (ionScript->numBailouts() >= js_IonOptions.frequentBailoutThreshold &&
+        if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold &&
             !script->hadFrequentBailouts())
         {
             script->setHadFrequentBailouts();
diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp
index 0956e75f4ca6..7cf1bc170c7d 100644
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -276,7 +276,7 @@ CanEnterBaselineJIT(JSContext *cx, HandleScript script, bool osr)
     if (IsJSDEnabled(cx) || cx->runtime()->parallelWarmup > 0) {
         if (osr)
             return Method_Skipped;
-    } else if (script->incUseCount() <= js_IonOptions.baselineUsesBeforeCompile) {
+    } else if (script->incUseCount() <= js_JitOptions.baselineUsesBeforeCompile) {
         return Method_Skipped;
     }
 
diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp
index a13f7bac1eff..1bad578a6f59 100644
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1705,7 +1705,7 @@ IonCompile(JSContext *cx, JSScript *script,
     IonSpewNewFunction(graph, builderScript);
 
     mozilla::Maybe protect;
-    if (js_IonOptions.checkThreadSafety &&
+    if (js_JitOptions.checkThreadSafety &&
         cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL &&
         !cx->runtime()->profilingScripts &&
         !cx->runtime()->spsProfiler.enabled())
@@ -1816,7 +1816,7 @@ static const uint32_t MAX_DOM_WORKER_LOCALS_AND_ARGS = 2048;
 static MethodStatus
 CheckScriptSize(JSContext *cx, JSScript* script)
 {
-    if (!js_IonOptions.limitScriptSize)
+    if (!js_JitOptions.limitScriptSize)
         return Method_Compiled;
 
     if (script->length() > MAX_OFF_THREAD_SCRIPT_SIZE) {
@@ -1893,7 +1893,7 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
     JS_ASSERT(jit::IsIonEnabled(cx));
     JS_ASSERT(jit::IsBaselineEnabled(cx));
     JS_ASSERT_IF(osrPc != nullptr, (JSOp)*osrPc == JSOP_LOOPENTRY);
-    JS_ASSERT_IF(executionMode == ParallelExecution, !osrFrame && !osrPC);
+    JS_ASSERT_IF(executionMode == ParallelExecution, !osrFrame && !osrPc);
     JS_ASSERT_IF(executionMode == ParallelExecution, !HasIonScript(script, executionMode));
 
     if (!script->hasBaselineScript())
@@ -1931,7 +1931,7 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
         // recompile with the right pc.
         if (osrPc && script->ionScript()->osrPc() != osrPc) {
             uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
-            if (count <= js_IonOptions.osrPcMismatchesBeforeRecompile)
+            if (count <= js_JitOptions.osrPcMismatchesBeforeRecompile)
                 return Method_Skipped;
 
             failedState = Method_Skipped;
@@ -1994,7 +1994,7 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
         return Method_Skipped;
 
     // Optionally ignore on user request.
-    if (!js_IonOptions.osr)
+    if (!js_JitOptions.osr)
         return Method_Skipped;
 
     // Mark as forbidden if frame can't be handled.
@@ -2077,7 +2077,7 @@ jit::CanEnter(JSContext *cx, RunState &state)
     // If --ion-eager is used, compile with Baseline first, so that we
     // can directly enter IonMonkey.
     RootedScript rscript(cx, script);
-    if (js_IonOptions.eagerCompilation && !rscript->hasBaselineScript()) {
+    if (js_JitOptions.eagerCompilation && !rscript->hasBaselineScript()) {
         MethodStatus status = CanEnterBaselineMethod(cx, state);
         if (status != Method_Compiled)
             return status;
@@ -2752,7 +2752,7 @@ jit::UsesBeforeIonCompile(JSScript *script, jsbytecode *pc)
     if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
         minUses = minUses * (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS);
 
-    if (JSOp(*pc) != JSOP_LOOPENTRY || js_IonOptions.eagerCompilation)
+    if (JSOp(*pc) != JSOP_LOOPENTRY || js_JitOptions.eagerCompilation)
         return minUses;
 
     // It's more efficient to enter outer loops, rather than inner loops, via OSR.
diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h
index 3052a540bab8..16b588ba0dc6 100644
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -16,7 +16,7 @@
 
 #include "jit/CompileInfo.h"
 #include "jit/CompileWrappers.h"
-#include "jit/IonOptions.h"
+#include "jit/JitOptions.h"
 
 namespace js {
 namespace jit {
@@ -174,7 +174,7 @@ IsIonInlinablePC(jsbytecode *pc) {
 inline bool
 TooManyArguments(unsigned nargs)
 {
-    return (nargs >= SNAPSHOT_MAX_NARGS || nargs > js_IonOptions.maxStackArgs);
+    return (nargs >= SNAPSHOT_MAX_NARGS || nargs > js_JitOptions.maxStackArgs);
 }
 
 void ForbidCompilation(JSContext *cx, JSScript *script);
diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp
index d91b5e49d5b9..a685efa82c89 100644
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1334,7 +1334,7 @@ void
 jit::AssertGraphCoherency(MIRGraph &graph)
 {
 #ifdef DEBUG
-    if (!js_IonOptions.checkGraphConsistency)
+    if (!js_JitOptions.checkGraphConsistency)
         return;
     AssertBasicGraphCoherency(graph);
     AssertReversePostOrder(graph);
@@ -1349,7 +1349,7 @@ jit::AssertExtendedGraphCoherency(MIRGraph &graph)
     // are split)
 
 #ifdef DEBUG
-    if (!js_IonOptions.checkGraphConsistency)
+    if (!js_JitOptions.checkGraphConsistency)
         return;
     AssertGraphCoherency(graph);
 
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 151c4ab38d22..8ba29ac96c83 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2038,7 +2038,7 @@ IonBuilder::restartLoop(CFGState state)
 {
     spew("New types at loop header, restarting loop body");
 
-    if (js_IonOptions.limitScriptSize) {
+    if (js_JitOptions.limitScriptSize) {
         if (++numLoopRestarts_ >= MAX_LOOP_RESTARTS)
             return ControlStatus_Abort;
     }
@@ -3438,7 +3438,7 @@ IonBuilder::jsop_try()
 {
     JS_ASSERT(JSOp(*pc) == JSOP_TRY);
 
-    if (!js_IonOptions.compileTryCatch)
+    if (!js_JitOptions.compileTryCatch)
         return abort("Try-catch support disabled");
 
     // Try-finally is not yet supported.
@@ -3993,7 +3993,7 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
     // Skip heuristics if we have an explicit hint to inline.
     if (!targetScript->shouldInline()) {
         // Cap the inlining depth.
-        if (js_IonOptions.isSmallFunction(targetScript)) {
+        if (js_JitOptions.isSmallFunction(targetScript)) {
             if (inliningDepth_ >= optimizationInfo().smallFunctionMaxInlineDepth())
                 return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
         } else {
diff --git a/js/src/jit/IonFrames.cpp b/js/src/jit/IonFrames.cpp
index cecde0dee5cc..66beb292251b 100644
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -1061,7 +1061,7 @@ static void
 MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations)
 {
 #ifdef CHECK_OSIPOINT_REGISTERS
-    if (js_IonOptions.checkOsiPointRegisters) {
+    if (js_JitOptions.checkOsiPointRegisters) {
         // GC can modify spilled registers, breaking our register checks.
         // To handle this, we disable these checks for the current VM call
         // when a GC happens.
diff --git a/js/src/jit/IonOptimizationLevels.h b/js/src/jit/IonOptimizationLevels.h
index 012a07968706..748f73794c49 100644
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -10,7 +10,7 @@
 #include "jsbytecode.h"
 #include "jstypes.h"
 
-#include "jit/IonOptions.h"
+#include "jit/JitOptions.h"
 #include "js/TypeDecls.h"
 
 namespace js {
@@ -99,41 +99,41 @@ class OptimizationInfo
     }
 
     bool inlineInterpreted() const {
-        return inlineInterpreted_ && !js_IonOptions.disableInlining;
+        return inlineInterpreted_ && !js_JitOptions.disableInlining;
     }
 
     bool inlineNative() const {
-        return inlineNative_ && !js_IonOptions.disableInlining;
+        return inlineNative_ && !js_JitOptions.disableInlining;
     }
 
     uint32_t usesBeforeCompile() const {
-        if (js_IonOptions.forceDefaultIonUsesBeforeCompile)
-            return js_IonOptions.forcedDefaultIonUsesBeforeCompile;
+        if (js_JitOptions.forceDefaultIonUsesBeforeCompile)
+            return js_JitOptions.forcedDefaultIonUsesBeforeCompile;
         return usesBeforeCompile_;
     }
 
     bool gvnEnabled() const {
-        return gvn_ && !js_IonOptions.disableGvn;
+        return gvn_ && !js_JitOptions.disableGvn;
     }
 
     bool licmEnabled() const {
-        return licm_ && !js_IonOptions.disableLicm;
+        return licm_ && !js_JitOptions.disableLicm;
     }
 
     bool uceEnabled() const {
-        return uce_ && !js_IonOptions.disableUce;
+        return uce_ && !js_JitOptions.disableUce;
     }
 
     bool rangeAnalysisEnabled() const {
-        return rangeAnalysis_ && !js_IonOptions.disableRangeAnalysis;
+        return rangeAnalysis_ && !js_JitOptions.disableRangeAnalysis;
     }
 
     bool eaaEnabled() const {
-        return eaa_ && !js_IonOptions.disableEaa;
+        return eaa_ && !js_JitOptions.disableEaa;
     }
 
     bool edgeCaseAnalysisEnabled() const {
-        return edgeCaseAnalysis_ && !js_IonOptions.disableEdgeCaseAnalysis;
+        return edgeCaseAnalysis_ && !js_JitOptions.disableEdgeCaseAnalysis;
     }
 
     bool eliminateRedundantChecksEnabled() const {
@@ -141,15 +141,15 @@ class OptimizationInfo
     }
 
     IonGvnKind gvnKind() const {
-        if (!js_IonOptions.forceGvnKind)
+        if (!js_JitOptions.forceGvnKind)
             return gvnKind_;
-        return js_IonOptions.forcedGvnKind;
+        return js_JitOptions.forcedGvnKind;
     }
 
     IonRegisterAllocator registerAllocator() const {
-        if (!js_IonOptions.forceRegisterAllocator)
+        if (!js_JitOptions.forceRegisterAllocator)
             return registerAllocator_;
-        return js_IonOptions.forcedRegisterAllocator;
+        return js_JitOptions.forcedRegisterAllocator;
     }
 
     uint32_t smallFunctionMaxInlineDepth() const {
diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp
index 3c3bf41d8dfb..a7d6dc1a6e67 100644
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -4,7 +4,9 @@
  * 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 "jit/IonOptions.h"
+#include "jit/JitOptions.h"
+
+#include "jsscript.h"
 
 using namespace js;
 using namespace js::jit;
@@ -12,9 +14,9 @@ using namespace js::jit;
 namespace js {
 namespace jit {
 
-IonOptions js_IonOptions;
+JitOptions js_JitOptions;
 
-IonOptions::IonOptions()
+JitOptions::JitOptions()
 {
     // Whether to perform expensive graph-consistency DEBUG-only assertions.
     // It can be useful to disable this to reduce DEBUG-compile time of large
@@ -115,13 +117,13 @@ IonOptions::IonOptions()
 }
 
 bool
-IonOptions::isSmallFunction(JSScript *script) const
+JitOptions::isSmallFunction(JSScript *script) const
 {
     return script->length() <= smallFunctionMaxBytecodeLength_;
 }
 
 void
-IonOptions::setEagerCompilation()
+JitOptions::setEagerCompilation()
 {
     eagerCompilation = true;
     baselineUsesBeforeCompile = 0;
@@ -130,27 +132,27 @@ IonOptions::setEagerCompilation()
 }
 
 void
-IonOptions::setUsesBeforeCompile(uint32_t useCount)
+JitOptions::setUsesBeforeCompile(uint32_t useCount)
 {
     forceDefaultIonUsesBeforeCompile = true;
     forcedDefaultIonUsesBeforeCompile = useCount;
 
     // Undo eager compilation
     if (eagerCompilation && useCount != 0) {
-        jit::IonOptions defaultValues;
+        jit::JitOptions defaultValues;
         eagerCompilation = false;
         baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile;
     }
 }
 
 void
-IonOptions::resetUsesBeforeCompile()
+JitOptions::resetUsesBeforeCompile()
 {
     forceDefaultIonUsesBeforeCompile = false;
 
     // Undo eager compilation
     if (eagerCompilation) {
-        jit::IonOptions defaultValues;
+        jit::JitOptions defaultValues;
         eagerCompilation = false;
         baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile;
     }
diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h
index 2f3d3d65cd18..4703e94f5768 100644
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -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/. */
 
-#ifndef jit_IonOptions_h
-#define jit_IonOptions_h
+#ifndef jit_JitOptions_h
+#define jit_JitOptions_h
 
 #include "jit/IonTypes.h"
+#include "js/TypeDecls.h"
 
 #ifdef JS_ION
 
@@ -26,7 +27,7 @@ enum IonGvnKind {
     GVN_Pessimistic
 };
 
-struct IonOptions
+struct JitOptions
 {
     bool checkGraphConsistency;
 #ifdef CHECK_OSIPOINT_REGISTERS
@@ -59,18 +60,18 @@ struct IonOptions
     uint32_t smallFunctionMaxBytecodeLength_;
     uint32_t usesBeforeCompilePar;
 
-    IonOptions();
+    JitOptions();
     bool isSmallFunction(JSScript *script) const;
     void setEagerCompilation();
     void setUsesBeforeCompile(uint32_t useCount);
     void resetUsesBeforeCompile();
 };
 
-extern IonOptions js_IonOptions;
+extern JitOptions js_JitOptions;
 
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
-#endif /* jit_IonOptions_h */
+#endif /* jit_JitOptions_h */
diff --git a/js/src/jit/LinearScan.cpp b/js/src/jit/LinearScan.cpp
index cd22f4d36818..154de5762109 100644
--- a/js/src/jit/LinearScan.cpp
+++ b/js/src/jit/LinearScan.cpp
@@ -1128,7 +1128,7 @@ LinearScanAllocator::canCoexist(LiveInterval *a, LiveInterval *b)
 void
 LinearScanAllocator::validateIntervals()
 {
-    if (!js_IonOptions.checkGraphConsistency)
+    if (!js_JitOptions.checkGraphConsistency)
         return;
 
     for (IntervalIterator i(active.begin()); i != active.end(); i++) {
@@ -1174,7 +1174,7 @@ LinearScanAllocator::validateIntervals()
 void
 LinearScanAllocator::validateAllocations()
 {
-    if (!js_IonOptions.checkGraphConsistency)
+    if (!js_JitOptions.checkGraphConsistency)
         return;
 
     for (IntervalIterator i(handled.begin()); i != handled.end(); i++) {
diff --git a/js/src/jit/LiveRangeAllocator.h b/js/src/jit/LiveRangeAllocator.h
index a469ebda4aeb..db8feecd035e 100644
--- a/js/src/jit/LiveRangeAllocator.h
+++ b/js/src/jit/LiveRangeAllocator.h
@@ -600,7 +600,7 @@ class LiveRangeAllocator : protected RegisterAllocator
     void validateVirtualRegisters()
     {
 #ifdef DEBUG
-        if (!js_IonOptions.checkGraphConsistency)
+        if (!js_JitOptions.checkGraphConsistency)
             return;
 
         for (size_t i = 1; i < graph.numVirtualRegisters(); i++) {
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index 5f65fe844778..7257a52951d6 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -523,7 +523,7 @@ LIRGenerator::visitAssertFloat32(MAssertFloat32 *assertion)
     if (!allowFloat32Optimizations())
         return true;
 
-    if (type != MIRType_Value && !js_IonOptions.eagerCompilation) {
+    if (type != MIRType_Value && !js_JitOptions.eagerCompilation) {
         JS_ASSERT_IF(checkIsFloat32, type == MIRType_Float32);
         JS_ASSERT_IF(!checkIsFloat32, type != MIRType_Float32);
     }
diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp
index a60f4ca555a1..3db801399761 100644
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2040,7 +2040,7 @@ RangeAnalysis::analyze()
 bool
 RangeAnalysis::addRangeAssertions()
 {
-    if (!js_IonOptions.checkRangeAnalysis)
+    if (!js_JitOptions.checkRangeAnalysis)
         return true;
 
     // Check the computed range for this instruction, if the option is set. Note
diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp
index bb6aecb62e36..879c06987db8 100644
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -603,7 +603,7 @@ CodeGeneratorShared::verifyOsiPointRegs(LSafepoint *safepoint)
 bool
 CodeGeneratorShared::shouldVerifyOsiPointRegs(LSafepoint *safepoint)
 {
-    if (!js_IonOptions.checkOsiPointRegisters)
+    if (!js_JitOptions.checkOsiPointRegisters)
         return false;
 
     if (gen->info().executionMode() != SequentialExecution)
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index c6739f38d2a7..2ad682aded64 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5984,19 +5984,19 @@ JS_SetGlobalJitCompilerOption(JSContext *cx, JSJitCompilerOption opt, uint32_t v
     switch (opt) {
       case JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER:
         if (value == uint32_t(-1)) {
-            jit::IonOptions defaultValues;
+            jit::JitOptions defaultValues;
             value = defaultValues.baselineUsesBeforeCompile;
         }
-        jit::js_IonOptions.baselineUsesBeforeCompile = value;
+        jit::js_JitOptions.baselineUsesBeforeCompile = value;
         break;
       case JSJITCOMPILER_ION_USECOUNT_TRIGGER:
         if (value == uint32_t(-1)) {
-            jit::js_IonOptions.resetUsesBeforeCompile();
+            jit::js_JitOptions.resetUsesBeforeCompile();
             break;
         }
-        jit::js_IonOptions.setUsesBeforeCompile(value);
+        jit::js_JitOptions.setUsesBeforeCompile(value);
         if (value == 0)
-            jit::js_IonOptions.setEagerCompilation();
+            jit::js_JitOptions.setEagerCompilation();
         break;
       case JSJITCOMPILER_ION_ENABLE:
         if (value == 1) {
diff --git a/js/src/moz.build b/js/src/moz.build
index 51468c1b5f5f..de817201df82 100644
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -258,8 +258,8 @@ if CONFIG['ENABLE_ION']:
         'jit/IonFrames.cpp',
         'jit/IonMacroAssembler.cpp',
         'jit/IonOptimizationLevels.cpp',
-        'jit/IonOptions.cpp',
         'jit/IonSpewer.cpp',
+        'jit/JitOptions.cpp',
         'jit/JSONSpewer.cpp',
         'jit/LICM.cpp',
         'jit/LinearScan.cpp',
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 7079590b3eac..8386af16dc35 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5441,13 +5441,13 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
 
     if (const char *str = op->getStringOption("ion-gvn")) {
         if (strcmp(str, "off") == 0) {
-            jit::js_IonOptions.disableGvn = true;
+            jit::js_JitOptions.disableGvn = true;
         } else if (strcmp(str, "pessimistic") == 0) {
-            jit::js_IonOptions.forceGvnKind = true;
-            jit::js_IonOptions.forcedGvnKind = jit::GVN_Pessimistic;
+            jit::js_JitOptions.forceGvnKind = true;
+            jit::js_JitOptions.forcedGvnKind = jit::GVN_Pessimistic;
         } else if (strcmp(str, "optimistic") == 0) {
-            jit::js_IonOptions.forceGvnKind = true;
-            jit::js_IonOptions.forcedGvnKind = jit::GVN_Optimistic;
+            jit::js_JitOptions.forceGvnKind = true;
+            jit::js_JitOptions.forcedGvnKind = jit::GVN_Optimistic;
         } else {
             return OptionFailure("ion-gvn", str);
         }
@@ -5455,95 +5455,95 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
 
     if (const char *str = op->getStringOption("ion-licm")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.disableLicm = false;
+            jit::js_JitOptions.disableLicm = false;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.disableLicm = true;
+            jit::js_JitOptions.disableLicm = true;
         else
             return OptionFailure("ion-licm", str);
     }
 
     if (const char *str = op->getStringOption("ion-edgecase-analysis")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.disableEdgeCaseAnalysis = false;
+            jit::js_JitOptions.disableEdgeCaseAnalysis = false;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.disableEdgeCaseAnalysis = true;
+            jit::js_JitOptions.disableEdgeCaseAnalysis = true;
         else
             return OptionFailure("ion-edgecase-analysis", str);
     }
 
      if (const char *str = op->getStringOption("ion-range-analysis")) {
          if (strcmp(str, "on") == 0)
-             jit::js_IonOptions.disableRangeAnalysis = false;
+             jit::js_JitOptions.disableRangeAnalysis = false;
          else if (strcmp(str, "off") == 0)
-             jit::js_IonOptions.disableRangeAnalysis = true;
+             jit::js_JitOptions.disableRangeAnalysis = true;
          else
              return OptionFailure("ion-range-analysis", str);
      }
 
     if (op->getBoolOption("ion-check-range-analysis"))
-        jit::js_IonOptions.checkRangeAnalysis = true;
+        jit::js_JitOptions.checkRangeAnalysis = true;
 
     if (op->getBoolOption("ion-check-thread-safety"))
-        jit::js_IonOptions.checkThreadSafety = true;
+        jit::js_JitOptions.checkThreadSafety = true;
 
     if (const char *str = op->getStringOption("ion-inlining")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.disableInlining = false;
+            jit::js_JitOptions.disableInlining = false;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.disableInlining = true;
+            jit::js_JitOptions.disableInlining = true;
         else
             return OptionFailure("ion-inlining", str);
     }
 
     if (const char *str = op->getStringOption("ion-osr")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.osr = true;
+            jit::js_JitOptions.osr = true;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.osr = false;
+            jit::js_JitOptions.osr = false;
         else
             return OptionFailure("ion-osr", str);
     }
 
     if (const char *str = op->getStringOption("ion-limit-script-size")) {
         if (strcmp(str, "on") == 0)
-            jit::js_IonOptions.limitScriptSize = true;
+            jit::js_JitOptions.limitScriptSize = true;
         else if (strcmp(str, "off") == 0)
-            jit::js_IonOptions.limitScriptSize = false;
+            jit::js_JitOptions.limitScriptSize = false;
         else
             return OptionFailure("ion-limit-script-size", str);
     }
 
     int32_t useCount = op->getIntOption("ion-uses-before-compile");
     if (useCount >= 0)
-        jit::js_IonOptions.setUsesBeforeCompile(useCount);
+        jit::js_JitOptions.setUsesBeforeCompile(useCount);
 
     useCount = op->getIntOption("baseline-uses-before-compile");
     if (useCount >= 0)
-        jit::js_IonOptions.baselineUsesBeforeCompile = useCount;
+        jit::js_JitOptions.baselineUsesBeforeCompile = useCount;
 
     if (op->getBoolOption("baseline-eager"))
-        jit::js_IonOptions.baselineUsesBeforeCompile = 0;
+        jit::js_JitOptions.baselineUsesBeforeCompile = 0;
 
     if (const char *str = op->getStringOption("ion-regalloc")) {
         if (strcmp(str, "lsra") == 0) {
-            jit::js_IonOptions.forceRegisterAllocator = true;
-            jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_LSRA;
+            jit::js_JitOptions.forceRegisterAllocator = true;
+            jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_LSRA;
         } else if (strcmp(str, "backtracking") == 0) {
-            jit::js_IonOptions.forceRegisterAllocator = true;
-            jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_Backtracking;
+            jit::js_JitOptions.forceRegisterAllocator = true;
+            jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_Backtracking;
         } else if (strcmp(str, "stupid") == 0) {
-            jit::js_IonOptions.forceRegisterAllocator = true;
-            jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_Stupid;
+            jit::js_JitOptions.forceRegisterAllocator = true;
+            jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_Stupid;
         } else {
             return OptionFailure("ion-regalloc", str);
         }
     }
 
     if (op->getBoolOption("ion-eager"))
-        jit::js_IonOptions.setEagerCompilation();
+        jit::js_JitOptions.setEagerCompilation();
 
     if (op->getBoolOption("ion-compile-try-catch"))
-        jit::js_IonOptions.compileTryCatch = true;
+        jit::js_JitOptions.compileTryCatch = true;
 
 #ifdef JS_THREADSAFE
     bool parallelCompilation = false;
diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp
index 1f2e41df78fe..83be1584e784 100644
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -2207,8 +2207,8 @@ js::ParallelTestsShouldPass(JSContext *cx)
 {
     return jit::IsIonEnabled(cx) &&
            jit::IsBaselineEnabled(cx) &&
-           !jit::js_IonOptions.eagerCompilation &&
-           jit::js_IonOptions.baselineUsesBeforeCompile != 0 &&
+           !jit::js_JitOptions.eagerCompilation &&
+           jit::js_JitOptions.baselineUsesBeforeCompile != 0 &&
            cx->runtime()->gcZeal() == 0;
 }
 

From 5c72865a1aa97bd4c6e9e47108dc95b4dca7f0ca Mon Sep 17 00:00:00 2001
From: Hannes Verschore 
Date: Thu, 12 Dec 2013 15:14:13 +0100
Subject: [PATCH 013/459] Bug 939614: IonMonkey: Use logic in UsesBeforeCompile
 to get actual needed usecount, r=jandem

---
 js/src/jit/BaselineCompiler.cpp      |  3 +-
 js/src/jit/Ion.cpp                   | 50 ++----------------------
 js/src/jit/Ion.h                     |  1 -
 js/src/jit/IonBuilder.cpp            |  3 +-
 js/src/jit/IonOptimizationLevels.cpp | 58 +++++++++++++++++++++-------
 js/src/jit/IonOptimizationLevels.h   | 20 +++++-----
 js/src/jit/JitOptions.h              | 11 ++++++
 7 files changed, 73 insertions(+), 73 deletions(-)

diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index 3e78fff94a01..c10a24191f7b 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -637,7 +637,8 @@ BaselineCompiler::emitUseCountIncrement()
 
     Label skipCall;
 
-    uint32_t minUses = UsesBeforeIonCompile(script, pc);
+    const OptimizationInfo *info = js_IonOptimizations.get(js_IonOptimizations.firstLevel());
+    uint32_t minUses = info->usesBeforeCompile(script, pc);
     masm.branch32(Assembler::LessThan, countReg, Imm32(minUses), &skipCall);
 
     masm.branchPtr(Assembler::Equal,
diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp
index 1bad578a6f59..f96cb7f39bb9 100644
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1802,17 +1802,6 @@ CheckScript(JSContext *cx, JSScript *script, bool osr)
     return true;
 }
 
-// Longer scripts can only be compiled off thread, as these compilations
-// can be expensive and stall the main thread for too long.
-static const uint32_t MAX_OFF_THREAD_SCRIPT_SIZE = 100 * 1000;
-static const uint32_t MAX_MAIN_THREAD_SCRIPT_SIZE = 2 * 1000;
-static const uint32_t MAX_MAIN_THREAD_LOCALS_AND_ARGS = 256;
-
-// DOM Worker runtimes don't have off thread compilation, but can also compile
-// larger scripts since this doesn't stall the main thread.
-static const uint32_t MAX_DOM_WORKER_SCRIPT_SIZE = 16 * 1000;
-static const uint32_t MAX_DOM_WORKER_LOCALS_AND_ARGS = 2048;
-
 static MethodStatus
 CheckScriptSize(JSContext *cx, JSScript* script)
 {
@@ -1876,14 +1865,14 @@ CanIonCompileScript(JSContext *cx, HandleScript script, bool osr)
 }
 
 static OptimizationLevel
-GetOptimizationLevel(HandleScript script, ExecutionMode executionMode)
+GetOptimizationLevel(HandleScript script, jsbytecode *pc, ExecutionMode executionMode)
 {
     if (executionMode == ParallelExecution)
         return Optimization_Normal;
 
     JS_ASSERT(executionMode == SequentialExecution);
 
-    return js_IonOptimizations.levelForUseCount(script->getUseCount());
+    return js_IonOptimizations.levelForScript(script, pc);
 }
 
 static MethodStatus
@@ -1916,7 +1905,7 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
     }
 
     bool recompile = false;
-    OptimizationLevel optimizationLevel = GetOptimizationLevel(script, executionMode);
+    OptimizationLevel optimizationLevel = GetOptimizationLevel(script, osrPc, executionMode);
     if (optimizationLevel == Optimization_DontCompile)
         return Method_Skipped;
 
@@ -2730,39 +2719,6 @@ jit::ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode)
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
-uint32_t
-jit::UsesBeforeIonCompile(JSScript *script, jsbytecode *pc)
-{
-    JS_ASSERT(pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY);
-
-    OptimizationLevel level = js_IonOptimizations.nextLevel(Optimization_DontCompile);
-    const OptimizationInfo *info = js_IonOptimizations.get(level);
-
-    uint32_t minUses = info->usesBeforeCompile();
-
-    // If the script is too large to compile on the main thread, we can still
-    // compile it off thread. In these cases, increase the use count threshold
-    // to improve the compilation's type information and hopefully avoid later
-    // recompilation.
-
-    if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE)
-        minUses = minUses * (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE);
-
-    uint32_t numLocalsAndArgs = analyze::TotalSlots(script);
-    if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
-        minUses = minUses * (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS);
-
-    if (JSOp(*pc) != JSOP_LOOPENTRY || js_JitOptions.eagerCompilation)
-        return minUses;
-
-    // It's more efficient to enter outer loops, rather than inner loops, via OSR.
-    // To accomplish this, we use a slightly higher threshold for inner loops.
-    // Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
-    uint32_t loopDepth = GET_UINT8(pc);
-    JS_ASSERT(loopDepth > 0);
-    return minUses + loopDepth * 100;
-}
-
 void
 AutoFlushCache::updateTop(uintptr_t p, size_t len)
 {
diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h
index 16b588ba0dc6..f516f1e12698 100644
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -179,7 +179,6 @@ TooManyArguments(unsigned nargs)
 
 void ForbidCompilation(JSContext *cx, JSScript *script);
 void ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode);
-uint32_t UsesBeforeIonCompile(JSScript *script, jsbytecode *pc);
 
 void PurgeCaches(JSScript *script, JS::Zone *zone);
 size_t SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf);
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 8ba29ac96c83..1a1bd7d6baee 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6013,7 +6013,8 @@ IonBuilder::insertRecompileCheck()
     // of the next optimization level.
     OptimizationLevel nextLevel = js_IonOptimizations.nextLevel(curLevel);
     const OptimizationInfo *info = js_IonOptimizations.get(nextLevel);
-    current->add(MRecompileCheck::New(alloc(), topBuilder->script(), info->usesBeforeCompile()));
+    uint32_t useCount = info->usesBeforeCompile(topBuilder->script());
+    current->add(MRecompileCheck::New(alloc(), topBuilder->script(), useCount));
 }
 
 JSObject *
diff --git a/js/src/jit/IonOptimizationLevels.cpp b/js/src/jit/IonOptimizationLevels.cpp
index e6679c8ccbf7..cd2009d03acd 100644
--- a/js/src/jit/IonOptimizationLevels.cpp
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -56,23 +56,49 @@ OptimizationInfo::initAsmjsOptimizationInfo()
     eliminateRedundantChecks_ = false;
 }
 
+uint32_t
+OptimizationInfo::usesBeforeCompile(JSScript *script, jsbytecode *pc) const
+{
+    JS_ASSERT(pc == nullptr || pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY);
+
+    if (pc == script->code())
+        pc = nullptr;
+
+    uint32_t minUses = usesBeforeCompile_;
+    if (js_JitOptions.forceDefaultIonUsesBeforeCompile)
+        minUses = js_JitOptions.forcedDefaultIonUsesBeforeCompile;
+
+    // If the script is too large to compile on the main thread, we can still
+    // compile it off thread. In these cases, increase the use count threshold
+    // to improve the compilation's type information and hopefully avoid later
+    // recompilation.
+
+    if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE)
+        minUses = minUses * (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE);
+
+    uint32_t numLocalsAndArgs = analyze::TotalSlots(script);
+    if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
+        minUses = minUses * (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS);
+
+    if (!pc || js_JitOptions.eagerCompilation)
+        return minUses;
+
+    // It's more efficient to enter outer loops, rather than inner loops, via OSR.
+    // To accomplish this, we use a slightly higher threshold for inner loops.
+    // Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
+    uint32_t loopDepth = GET_UINT8(pc);
+    JS_ASSERT(loopDepth > 0);
+    return minUses + loopDepth * 100;
+}
+
 OptimizationInfos::OptimizationInfos()
 {
     infos_[Optimization_Normal - 1].initNormalOptimizationInfo();
     infos_[Optimization_AsmJS - 1].initAsmjsOptimizationInfo();
-
-#ifdef DEBUG
-    OptimizationLevel prev = nextLevel(Optimization_DontCompile);
-    while (!isLastLevel(prev)) {
-        OptimizationLevel next = nextLevel(prev);
-        JS_ASSERT(get(prev)->usesBeforeCompile() < get(next)->usesBeforeCompile());
-        prev = next;
-    }
-#endif
 }
 
 OptimizationLevel
-OptimizationInfos::nextLevel(OptimizationLevel level)
+OptimizationInfos::nextLevel(OptimizationLevel level) const
 {
     JS_ASSERT(!isLastLevel(level));
     switch (level) {
@@ -83,21 +109,27 @@ OptimizationInfos::nextLevel(OptimizationLevel level)
     }
 }
 
+OptimizationLevel
+OptimizationInfos::firstLevel() const
+{
+    return nextLevel(Optimization_DontCompile);
+}
+
 bool
-OptimizationInfos::isLastLevel(OptimizationLevel level)
+OptimizationInfos::isLastLevel(OptimizationLevel level) const
 {
     return level == Optimization_Normal;
 }
 
 OptimizationLevel
-OptimizationInfos::levelForUseCount(uint32_t useCount)
+OptimizationInfos::levelForScript(JSScript *script, jsbytecode *pc) const
 {
     OptimizationLevel prev = Optimization_DontCompile;
 
     while (!isLastLevel(prev)) {
         OptimizationLevel level = nextLevel(prev);
         const OptimizationInfo *info = get(level);
-        if (useCount < info->usesBeforeCompile())
+        if (script->getUseCount() < info->usesBeforeCompile(script, pc))
             return prev;
 
         prev = level;
diff --git a/js/src/jit/IonOptimizationLevels.h b/js/src/jit/IonOptimizationLevels.h
index 748f73794c49..fc37b652d2cd 100644
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -106,11 +106,7 @@ class OptimizationInfo
         return inlineNative_ && !js_JitOptions.disableInlining;
     }
 
-    uint32_t usesBeforeCompile() const {
-        if (js_JitOptions.forceDefaultIonUsesBeforeCompile)
-            return js_JitOptions.forcedDefaultIonUsesBeforeCompile;
-        return usesBeforeCompile_;
-    }
+    uint32_t usesBeforeCompile(JSScript *script, jsbytecode *pc = nullptr) const;
 
     bool gvnEnabled() const {
         return gvn_ && !js_JitOptions.disableGvn;
@@ -171,7 +167,10 @@ class OptimizationInfo
     }
 
     uint32_t usesBeforeInlining() const {
-        return usesBeforeCompile() * usesBeforeInliningFactor_;
+        uint32_t usesBeforeCompile = usesBeforeCompile_;
+        if (js_JitOptions.forceDefaultIonUsesBeforeCompile)
+            usesBeforeCompile = js_JitOptions.forcedDefaultIonUsesBeforeCompile;
+        return usesBeforeCompile * usesBeforeInliningFactor_;
     }
 };
 
@@ -183,16 +182,17 @@ class OptimizationInfos
   public:
     OptimizationInfos();
 
-    const OptimizationInfo *get(OptimizationLevel level) {
+    const OptimizationInfo *get(OptimizationLevel level) const {
         JS_ASSERT(level < Optimization_Count);
         JS_ASSERT(level != Optimization_DontCompile);
 
         return &infos_[level - 1];
     }
 
-    OptimizationLevel nextLevel(OptimizationLevel level);
-    bool isLastLevel(OptimizationLevel level);
-    OptimizationLevel levelForUseCount(uint32_t useCount);
+    OptimizationLevel nextLevel(OptimizationLevel level) const;
+    OptimizationLevel firstLevel() const;
+    bool isLastLevel(OptimizationLevel level) const;
+    OptimizationLevel levelForScript(JSScript *script, jsbytecode *pc = nullptr) const;
 };
 
 extern OptimizationInfos js_IonOptimizations;
diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h
index 4703e94f5768..a6f3d5843063 100644
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -15,6 +15,17 @@
 namespace js {
 namespace jit {
 
+// Longer scripts can only be compiled off thread, as these compilations
+// can be expensive and stall the main thread for too long.
+static const uint32_t MAX_OFF_THREAD_SCRIPT_SIZE = 100 * 1000;
+static const uint32_t MAX_MAIN_THREAD_SCRIPT_SIZE = 2 * 1000;
+static const uint32_t MAX_MAIN_THREAD_LOCALS_AND_ARGS = 256;
+
+// DOM Worker runtimes don't have off thread compilation, but can also compile
+// larger scripts since this doesn't stall the main thread.
+static const uint32_t MAX_DOM_WORKER_SCRIPT_SIZE = 16 * 1000;
+static const uint32_t MAX_DOM_WORKER_LOCALS_AND_ARGS = 2048;
+
 // Possible register allocators which may be used.
 enum IonRegisterAllocator {
     RegisterAllocator_LSRA,

From c1566d3c2c5651572d53957bc21ba4a016225b30 Mon Sep 17 00:00:00 2001
From: Hannes Verschore 
Date: Thu, 12 Dec 2013 15:14:13 +0100
Subject: [PATCH 014/459] Bug 939614: IonMonkey: Enable reschedule same
 optimizationLevel if osrPc is different, r=jandem

---
 js/src/jit/Ion.cpp | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp
index f96cb7f39bb9..92a1e4aed529 100644
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1726,15 +1726,15 @@ IonCompile(JSContext *cx, JSScript *script,
         return builder->abortReason();
     }
 
+    if (recompile) {
+        JS_ASSERT(executionMode == SequentialExecution);
+        builderScript->ionScript()->setRecompiling();
+    } else {
+        SetIonScript(builder->script(), executionMode, ION_COMPILING_SCRIPT);
+    }
+
     // If possible, compile the script off thread.
     if (OffThreadCompilationAvailable(cx)) {
-        if (recompile) {
-            JS_ASSERT(executionMode == SequentialExecution);
-            builderScript->ionScript()->setRecompiling();
-        } else {
-            SetIonScript(builder->script(), executionMode, ION_COMPILING_SCRIPT);
-        }
-
         if (!StartOffThreadIonCompile(cx, builder)) {
             IonSpew(IonSpew_Abort, "Unable to start off-thread ion compilation.");
             return AbortReason_Alloc;
@@ -1928,9 +1928,15 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
 
         // Don't recompile/overwrite higher optimized code,
         // with a lower optimization level.
-        if (optimizationLevel <= scriptIon->optimizationLevel())
+        if (optimizationLevel < scriptIon->optimizationLevel())
             return failedState;
 
+        if (optimizationLevel == scriptIon->optimizationLevel() &&
+            (!osrPc || script->ionScript()->osrPc() == osrPc))
+        {
+            return failedState;
+        }
+
         // Don't start compiling if already compiling
         if (scriptIon->isRecompiling())
             return failedState;

From 4cd98a6cc14f46d95dd9245288a8345007548f8b Mon Sep 17 00:00:00 2001
From: Hannes Verschore 
Date: Thu, 12 Dec 2013 15:14:13 +0100
Subject: [PATCH 015/459] Bug 939614: IonMonkey: Properly spew recompiling
 instead of analyzing, r=jandem

---
 js/src/jit/IonBuilder.cpp          | 13 +++++++++++--
 js/src/jit/IonOptimizationLevels.h | 17 +++++++++++++++++
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 1a1bd7d6baee..24eeae4a238e 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -609,8 +609,17 @@ IonBuilder::build()
     if (!current)
         return false;
 
-    IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d)",
-            script()->filename(), script()->lineno(), (void *)script(), (int)script()->getUseCount());
+#ifdef DEBUG
+    if (info().executionMode() == SequentialExecution && script()->hasIonScript()) {
+        IonSpew(IonSpew_Scripts, "Recompiling script %s:%d (%p) (usecount=%d, level=%s)",
+                script()->filename(), script()->lineno(), (void *)script(),
+                (int)script()->getUseCount(), OptimizationLevelString(optimizationInfo().level()));
+    } else {
+        IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d, level=%s)",
+                script()->filename(), script()->lineno(), (void *)script(),
+                (int)script()->getUseCount(), OptimizationLevelString(optimizationInfo().level()));
+    }
+#endif
 
     if (!initParameters())
         return false;
diff --git a/js/src/jit/IonOptimizationLevels.h b/js/src/jit/IonOptimizationLevels.h
index fc37b652d2cd..b939bfbfdd30 100644
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -24,6 +24,23 @@ enum OptimizationLevel
     Optimization_Count
 };
 
+#ifdef DEBUG
+inline const char *
+OptimizationLevelString(OptimizationLevel level)
+{
+    switch (level) {
+      case Optimization_DontCompile:
+        return "Optimization_DontCompile";
+      case Optimization_Normal:
+        return "Optimization_Normal";
+      case Optimization_AsmJS:
+        return "Optimization_AsmJS";
+      default:
+        MOZ_ASSUME_UNREACHABLE("Invalid OptimizationLevel");
+    }
+}
+#endif
+
 class OptimizationInfo
 {
   public:

From 50418a12fcdffd44b438886f26aed17ba2652d46 Mon Sep 17 00:00:00 2001
From: "Nicolas B. Pierron" 
Date: Thu, 12 Dec 2013 06:35:00 -0800
Subject: [PATCH 016/459] Bug 948992 - Copy the deviceSerial to ADB's device
 manager. r=jgriffin

---
 testing/marionette/client/marionette/runner/base.py      | 9 ++++++---
 .../marionette/client/marionette/runner/mixins/b2g.py    | 8 +++++---
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/testing/marionette/client/marionette/runner/base.py b/testing/marionette/client/marionette/runner/base.py
index 206811ec13e9..b98f4650c314 100644
--- a/testing/marionette/client/marionette/runner/base.py
+++ b/testing/marionette/client/marionette/runner/base.py
@@ -634,12 +634,14 @@ class BaseMarionetteTestRunner(object):
                                              logcat_dir=self.logcat_dir,
                                              gecko_path=self.gecko_path,
                                              symbols_path=self.symbols_path,
-                                             timeout=self.timeout)
+                                             timeout=self.timeout,
+                                             device_serial=self.device_serial)
             else:
                 self.marionette = Marionette(host=host,
                                              port=int(port),
                                              baseurl=self.baseurl,
-                                             timeout=self.timeout)
+                                             timeout=self.timeout,
+                                             device_serial=self.device_serial)
         elif self.emulator:
             self.marionette = Marionette.getMarionetteOrExit(
                                          emulator=self.emulator,
@@ -653,7 +655,8 @@ class BaseMarionetteTestRunner(object):
                                          gecko_path=self.gecko_path,
                                          symbols_path=self.symbols_path,
                                          timeout=self.timeout,
-                                         sdcard=self.sdcard)
+                                         sdcard=self.sdcard,
+                                         device_serial=self.device_serial)
         else:
             raise Exception("must specify binary, address or emulator")
 
diff --git a/testing/marionette/client/marionette/runner/mixins/b2g.py b/testing/marionette/client/marionette/runner/mixins/b2g.py
index 8cbe388d152c..9a48cf267e9d 100644
--- a/testing/marionette/client/marionette/runner/mixins/b2g.py
+++ b/testing/marionette/client/marionette/runner/mixins/b2g.py
@@ -12,12 +12,11 @@ class B2GTestCaseMixin(object):
     def __init__(self, *args, **kwargs):
         self._device_manager = None
 
-    @property
-    def device_manager(self, *args, **kwargs):
+    def get_device_manager(self, *args, **kwargs):
         if not self._device_manager:
             dm_type = os.environ.get('DM_TRANS', 'adb')
             if dm_type == 'adb':
-                self._device_manager = mozdevice.DeviceManagerADB()
+                self._device_manager = mozdevice.DeviceManagerADB(**kwargs)
             elif dm_type == 'sut':
                 host = os.environ.get('TEST_DEVICE')
                 if not host:
@@ -27,3 +26,6 @@ class B2GTestCaseMixin(object):
                 raise Exception('Unknown device manager type: %s' % dm_type)
         return self._device_manager
 
+    @property
+    def device_manager(self):
+        return self.get_device_manager()

From 4b94156b002352ac3c222266b6234db1037ac8fe Mon Sep 17 00:00:00 2001
From: Mike Hommey 
Date: Thu, 12 Dec 2013 23:41:05 +0900
Subject: [PATCH 017/459] Bug 778236 - Treat gyp files as if their content was
 defined in moz.build files. r=gps

---
 build/autoconf/arch.m4                        |   1 +
 build/gyp.mozbuild                            | 109 ++++
 build/virtualenv_packages.txt                 |   1 +
 config/config.mk                              |  27 +-
 configure.in                                  | 184 +------
 js/src/build/autoconf/arch.m4                 |   1 +
 js/src/config/config.mk                       |  27 +-
 media/mtransport/third_party/moz.build        |  13 +-
 media/mtransport/third_party/nICEr/nicer.gyp  |   3 -
 .../third_party/nrappkit/nrappkit.gyp         |   3 -
 media/webrtc/moz.build                        |  33 +-
 .../tools/gyp/pylib/gyp/generator/mozmake.py  | 485 ------------------
 .../webrtc/trunk/tools/gyp/pylib/gyp/input.py |   4 +
 .../audio_coding/codecs/opus/opus.gypi        |   2 +-
 .../modules/audio_device/audio_device.gypi    |  11 +-
 media/webrtc/webrtc_config.gypi               |  54 --
 .../mozbuild/backend/recursivemake.py         |   1 -
 python/mozbuild/mozbuild/frontend/emitter.py  |  11 +-
 .../mozbuild/mozbuild/frontend/gyp_reader.py  | 190 +++++++
 python/mozbuild/mozbuild/frontend/reader.py   |  53 +-
 .../mozbuild/frontend/sandbox_symbols.py      |  25 +
 .../test/backend/test_recursivemake.py        |   2 -
 22 files changed, 478 insertions(+), 762 deletions(-)
 create mode 100644 build/gyp.mozbuild
 delete mode 100644 media/webrtc/trunk/tools/gyp/pylib/gyp/generator/mozmake.py
 delete mode 100644 media/webrtc/webrtc_config.gypi
 create mode 100644 python/mozbuild/mozbuild/frontend/gyp_reader.py

diff --git a/build/autoconf/arch.m4 b/build/autoconf/arch.m4
index a351ac16bb9d..7c64ab9db339 100644
--- a/build/autoconf/arch.m4
+++ b/build/autoconf/arch.m4
@@ -247,6 +247,7 @@ fi # CPU_ARCH = arm
 AC_SUBST(HAVE_ARM_SIMD)
 AC_SUBST(HAVE_ARM_NEON)
 AC_SUBST(BUILD_ARM_NEON)
+AC_SUBST(ARM_ARCH)
 
 if test -n "$MOZ_ARCH"; then
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-arch=$MOZ_ARCH"
diff --git a/build/gyp.mozbuild b/build/gyp.mozbuild
new file mode 100644
index 000000000000..5c00df5c30c5
--- /dev/null
+++ b/build/gyp.mozbuild
@@ -0,0 +1,109 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+gyp_vars = {
+    'build_with_mozilla': 1,
+    'build_with_chromium': 0,
+    'have_clock_monotonic': 1 if CONFIG['HAVE_CLOCK_MONOTONIC'] else 0,
+    'have_ethtool_cmd_speed_hi': 1 if CONFIG['MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI'] else 0,
+    'include_alsa_audio': 1 if CONFIG['MOZ_ALSA'] else 0,
+    'include_pulse_audio': 1 if CONFIG['MOZ_PULSEAUDIO'] else 0,
+    # basic stuff for everything
+    'include_internal_video_render': 0,
+    'clang_use_chrome_plugins': 0,
+    'enable_protobuf': 0,
+    'include_tests': 0,
+    'enable_android_opensl': 1,
+    # use_system_lib* still seems to be in use in trunk/build
+    'use_system_libjpeg': 0,
+    'use_system_libvpx': 0,
+    'build_libjpeg': 0,
+    'build_libvpx': 0,
+    # saves 4MB when webrtc_trace is off
+    'enable_lazy_trace_alloc': 1,
+
+     # turn off mandatory use of NEON and instead use NEON detection
+    'arm_neon': 0,
+
+    'moz_widget_toolkit_gonk': 0,
+
+    # (for vp8) chromium sets to 0 also
+    'use_temporal_layers': 0,
+    # Creates AEC internal sample dump files in current directory
+    # 'aec_debug_dump': 1,
+
+    # codec enable/disables:
+    # Note: if you change one here, you must modify layout/media/webrtc/Makefile.in!
+    'include_g711': 1,
+    'include_opus': 1,
+    'include_g722': 0,
+    'include_ilbc': 0,
+    'include_isac': 0,
+    'include_pcm16b': 1,
+}
+
+os = CONFIG['OS_TARGET']
+
+if os == 'WINNT':
+    gyp_vars.update(
+        MSVS_VERSION=CONFIG['_MSVS_VERSION'],
+        MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_OS'] else 32,
+    )
+elif os == 'Android':
+    if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+        gyp_vars['build_with_gonk'] = 1
+        gyp_vars['moz_widget_toolkit_gonk'] = 1
+    else:
+        gyp_vars.update(
+            gtest_target_type='executable',
+            android_toolchain=CONFIG['ANDROID_TOOLCHAIN'],
+        )
+
+flavors = {
+    'WINNT': 'win',
+    'Android': 'linux' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' else 'android',
+    'Linux': 'linux',
+    'Darwin': 'mac' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' else 'ios',
+    'SunOS': 'solaris',
+    'GNU_kFreeBSD': 'freebsd',
+    'DragonFly': 'dragonfly',
+    'FreeBSD': 'freebsd',
+    'NetBSD': 'netbsd',
+    'OpenBSD': 'openbsd',
+}
+gyp_vars['OS'] = flavors[os]
+
+arches = {
+    'x86_64': 'x64',
+    'arm': 'arm',
+    'x86': 'ia32',
+    'ppc': 'ppc',
+    'ppc64': 'ppc',
+    'ia64': 'ia64',
+}
+
+gyp_vars['target_arch'] = arches[CONFIG['CPU_ARCH']]
+
+if CONFIG['ARM_ARCH']:
+    # We currently don't have a way to convert a string to an int in moz.build.
+    # As of writing, ARM_ARCH is not going to be over 8, so a string comparison
+    # works.
+    if CONFIG['ARM_ARCH'] < '7':
+        gyp_vars['armv7'] = 0
+    elif os == 'Android':
+        gyp_vars['armv7'] = 1
+    else:
+        # CPU detection for ARM works on Android only.  armv7 always uses CPU
+        # detection, so we have to set armv7=0 for non-Android target
+        gyp_vars['armv7'] = 0
+
+# Don't try to compile ssse3/sse4.1 code if toolchain doesn't support
+if CONFIG['INTEL_ARCHITECTURE']:
+    if not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSSE3'] or not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSE4_1']:
+        gyp_vars['yuv_disable_asm'] = 1
+
+if CONFIG['MACOS_SDK_DIR']:
+    gyp_vars['mac_sdk_path'] = CONFIG['MACOS_SDK_DIR']
diff --git a/build/virtualenv_packages.txt b/build/virtualenv_packages.txt
index 056423aa3f6e..aade8e7598e3 100644
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -17,3 +17,4 @@ moztreedocs.pth:tools/docs
 copy:build/buildconfig.py
 packages.txt:testing/mozbase/packages.txt
 objdir:build
+gyp.pth:media/webrtc/trunk/tools/gyp/pylib
diff --git a/config/config.mk b/config/config.mk
index 2c1903172a22..379eb9aa9141 100644
--- a/config/config.mk
+++ b/config/config.mk
@@ -587,10 +587,11 @@ OS_COMPILE_CMMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch
 endif
 endif
 
-COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS)
-COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS)
-COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS)
-COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS)
+COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(EXTRA_COMPILE_FLAGS)
+ASFLAGS += $(EXTRA_ASSEMBLER_FLAGS)
 
 ifndef CROSS_COMPILE
 HOST_CFLAGS += $(RTL_FLAGS)
@@ -884,3 +885,21 @@ MOZ_GTK2_CFLAGS := -I$(topsrcdir)/widget/gtk/compat $(MOZ_GTK2_CFLAGS)
 endif
 
 DEFINES += -DNO_NSPR_10_SUPPORT
+
+ifdef IS_GYP_DIR
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/ipc/chromium/src \
+  -I$(topsrcdir)/ipc/glue \
+  -I$(DEPTH)/ipc/ipdl/_ipdlheaders \
+  $(NULL)
+
+ifeq (WINNT,$(OS_TARGET))
+# These get set via VC project file settings for normal GYP builds.
+DEFINES += -DUNICODE -D_UNICODE
+LOCAL_INCLUDES += -I'$(MOZ_DIRECTX_SDK_PATH)/include'
+endif
+
+STL_FLAGS=
+# Skip most Mozilla-specific include locations.
+INCLUDES = -I. $(LOCAL_INCLUDES) -I$(DEPTH)/dist/include
+endif
diff --git a/configure.in b/configure.in
index 42222f74dc22..60df11849621 100644
--- a/configure.in
+++ b/configure.in
@@ -456,16 +456,17 @@ case "$target" in
 
         if test "$_CC_MAJOR_VERSION" = "16"; then
             _CC_SUITE=10
-            _MSVS_VERSION=2010
+            MSVS_VERSION=2010
         elif test "$_CC_MAJOR_VERSION" = "17"; then
             _CC_SUITE=11
-            _MSVS_VERSION=2012
+            MSVS_VERSION=2012
         elif test "$_CC_MAJOR_VERSION" = "18"; then
             _CC_SUITE=12
-            _MSVS_VERSION=2013
+            MSVS_VERSION=2013
         else
             AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
         fi
+	AC_SUBST(MSVS_VERSION)
 
         AC_DEFINE(HAVE_SEH_EXCEPTIONS)
 
@@ -5068,29 +5069,15 @@ AC_TRY_COMPILE([#include ],
                [ struct ethtool_cmd cmd; cmd.speed_hi = 0; ],
                MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI=1)
 
+AC_SUBST(MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI)
+
 # target_arch is from {ia32|x64|arm|ppc}
 case "$CPU_ARCH" in
-x86_64)
-    WEBRTC_TARGET_ARCH=x64
-    ;;
-
-arm*)
-    WEBRTC_TARGET_ARCH=arm
-    ;;
-
-x86)
-    WEBRTC_TARGET_ARCH=ia32
-    ;;
-
-ppc*)
-    WEBRTC_TARGET_ARCH=ppc
-    ;;
-ia64)
-    WEBRTC_TARGET_ARCH=ia64
+x86_64 | arm | x86 | ppc* | ia64)
+    :
     ;;
 *)
 # unsupported arch for webrtc
-    WEBRTC_TARGET_ARCH=unknown
     MOZ_WEBRTC=
     ;;
 
@@ -8838,161 +8825,6 @@ case "$host" in
     ;;
 esac
 
-# Generate Makefiles for WebRTC directly from .gyp files
-if test "${OS_TARGET}" = "WINNT"; then
-   if test "$HAVE_64BIT_OS"; then
-      OS_BITS=64
-   else
-      OS_BITS=32
-   fi
-   EXTRA_GYP_DEFINES="-D MSVS_VERSION=${_MSVS_VERSION} -D MSVS_OS_BITS=${OS_BITS}"
-
-elif test "${OS_TARGET}" = "Android"; then
-   if test "${MOZ_WIDGET_TOOLKIT}" = "gonk"; then
-      EXTRA_GYP_DEFINES="-G os=linux "
-   else
-      EXTRA_GYP_DEFINES="-D gtest_target_type=executable -D android_toolchain=${android_toolchain} -G os=android "
-   fi
-fi
-
-if test -n "$ARM_ARCH"; then
-    if test "$ARM_ARCH" -lt 7; then
-        EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 "
-    else
-        if test "${OS_TARGET}" = "Android"; then
-            EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=1 "
-        else
-            dnl CPU detection for ARM works on Android only.  armv7 always uses CPU detection, so
-            dnl we have to set armv7=0 for non-Android target
-            EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 "
-        fi
-    fi
-fi
-
-# Keep libcubeb and audio_device backends in sync
-if test -n "$MOZ_ALSA"; then
-   EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D include_alsa_audio=1"
-else
-   EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D include_alsa_audio=0"
-fi
-if test -n "$MOZ_PULSEAUDIO"; then
-   EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D include_pulse_audio=1"
-else
-   EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D include_pulse_audio=0"
-fi
-
-# Don't try to compile ssse3/sse4.1 code if toolchain doesn't support
-if test -n "$INTEL_ARCHITECTURE"; then
-  if test -z "$HAVE_TOOLCHAIN_SUPPORT_MSSSE3" -o -z "$HAVE_TOOLCHAIN_SUPPORT_MSSE4_1"; then
-    EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D yuv_disable_asm=1"
-  fi
-fi
-
-if test -n "$MOZ_WEBRTC"; then
-   AC_MSG_RESULT("generating WebRTC Makefiles...")
-
-   if test "${MOZ_WIDGET_TOOLKIT}" = "gonk"; then
-      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D build_with_gonk=1"
-   fi
-
-dnl Any --include files must also appear in -D FORCED_INCLUDE_FILE= entries
-dnl so that regeneration via dependencies works correctly
-   WEBRTC_CONFIG="-D build_with_mozilla=1 -D build_with_chromium=0 --include ${srcdir}/media/webrtc/webrtc_config.gypi -D FORCED_INCLUDE_FILE=${srcdir}/media/webrtc/webrtc_config.gypi"
-
-   if test -n HAVE_CLOCK_MONOTONIC; then
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D have_clock_monotonic=1"
-   else
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D have_clock_monotonic=0"
-   fi
-
-   if test -n "$MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI"; then
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D have_ethtool_cmd_speed_hi=1"
-   else
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D have_ethtool_cmd_speed_hi=0"
-   fi
-
-   if test -n "$CROSS_COMPILE"; then
-      case "$target" in
-      *-mingw*)
-      	GYP_MOZMAKE_OPTIONS="-G os=win"
-        ;;
-      *-darwin*)
-        GYP_MOZMAKE_OPTIONS="-G os=mac"
-        if test "$MACOS_SDK_DIR"; then
-           GYP_MOZMAKE_OPTIONS="${GYP_MOZMAKE_OPTIONS} -D mac_sdk_path=$MACOS_SDK_DIR"
-        fi
-        ;;
-      *-*linux*)
-        GYP_MOZMAKE_OPTIONS="-G os=linux"
-        ;;
-      *)
-        AC_MSG_ERROR([Don't know what options to give to WebRTC for cross-compilation])
-    	;;
-      esac
-   fi
-
-   GYP_WEBRTC_OPTIONS="--format=mozmake ${GYP_MOZMAKE_OPTIONS} ${WEBRTC_CONFIG} -D target_arch=${WEBRTC_TARGET_ARCH} ${EXTRA_GYP_DEFINES} --depth=${srcdir}/media/webrtc/trunk --toplevel-dir=${srcdir} -G OBJDIR=${_objdir}"
-
-   $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium \
-     $GYP_WEBRTC_OPTIONS \
-     --generator-output=${_objdir}/media/webrtc/trunk \
-     ${srcdir}/media/webrtc/trunk/peerconnection.gyp
-   if test "$?" != 0; then
-      AC_MSG_ERROR([failed to generate WebRTC Makefiles])
-   fi
-
-   # XXX disable until we land the tranche with signaling
-   if test -n "$MOZ_WEBRTC_SIGNALING"; then
-     AC_MSG_RESULT("generating WebRTC/Signaling Makefiles...")
-     $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium \
-       $GYP_WEBRTC_OPTIONS \
-       -D build_for_test=0 \
-       --generator-output=${_objdir}/media/webrtc/signaling \
-       ${srcdir}/media/webrtc/signaling/signaling.gyp
-     if test "$?" != 0; then
-        AC_MSG_ERROR([failed to generate WebRTC/Signaling Makefiles])
-     fi
-
-     AC_MSG_RESULT("generating WebRTC/SignalingTest Makefiles...")
-     $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium --format=mozmake \
-       $GYP_WEBRTC_OPTIONS \
-       -D build_for_test=1 \
-       --generator-output=${_objdir}/media/webrtc/signalingtest \
-       ${srcdir}/media/webrtc/signaling/signaling.gyp
-     if test "$?" != 0; then
-       AC_MSG_ERROR([failed to generate WebRTC/SignalingTest Makefiles])
-     fi
-   fi
-
-   AC_MSG_RESULT("generating gtest Makefiles...")
-   # Ok to pass some extra -D's that are ignored here
-   $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium \
-     $GYP_WEBRTC_OPTIONS \
-     --generator-output=${_objdir}/media/webrtc/trunk/testing/ \
-     ${srcdir}/media/webrtc/trunk/testing/gtest.gyp
-   if test "$?" != 0; then
-      AC_MSG_ERROR([failed to generate gtest Makefiles])
-   fi
-
-   AC_MSG_RESULT("generating nrappkit Makefiles...")
-   $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium --format=mozmake \
-     $GYP_WEBRTC_OPTIONS \
-     --generator-output=${_objdir}/media/mtransport/third_party/nrappkit \
-     ${srcdir}/media/mtransport/third_party/nrappkit/nrappkit.gyp
-   if test "$?" != 0; then
-      AC_MSG_ERROR([failed to generate nrappkit Makefiles])
-   fi
-
-   AC_MSG_RESULT("generating nICEr Makefiles...")
-   $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium --format=mozmake \
-     $GYP_WEBRTC_OPTIONS \
-     --generator-output=${_objdir}/media/mtransport/third_party/nICEr \
-     ${srcdir}/media/mtransport/third_party/nICEr/nicer.gyp
-   if test "$?" != 0; then
-      AC_MSG_ERROR([failed to generate nICEr Makefiles])
-   fi
-fi
-
 # Run jemalloc configure script
 
 if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -o -n "$MOZ_REPLACE_MALLOC"; then
diff --git a/js/src/build/autoconf/arch.m4 b/js/src/build/autoconf/arch.m4
index a351ac16bb9d..7c64ab9db339 100644
--- a/js/src/build/autoconf/arch.m4
+++ b/js/src/build/autoconf/arch.m4
@@ -247,6 +247,7 @@ fi # CPU_ARCH = arm
 AC_SUBST(HAVE_ARM_SIMD)
 AC_SUBST(HAVE_ARM_NEON)
 AC_SUBST(BUILD_ARM_NEON)
+AC_SUBST(ARM_ARCH)
 
 if test -n "$MOZ_ARCH"; then
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-arch=$MOZ_ARCH"
diff --git a/js/src/config/config.mk b/js/src/config/config.mk
index 2c1903172a22..379eb9aa9141 100644
--- a/js/src/config/config.mk
+++ b/js/src/config/config.mk
@@ -587,10 +587,11 @@ OS_COMPILE_CMMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch
 endif
 endif
 
-COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS)
-COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS)
-COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS)
-COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS)
+COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(EXTRA_COMPILE_FLAGS)
+ASFLAGS += $(EXTRA_ASSEMBLER_FLAGS)
 
 ifndef CROSS_COMPILE
 HOST_CFLAGS += $(RTL_FLAGS)
@@ -884,3 +885,21 @@ MOZ_GTK2_CFLAGS := -I$(topsrcdir)/widget/gtk/compat $(MOZ_GTK2_CFLAGS)
 endif
 
 DEFINES += -DNO_NSPR_10_SUPPORT
+
+ifdef IS_GYP_DIR
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/ipc/chromium/src \
+  -I$(topsrcdir)/ipc/glue \
+  -I$(DEPTH)/ipc/ipdl/_ipdlheaders \
+  $(NULL)
+
+ifeq (WINNT,$(OS_TARGET))
+# These get set via VC project file settings for normal GYP builds.
+DEFINES += -DUNICODE -D_UNICODE
+LOCAL_INCLUDES += -I'$(MOZ_DIRECTX_SDK_PATH)/include'
+endif
+
+STL_FLAGS=
+# Skip most Mozilla-specific include locations.
+INCLUDES = -I. $(LOCAL_INCLUDES) -I$(DEPTH)/dist/include
+endif
diff --git a/media/mtransport/third_party/moz.build b/media/mtransport/third_party/moz.build
index 2cd7ad5c9a18..dfc73472cbef 100644
--- a/media/mtransport/third_party/moz.build
+++ b/media/mtransport/third_party/moz.build
@@ -4,4 +4,15 @@
 # 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/.
 
-PARALLEL_EXTERNAL_MAKE_DIRS += ['nrappkit', 'nICEr']
+include('/build/gyp.mozbuild')
+
+GYP_DIRS += [
+    'nICEr',
+    'nrappkit',
+]
+
+GYP_DIRS['nICEr'].input = 'nICEr/nicer.gyp'
+GYP_DIRS['nICEr'].variables = gyp_vars
+
+GYP_DIRS['nrappkit'].input = 'nrappkit/nrappkit.gyp'
+GYP_DIRS['nrappkit'].variables = gyp_vars
diff --git a/media/mtransport/third_party/nICEr/nicer.gyp b/media/mtransport/third_party/nICEr/nicer.gyp
index 8bf16d3a5b49..f8cc39e6c4d6 100644
--- a/media/mtransport/third_party/nICEr/nicer.gyp
+++ b/media/mtransport/third_party/nICEr/nicer.gyp
@@ -34,9 +34,6 @@
               "./src/net",
               "./src/stun",
               "./src/util",
-
-	      # Mozilla, hopefully towards the end
-             '$(DEPTH)/dist/include',
           ],
 
           'sources' : [
diff --git a/media/mtransport/third_party/nrappkit/nrappkit.gyp b/media/mtransport/third_party/nrappkit/nrappkit.gyp
index 94e81924b148..b22923838f00 100644
--- a/media/mtransport/third_party/nrappkit/nrappkit.gyp
+++ b/media/mtransport/third_party/nrappkit/nrappkit.gyp
@@ -22,9 +22,6 @@
 	      'src/stats',
 	      'src/util',
 	      'src/util/libekr',
-
-	      # Mozilla, hopefully towards the end
-             '$(DEPTH)/dist/include',
           ],
 
           'sources' : [
diff --git a/media/webrtc/moz.build b/media/webrtc/moz.build
index f6034dc30d3d..5c5243a4c55c 100644
--- a/media/webrtc/moz.build
+++ b/media/webrtc/moz.build
@@ -4,13 +4,30 @@
 # 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/.
 
-EXTERNAL_MAKE_DIRS += [
-    'trunk',
-    'signaling',
-]
+include('/build/gyp.mozbuild')
+
+GYP_DIRS += ['trunk']
+
+GYP_DIRS['trunk'].input = 'trunk/peerconnection.gyp'
+GYP_DIRS['trunk'].variables = gyp_vars
+
+if CONFIG['MOZ_WEBRTC_SIGNALING']:
+    GYP_DIRS += ['signaling']
+    GYP_DIRS['signaling'].input = 'signaling/signaling.gyp'
+    GYP_DIRS['signaling'].variables = gyp_vars.copy()
+    GYP_DIRS['signaling'].variables.update(
+        build_for_test=0
+    )
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
-    EXTERNAL_MAKE_DIRS += [
-        'trunk/testing',
-        'signalingtest',
-    ]
+    GYP_DIRS += ['trunk/testing']
+    GYP_DIRS['trunk/testing'].input = 'trunk/testing/gtest.gyp'
+    GYP_DIRS['trunk/testing'].variables = gyp_vars
+
+    if CONFIG['MOZ_WEBRTC_SIGNALING']:
+        GYP_DIRS += ['signalingtest']
+        GYP_DIRS['signalingtest'].input = 'signaling/signaling.gyp'
+        GYP_DIRS['signalingtest'].variables = gyp_vars.copy()
+        GYP_DIRS['signalingtest'].variables.update(
+            build_for_test=1
+        )
diff --git a/media/webrtc/trunk/tools/gyp/pylib/gyp/generator/mozmake.py b/media/webrtc/trunk/tools/gyp/pylib/gyp/generator/mozmake.py
deleted file mode 100644
index 482d38e9cc6e..000000000000
--- a/media/webrtc/trunk/tools/gyp/pylib/gyp/generator/mozmake.py
+++ /dev/null
@@ -1,485 +0,0 @@
-# Copyright (c) 2012 Mozilla Foundation. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import collections
-import gyp
-import gyp.common
-import sys
-import platform
-import os
-import re
-import shlex
-from mozbuild.util import (
-    FileAvoidWrite,
-    shell_quote,
-)
-
-generator_wants_sorted_dependencies = True
-
-generator_default_variables = {
-}
-for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR',
-                'LIB_DIR', 'SHARED_LIB_DIR']:
-  # Some gyp steps fail if these are empty(!).
-  generator_default_variables[dirname] = 'dir'
-for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
-               'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
-               'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX',
-               'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX',
-               'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX',
-               'LINKER_SUPPORTS_ICF']:
-  generator_default_variables[unused] = ''
-
-COMMON_HEADER = """# This makefile was automatically generated from %(buildfile)s. Please do not edit it directly.
-DEPTH		= %(depth)s
-topsrcdir	= %(topsrcdir)s
-srcdir          = %(srcdir)s
-VPATH           = %(srcdir)s
-
-EXTERNALLY_MANAGED_MAKE_FILE := 1
-
-"""
-
-COMMON_FOOTER = """
-
-include %(common_mk_path)s
-"""
-
-COMMON_MK = """# This file was generated by mozmake.py. Do not edit it directly.
-ifndef COMMON_MK_INCLUDED
-COMMON_MK_INCLUDED := 1
-
-include $(topsrcdir)/config/rules.mk
-
-LOCAL_INCLUDES += \\
-  -I$(topsrcdir)/ipc/chromium/src \\
-  -I$(topsrcdir)/ipc/glue \\
-  -I$(DEPTH)/ipc/ipdl/_ipdlheaders \\
-  $(NULL)
-
-ifdef MOZ_DEBUG
-CFLAGS += $(CPPFLAGS_Debug) $(CFLAGS_Debug)
-CXXFLAGS += $(CPPFLAGS_Debug) $(CXXFLAGS_Debug)
-DEFINES += $(DEFINES_Debug)
-LOCAL_INCLUDES += $(INCLUDES_Debug)
-ASFLAGS += $(ASFLAGS_Debug)
-else # non-MOZ_DEBUG
-CFLAGS += $(CPPFLAGS_Release) $(CFLAGS_Release)
-CXXFLAGS += $(CPPFLAGS_Release) $(CXXFLAGS_Release)
-DEFINES += $(DEFINES_Release)
-LOCAL_INCLUDES += $(INCLUDES_Release)
-ASFLAGS += $(ASFLAGS_Release)
-endif
-
-ifeq (WINNT,$(OS_TARGET))
-# These get set via VC project file settings for normal GYP builds.
-DEFINES += -DUNICODE -D_UNICODE
-LOCAL_INCLUDES += -I'$(MOZ_DIRECTX_SDK_PATH)/include'
-endif
-
-# Don't use STL wrappers when compiling Google code.
-STL_FLAGS =
-
-# Skip most Mozilla-specific include locations.
-INCLUDES = -I. $(LOCAL_INCLUDES) -I$(DEPTH)/dist/include
-
-# Ensure that subdirs for sources get created before compiling
-ifdef OBJS
-SUB_SRCDIRS := $(addsuffix .dirstamp,$(addprefix $(CURDIR)/,$(sort $(dir $(OBJS)))))
-$(OBJS): $(SUB_SRCDIRS)
-$(SUB_SRCDIRS):
-	$(MKDIR) -p $(dir $@)
-	touch $@
-endif
-
-# COPY_SRCS get copied to the current directory to be compiled
-
-define COPY_SRC
-$(notdir $(1)): $(1)
-	$$(INSTALL) $$(IFLAGS1) '$$<' .
-
-endef # COPY_SRC
-ifdef COPY_SRCS
-GARBAGE += $(notdir $(COPY_SRCS))
-$(foreach s,$(COPY_SRCS), $(eval $(call COPY_SRC,$(s))))
-endif
-
-# Rules for regenerating Makefiles from GYP files.
-Makefile: %(input_gypfiles)s %(generator)s
-	$(PYTHON) %(commandline)s
-	@$(TOUCH) $@
-endif
-
-include $(topsrcdir)/config/recurse.mk
-"""
-
-def ensure_directory_exists(path):
-  dir = os.path.dirname(path)
-  if dir and not os.path.exists(dir):
-    os.makedirs(dir)
-
-def GetFlavor(params):
-  """Returns |params.flavor| if it's set, the system's default flavor else."""
-  system = platform.system().lower()
-  flavors = {
-    'microsoft': 'win',
-    'windows'  : 'win',
-    'cygwin'   : 'win',
-    'darwin'   : 'mac',
-    'sunos'    : 'solaris',
-    'dragonfly': 'dragonfly',
-    'freebsd'  : 'freebsd',
-    'netbsd'   : 'netbsd',
-    'openbsd'  : 'openbsd',
-  }
-
-  if 'flavor' in params:
-    return params['flavor']
-  if system in flavors:
-    return flavors[system]
-
-  return 'linux'
-
-
-def CalculateVariables(default_variables, params):
-  generator_flags = params.get('generator_flags', {})
-  default_variables['OS'] = generator_flags.get('os', GetFlavor(params))
-
-
-def CalculateGeneratorInputInfo(params):
-  """Calculate the generator specific info that gets fed to input (called by
-  gyp)."""
-  generator_flags = params.get('generator_flags', {})
-  if generator_flags.get('adjust_static_libraries', False):
-    global generator_wants_static_library_dependencies_adjusted
-    generator_wants_static_library_dependencies_adjusted = True
-
-def WriteMakefile(filename, data, build_file, depth, topsrcdir, srcdir, relative_path, common_mk_path, extra_data=None):
-  if not os.path.isabs(topsrcdir):
-    topsrcdir = depth + "/" + topsrcdir
-  if not os.path.isabs(srcdir):
-    srcdir = depth + "/" + srcdir
-  #TODO: should compare with the existing file and not overwrite it if the
-  # contents are the same!
-  ensure_directory_exists(filename)
-  with FileAvoidWrite(filename) as f:
-    f.write(COMMON_HEADER % {'buildfile': build_file,
-                             'depth': depth,
-                             'topsrcdir': topsrcdir,
-                             'srcdir': srcdir})
-    for k, v in data.iteritems():
-      f.write("%s = %s\n" % (k, " \\\n  ".join([''] + v) if isinstance(v, list) else v))
-    f.write(COMMON_FOOTER % {'common_mk_path': common_mk_path})
-    if extra_data:
-      f.write(extra_data)
-
-def WriteCommonMk(path, build_files, scriptname, commandline):
-  with FileAvoidWrite(path) as f:
-    f.write(COMMON_MK % {'input_gypfiles': ' '.join(build_files),
-                         'generator': scriptname,
-                         'commandline': ' '.join(commandline)})
-
-def striplib(name):
-  "Strip lib prefixes from library names."
-  if name[:3] == 'lib':
-    return name[3:]
-  return name
-
-AS_EXTENSIONS = set([
-  '.s',
-  '.S'
-])
-CPLUSPLUS_EXTENSIONS = set([
-  '.cc',
-  '.cpp',
-  '.cxx'
-])
-COMPILABLE_EXTENSIONS = set([
-  '.c',
-  '.s',
-  '.S',
-  '.m',
-  '.mm'
-])
-COMPILABLE_EXTENSIONS.update(CPLUSPLUS_EXTENSIONS)
-
-def swapslashes(p):
-  "Swap backslashes for forward slashes in a path."
-  return p.replace('\\', '/')
-
-def getdepth(s):
-  """Given a relative path, return a relative path consisting
-  of .. segments that would lead to the parent directory."""
-  return "/".join(".." for x in swapslashes(s).split("/") if x)
-
-def Compilable(filename):
-  return os.path.splitext(filename)[1] in COMPILABLE_EXTENSIONS
-
-class MakefileGenerator(object):
-  def __init__(self, target_dicts, data, options, depth, topsrcdir, relative_topsrcdir, relative_srcdir, output_dir, flavor, common_mk_path):
-    self.target_dicts = target_dicts
-    self.data = data
-    self.options = options
-    self.depth = depth
-    self.relative_srcdir = swapslashes(relative_srcdir)
-    self.topsrcdir = swapslashes(topsrcdir)
-    self.relative_topsrcdir = swapslashes(relative_topsrcdir)
-    self.srcdir = swapslashes(os.path.join(topsrcdir, relative_srcdir))
-    self.output_dir = output_dir
-    self.flavor = flavor
-    self.common_mk_path = common_mk_path
-    # Directories to be built in order.
-    self.dirs = []
-    # Directories that can be built in any order, but before |dirs|.
-    self.parallel_dirs = []
-    # Targets that have been processed.
-    self.visited = set()
-    # Link dependencies.
-    self.target_link_deps = {}
-
-  def CalculateMakefilePath(self, build_file, target_name):
-    """Determine where to write a Makefile for a given gyp file."""
-    rel_path = gyp.common.RelativePath(os.path.dirname(build_file),
-                                       self.srcdir)
-    # Add a subdir using the build_file name and the target_name.
-    rel_path = os.path.join(rel_path,
-                             os.path.splitext(os.path.basename(build_file))[0]
-                             + "_" + target_name)
-    output_file = os.path.join(self.output_dir, rel_path, "Makefile")
-    return swapslashes(rel_path), swapslashes(output_file)
-
-  def ProcessTargets(self, needed_targets):
-    """
-    Put all targets in proper order so that dependencies get built before
-    the targets that need them. Targets that have no dependencies
-    can get built in parallel_dirs. Targets with dependencies must be in
-    dirs, and must also be listed after any of their dependencies.
-    """
-    for qualified_target in needed_targets:
-      if qualified_target in self.visited:
-        continue
-      self.ProcessTarget(qualified_target)
-
-  def ProcessTarget(self, qualified_target):
-    """
-    Write a Makefile.in for |qualified_target| and add it to |dirs| or
-    |parallel_dirs| as appropriate, after processing all of its
-    dependencies.
-    """
-    spec = self.target_dicts[qualified_target]
-    if 'dependencies' in spec and spec['dependencies']:
-      for dep in spec['dependencies']:
-        if dep not in self.visited:
-          self.ProcessTarget(dep)
-      dirs = self.dirs
-    else:
-      # no dependencies
-      dirs = self.parallel_dirs
-    # Now write a Makefile for this target
-    build_file, target, toolset = gyp.common.ParseQualifiedTarget(
-      qualified_target)
-    build_file = os.path.abspath(build_file)
-    rel_path, output_file = self.CalculateMakefilePath(build_file, target)
-    subdepth = self.depth + "/" + getdepth(rel_path)
-    if self.WriteTargetMakefile(output_file, rel_path, qualified_target, spec, build_file, subdepth):
-        # If WriteTargetMakefile returns True, then this is a useful target
-      dirs.append(rel_path)
-    self.visited.add(qualified_target)
-
-  def WriteTargetMakefile(self, output_file, rel_path, qualified_target, spec, build_file, depth):
-    configs = spec['configurations']
-    # Update global list of link dependencies.
-    if spec['type'] in ('static_library', 'shared_library'):
-      self.target_link_deps[qualified_target] = "$(call EXPAND_LIBNAME_PATH,%s,$(DEPTH)/%s/%s)" % (striplib(spec['target_name']), self.relative_srcdir, rel_path)
-
-    data = {}
-    #TODO: handle actions/rules/copies
-    if 'actions' in spec:
-      pass
-    if 'rules' in spec:
-      pass
-    if 'copies' in spec:
-      pass
-    libs = []
-    if 'dependencies' in spec:
-      for dep in spec['dependencies']:
-        if dep in self.target_link_deps:
-          libs.append(self.target_link_deps[dep])
-    if libs:
-      data['EXTRA_LIBS'] = libs
-
-    # Get DEFINES/INCLUDES
-    for configname in sorted(configs.keys()):
-      config = configs[configname]
-      #XXX: this sucks
-      defines = config.get('defines')
-      if defines:
-        data['DEFINES_%s' % configname] = [shell_quote("-D%s" % d) for d in defines]
-      includes = []
-      for i in config.get('include_dirs', []):
-        # Make regular paths into srcdir-relative paths, leave
-        # variable-specified paths alone.
-        if i.startswith("$(") or os.path.isabs(i):
-          if ' ' in i:
-            includes.append('"%s"' % i)
-          else:
-            includes.append(i)
-        else:
-          includes.append("$(srcdir)/" + i)
-      if includes:
-        data['INCLUDES_%s' % configname] = ["-I%s" %i for i in includes]
-      #XXX: handle mac stuff?
-# we want to use our compiler options in general
-#      cflags = config.get('cflags')
-#      if cflags:
-#        data['CPPFLAGS_%s' % configname] = cflags
-#      cflags_c = config.get('cflags_c')
-#      if cflags_c:
-#        data['CFLAGS_%s' % configname] = cflags_c
-#      cflags_cc = config.get('cflags_cc')
-#      if cflags_cc:
-#        data['CXXFLAGS_%s' % configname] = cflags_cc
-# we need to keep pkg-config flags however
-      cflags_mozilla = config.get('cflags_mozilla')
-      if cflags_mozilla:
-        data['CPPFLAGS_%s' % configname] = cflags_mozilla
-      asflags_mozilla = config.get('asflags_mozilla')
-      if asflags_mozilla:
-        data['ASFLAGS_%s' % configname] = asflags_mozilla
-    sources = {
-      'CPPSRCS': {'exts': CPLUSPLUS_EXTENSIONS, 'files': []},
-      'CSRCS': {'exts': ['.c'], 'files': []},
-      'CMSRCS': {'exts': ['.m'], 'files': []},
-      'CMMSRCS': {'exts': ['.mm'], 'files': []},
-      'SSRCS': {'exts': AS_EXTENSIONS, 'files': []},
-      }
-    copy_srcs = []
-    for s in spec.get('sources', []):
-      if not Compilable(s):
-        continue
-
-      # Special-case absolute paths, they'll get copied into the objdir
-      # for compiling.
-      if os.path.isabs(s):
-        # GNU Make falls down pretty badly with spaces in filenames.
-        # Conveniently, using a single-character ? as a wildcard
-        # works fairly well.
-        copy_srcs.append(s.replace(' ', '?'))
-        s = os.path.basename(s)
-
-      ext = os.path.splitext(s)[1]
-      for source_type, d in sources.iteritems():
-        if ext in d['exts']:
-          d['files'].append(s)
-          break
-      
-    for source_type, d in sources.iteritems():
-      if d['files']:
-        data[source_type] = d['files']
-
-    if copy_srcs:
-      data['COPY_SRCS'] = copy_srcs
-
-    if spec['type'] == 'executable':
-      data['PROGRAM'] = spec['target_name']
-    elif spec['type'] == 'static_library':
-      data['LIBRARY_NAME'] = striplib(spec['target_name'])
-      data['FORCE_STATIC_LIB'] = 1
-    elif spec['type'] in ('loadable_module', 'shared_library'):
-      data['LIBRARY_NAME'] = striplib(spec['target_name'])
-      data['FORCE_SHARED_LIB'] = 1
-    else:
-      # Maybe nothing?
-      return False
-    WriteMakefile(output_file, data, build_file, depth, self.topsrcdir,
-                  # we set srcdir up one directory, since the subdir
-                  # doesn't actually exist in the source directory
-                  swapslashes(os.path.normpath(os.path.join(self.topsrcdir, self.relative_srcdir, os.path.split(rel_path)[0]))),
-                  self.relative_srcdir,
-                  self.common_mk_path)
-    return True
-
-def GenerateOutput(target_list, target_dicts, data, params):
-  options = params['options']
-  flavor = GetFlavor(params)
-  generator_flags = params.get('generator_flags', {})
-
-  # Get a few directories into Mozilla-common naming conventions
-  # The root of the source repository.
-  topsrcdir = os.path.abspath(options.toplevel_dir)
-  # The object directory (root of the build).
-  objdir = os.path.abspath(generator_flags['OBJDIR'] if 'OBJDIR' in generator_flags else '.')
-  # A relative path from the objdir to the topsrcdir
-  relative_topsrcdir = gyp.common.RelativePath(topsrcdir, objdir)
-  # The directory containing the gyp file on which gyp was invoked.
-  gyp_file_dir = os.path.abspath(os.path.dirname(params['build_files'][0]) or '.')
-  # The relative path from topsrcdir to gyp_file_dir
-  relative_srcdir = gyp.common.RelativePath(gyp_file_dir, topsrcdir)
-  # The relative path from objdir to gyp_file_dir
-  srcdir = gyp.common.RelativePath(gyp_file_dir, objdir)
-  # The absolute path to the source dir
-  abs_srcdir = topsrcdir + "/" + relative_srcdir
-  # The path to get up to the root of the objdir from the output dir.
-  depth = getdepth(relative_srcdir)
-  # The output directory.
-  output_dir = os.path.abspath(options.generator_output or '.')
-  # The path to the root Makefile
-  makefile_path = os.path.join(output_dir, "Makefile")
-
-  def topsrcdir_path(path):
-    return "$(topsrcdir)/" + swapslashes(gyp.common.RelativePath(path, topsrcdir))
-  def objdir_path(path):
-    return "$(DEPTH)/" + swapslashes(gyp.common.RelativePath(path, objdir))
-
-  # Find the list of targets that derive from the gyp file(s) being built.
-  needed_targets = set()
-  build_files = set()
-  for build_file in params['build_files']:
-    build_file = os.path.normpath(build_file)
-    for target in gyp.common.AllTargets(target_list, target_dicts, build_file):
-      needed_targets.add(target)
-      build_file_, _, _ = gyp.common.ParseQualifiedTarget(target)
-      build_files.add(topsrcdir_path(build_file_))
-
-  common_mk_path = objdir_path(os.path.join(output_dir, "common.mk"))
-
-  generator = MakefileGenerator(target_dicts, data, options, depth, topsrcdir, relative_topsrcdir, relative_srcdir, output_dir, flavor, common_mk_path)
-  generator.ProcessTargets(needed_targets)
-
-  # Write the top-level makefile, which simply calls the other makefiles
-  topdata = {'DIRS': generator.dirs}
-  if generator.parallel_dirs:
-    topdata['PARALLEL_DIRS'] = generator.parallel_dirs
-  WriteMakefile(makefile_path, topdata, params['build_files'][0],
-                depth,
-                swapslashes(topsrcdir),
-                swapslashes(abs_srcdir),
-                swapslashes(relative_srcdir),
-                common_mk_path)
-  scriptname = "$(topsrcdir)/media/webrtc/trunk/tools/gyp/pylib/gyp/generator/mozmake.py"
-  # Reassemble a commandline from parts so that all the paths are correct
-  # NOTE: this MUST match the commandline generated in configure.in!
-  # since we don't see --include statements, duplicate them in FORCE_INCLUDE_FILE lines
-  # Being in a define, they also get used by the common.mk invocation of gyp so they
-  # they don't disappear in the second round of tail-swallowing
-  forced_includes = ""
-  for option in options.defines:
-    if option[:20] == "FORCED_INCLUDE_FILE=":
-      forced_includes += "--include=%s" % option[20:]
-
-  commandline = [topsrcdir_path(sys.argv[0]),
-                 "--format=mozmake",
-                 forced_includes,
-                 "--depth=%s" % topsrcdir_path(options.depth),
-                 "--generator-output=%s" % objdir_path(options.generator_output),
-                 "--toplevel-dir=$(topsrcdir)",
-                 "-G OBJDIR=$(DEPTH)"] + \
-                 ['-G %s' % g for g in options.generator_flags if not g.startswith('OBJDIR=')] + \
-                 ['-D%s' % d for d in options.defines] + \
-                 [topsrcdir_path(b) for b in params['build_files']]
-
-  WriteCommonMk(os.path.join(output_dir, "common.mk"),
-                build_files,
-                scriptname,
-                commandline)
diff --git a/media/webrtc/trunk/tools/gyp/pylib/gyp/input.py b/media/webrtc/trunk/tools/gyp/pylib/gyp/input.py
index 65236671f97a..f211e02c22ae 100644
--- a/media/webrtc/trunk/tools/gyp/pylib/gyp/input.py
+++ b/media/webrtc/trunk/tools/gyp/pylib/gyp/input.py
@@ -1424,6 +1424,10 @@ class DependencyGraphNode(object):
     self.dependencies = []
     self.dependents = []
 
+  # This Mozilla change makes DependencyGraphNode more idempotent.
+  def __hash__(self):
+    return hash(self.ref)
+
   def FlattenToList(self):
     # flat_list is the sorted list of dependencies - actually, the list items
     # are the "ref" attributes of DependencyGraphNodes.  Every target will
diff --git a/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi b/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi
index b60cee549b93..00e77e7d31d0 100644
--- a/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi
+++ b/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi
@@ -15,7 +15,7 @@
         ['build_with_mozilla==1', {
           # Mozilla provides its own build of the opus library.
           'include_dirs': [
-            '$(DIST)/include/opus',
+            '/media/libopus/include',
            ]
         }, {
           'dependencies': [
diff --git a/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi b/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi
index 9a24f931fdfe..67bc5753eb3b 100644
--- a/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi
+++ b/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi
@@ -47,9 +47,6 @@
       ],
       'conditions': [
         ['build_with_mozilla==1', {
-          'include_dirs': [
-            '$(DIST)/include',
-          ],
           'cflags_mozilla': [
             '$(NSPR_CFLAGS)',
           ],
@@ -76,14 +73,16 @@
         }],
         ['OS=="android"', {
           'include_dirs': [
-            '$(topsrcdir)/widget/android',
+            '/widget/android',
             'android',
           ],
         }], # OS==android
         ['moz_widget_toolkit_gonk==1', {
+          'cflags_mozilla': [
+            '-I$(ANDROID_SOURCE)/frameworks/wilhelm/include',
+            '-I$(ANDROID_SOURCE)/system/media/wilhelm/include',
+          ],
           'include_dirs': [
-            '$(ANDROID_SOURCE)/frameworks/wilhelm/include',
-            '$(ANDROID_SOURCE)/system/media/wilhelm/include',
             'android',
           ],
         }], # moz_widget_toolkit_gonk==1
diff --git a/media/webrtc/webrtc_config.gypi b/media/webrtc/webrtc_config.gypi
deleted file mode 100644
index 445d95146181..000000000000
--- a/media/webrtc/webrtc_config.gypi
+++ /dev/null
@@ -1,54 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# definitions to control what gets built in webrtc
-# NOTE!!! if you change something here, due to .gyp files not
-# being reprocessed on .gypi changes, run this before building:
-# "find . -name '*.gyp' | xargs touch"
-{
-  'variables': {
-    'build_with_mozilla': 1,
-    'build_with_chromium': 0,
-    # basic stuff for everything
-    'include_internal_video_render': 0,
-    'clang_use_chrome_plugins': 0,
-    'enable_protobuf': 0,
-    'include_tests': 0,
-    'enable_android_opensl': 1,
-# use_system_lib* still seems to be in use in trunk/build
-    'use_system_libjpeg': 0,
-    'use_system_libvpx': 0,
-    'build_libjpeg': 0,
-    'build_libvpx': 0,
-    # saves 4MB when webrtc_trace is off
-    'enable_lazy_trace_alloc': 1,
-
-    # turn off mandatory use of NEON and instead use NEON detection
-    'arm_neon': 0,
-
-    #if "-D build_with_gonk=1", then set moz_widget_toolkit_gonk to 1
-    'moz_widget_toolkit_gonk': 0,
-    'variables': {
-      'build_with_gonk%': 0,
-    },
-    'conditions': [
-      ['build_with_gonk==1', {
-         'moz_widget_toolkit_gonk': 1,
-      }],
-    ],
-# (for vp8) chromium sets to 0 also
-    'use_temporal_layers': 0,
-# Creates AEC internal sample dump files in current directory
-#    'aec_debug_dump': 1,
-
-    # codec enable/disables:
-    # Note: if you change one here, you must modify layout/media/webrtc/Makefile.in!
-    'include_g711': 1,
-    'include_opus': 1,
-    'include_g722': 0,
-    'include_ilbc': 0,
-    'include_isac': 0,
-    'include_pcm16b': 1,
-  }
-}
diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py
index c045566232e8..279c390e84b2 100644
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -93,7 +93,6 @@ class BackendMakeFile(object):
         self.fh = FileAvoidWrite(self.name, capture_diff=True)
         self.fh.write('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.\n')
         self.fh.write('\n')
-        self.fh.write('MOZBUILD_DERIVED := 1\n')
 
     def write(self, buf):
         self.fh.write(buf)
diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py
index 06f98b9de028..27a9bfda88a6 100644
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -49,6 +49,8 @@ from .reader import (
     SandboxValidationError,
 )
 
+from .gyp_reader import GypSandbox
+
 
 class TreeMetadataEmitter(LoggingMixin):
     """Converts the executed mozbuild files into data structures.
@@ -91,7 +93,7 @@ class TreeMetadataEmitter(LoggingMixin):
                     raise Exception('Unhandled object of type %s' % type(o))
 
         for out in output:
-            if isinstance(out, MozbuildSandbox):
+            if isinstance(out, (MozbuildSandbox, GypSandbox)):
                 # Keep all sandboxes around, we will need them later.
                 sandboxes[out['OBJDIR']] = out
 
@@ -107,7 +109,7 @@ class TreeMetadataEmitter(LoggingMixin):
                 sandbox_execution_time += out.execution_time
 
             else:
-                raise Exception('Unhandled output type: %s' % out)
+                raise Exception('Unhandled output type: %s' % type(out))
 
         start = time.time()
         objs = list(self._emit_libs_derived(sandboxes))
@@ -209,6 +211,8 @@ class TreeMetadataEmitter(LoggingMixin):
             ANDROID_RES_DIRS='ANDROID_RES_DIRS',
             CPP_UNIT_TESTS='CPP_UNIT_TESTS',
             EXPORT_LIBRARY='EXPORT_LIBRARY',
+            EXTRA_ASSEMBLER_FLAGS='EXTRA_ASSEMBLER_FLAGS',
+            EXTRA_COMPILE_FLAGS='EXTRA_COMPILE_FLAGS',
             EXTRA_COMPONENTS='EXTRA_COMPONENTS',
             EXTRA_JS_MODULES='EXTRA_JS_MODULES',
             EXTRA_PP_COMPONENTS='EXTRA_PP_COMPONENTS',
@@ -220,6 +224,7 @@ class TreeMetadataEmitter(LoggingMixin):
             GENERATED_FILES='GENERATED_FILES',
             HOST_LIBRARY_NAME='HOST_LIBRARY_NAME',
             IS_COMPONENT='IS_COMPONENT',
+            IS_GYP_DIR='IS_GYP_DIR',
             JS_MODULES_PATH='JS_MODULES_PATH',
             LIBS='LIBS',
             LIBXUL_LIBRARY='LIBXUL_LIBRARY',
@@ -229,7 +234,7 @@ class TreeMetadataEmitter(LoggingMixin):
             SDK_LIBRARY='SDK_LIBRARY',
         )
         for mak, moz in varmap.items():
-            if sandbox[moz]:
+            if moz in sandbox and sandbox[moz]:
                 passthru.variables[mak] = sandbox[moz]
 
         # NO_VISIBILITY_FLAGS is slightly different
diff --git a/python/mozbuild/mozbuild/frontend/gyp_reader.py b/python/mozbuild/mozbuild/frontend/gyp_reader.py
new file mode 100644
index 000000000000..2f674f96b3d7
--- /dev/null
+++ b/python/mozbuild/mozbuild/frontend/gyp_reader.py
@@ -0,0 +1,190 @@
+# 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/.
+
+from __future__ import unicode_literals
+import gyp
+import sys
+import time
+import os
+import mozpack.path as mozpath
+from mozpack.files import FileFinder
+from .sandbox import (
+    alphabetical_sorted,
+    GlobalNamespace,
+)
+from .sandbox_symbols import VARIABLES
+
+# Define this module as gyp.generator.mozbuild so that gyp can use it
+# as a generator under the name "mozbuild".
+sys.modules['gyp.generator.mozbuild'] = sys.modules[__name__]
+
+# build/gyp_chromium does this:
+#   script_dir = os.path.dirname(os.path.realpath(__file__))
+#   chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
+#   sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
+# We're not importing gyp_chromium, but we want both script_dir and
+# chrome_src for the default includes, so go backwards from the pylib
+# directory, which is the parent directory of gyp module.
+chrome_src = mozpath.abspath(mozpath.join(mozpath.dirname(gyp.__file__),
+    '../../../..'))
+script_dir = mozpath.join(chrome_src, 'build')
+
+# Default variables gyp uses when evaluating gyp files.
+generator_default_variables = {
+}
+for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR',
+                'LIB_DIR', 'SHARED_LIB_DIR']:
+  # Some gyp steps fail if these are empty(!).
+  generator_default_variables[dirname] = b'dir'
+
+for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
+               'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
+               'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX',
+               'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX',
+               'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX',
+               'LINKER_SUPPORTS_ICF']:
+  generator_default_variables[unused] = b''
+
+
+class GypSandbox(GlobalNamespace):
+    """Class mimicking MozbuildSandbox for processing of the data
+    extracted from Gyp by a mozbuild backend.
+
+    Inherits from GlobalNamespace because it doesn't need the extra
+    functionality from Sandbox.
+    """
+    def __init__(self, main_path, dependencies_paths=[]):
+        self.main_path = main_path
+        self.all_paths = set([main_path]) | set(dependencies_paths)
+        self.execution_time = 0
+        GlobalNamespace.__init__(self, allowed_variables=VARIABLES)
+
+    def get_affected_tiers(self):
+        tiers = (VARIABLES[key][3] for key in self if key in VARIABLES)
+        return set(tier for tier in tiers if tier)
+
+
+def encode(value):
+    if isinstance(value, unicode):
+        return value.encode('utf-8')
+    return value
+
+
+def read_from_gyp(config, path, output, vars):
+    """Read a gyp configuration and emits GypSandboxes for the backend to
+    process.
+
+    config is a ConfigEnvironment, path is the path to a root gyp configuration
+    file, output is the base path under which the objdir for the various gyp
+    dependencies will be, and vars a dict of variables to pass to the gyp
+    processor.
+    """
+
+    time_start = time.time()
+
+    # gyp expects plain str instead of unicode. The frontend code gives us
+    # unicode strings, so convert them.
+    path = encode(path)
+    str_vars = dict((name, encode(value)) for name, value in vars.items())
+
+    params = {
+        b'parallel': False,
+        b'generator_flags': {},
+        b'build_files': [path],
+    }
+
+    # Files that gyp_chromium always includes
+    includes = [encode(mozpath.join(script_dir, 'common.gypi'))]
+    finder = FileFinder(chrome_src, find_executables=False)
+    includes.extend(encode(mozpath.join(chrome_src, name))
+        for name, _ in finder.find('*/supplement.gypi'))
+
+    # Read the given gyp file and its dependencies.
+    generator, flat_list, targets, data = \
+        gyp.Load([path], format=b'mozbuild',
+            default_variables=str_vars,
+            includes=includes,
+            depth=encode(mozpath.dirname(path)),
+            params=params)
+
+    # Process all targets from the given gyp files and its dependencies.
+    # The path given to AllTargets needs to use os.sep, while the frontend code
+    # gives us paths normalized with forward slash separator.
+    for target in gyp.common.AllTargets(flat_list, targets, path.replace(b'/', os.sep)):
+        build_file, target_name, toolset = gyp.common.ParseQualifiedTarget(target)
+        # The list of included files returned by gyp are relative to build_file
+        included_files = [mozpath.abspath(mozpath.join(mozpath.dirname(build_file), f))
+                          for f in data[build_file]['included_files']]
+        # Emit a sandbox for each target.
+        sandbox = GypSandbox(mozpath.abspath(build_file), included_files)
+
+        with sandbox.allow_all_writes() as d:
+            topsrcdir = d['TOPSRCDIR'] = config.topsrcdir
+            d['TOPOBJDIR'] = config.topobjdir
+            relsrcdir = d['RELATIVEDIR'] = mozpath.relpath(mozpath.dirname(build_file), config.topsrcdir)
+            d['SRCDIR'] = mozpath.join(topsrcdir, relsrcdir)
+
+            # Each target is given its own objdir. The base of that objdir
+            # is derived from the relative path from the root gyp file path
+            # to the current build_file, placed under the given output
+            # directory. Since several targets can be in a given build_file,
+            # separate them in subdirectories using the build_file basename
+            # and the target_name.
+            reldir  = mozpath.relpath(mozpath.dirname(build_file),
+                                      mozpath.dirname(path))
+            subdir = '%s_%s' % (
+                mozpath.splitext(mozpath.basename(build_file))[0],
+                target_name,
+            )
+            d['OBJDIR'] = mozpath.join(output, reldir, subdir)
+            d['IS_GYP_DIR'] = True
+
+        spec = targets[target]
+
+        # Derive which gyp configuration to use based on MOZ_DEBUG.
+        c = 'Debug' if config.substs['MOZ_DEBUG'] else 'Release'
+        if c not in spec['configurations']:
+            raise RuntimeError('Missing %s gyp configuration for target %s '
+                               'in %s' % (c, target_name, build_file))
+        target_conf = spec['configurations'][c]
+
+        if spec['type'] == 'none':
+            continue
+        elif spec['type'] == 'static_library':
+            sandbox['FORCE_STATIC_LIB'] = True
+            # Remove leading 'lib' from the target_name if any, and use as
+            # library name.
+            name = spec['target_name']
+            if name.startswith('lib'):
+                name = name[3:]
+            # The sandbox expects an unicode string.
+            sandbox['LIBRARY_NAME'] = name.decode('utf-8')
+            # The sandbox expects alphabetical order when adding sources
+            sources = alphabetical_sorted(spec.get('sources', []))
+            # gyp files contain headers in sources lists.
+            sandbox['SOURCES'] = \
+                [f for f in sources if mozpath.splitext(f)[-1] != '.h']
+
+            for define in target_conf.get('defines', []):
+                if '=' in define:
+                    name, value = define.split('=', 1)
+                    sandbox['DEFINES'][name] = value
+                else:
+                    sandbox['DEFINES'][define] = True
+
+            for include in target_conf.get('include_dirs', []):
+                sandbox['LOCAL_INCLUDES'] += [include]
+
+            with sandbox.allow_all_writes() as d:
+                d['EXTRA_ASSEMBLER_FLAGS'] = target_conf.get('asflags_mozilla', [])
+                d['EXTRA_COMPILE_FLAGS'] = target_conf.get('cflags_mozilla', [])
+        else:
+            # Ignore other types than static_library because we don't have
+            # anything using them, and we're not testing them. They can be
+            # added when that becomes necessary.
+            raise NotImplementedError('Unsupported gyp target type: %s' % spec['type'])
+
+        sandbox.execution_time = time.time() - time_start
+        yield sandbox
+        time_start = time.time()
diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py
index 990cde719e9b..98613fead466 100644
--- a/python/mozbuild/mozbuild/frontend/reader.py
+++ b/python/mozbuild/mozbuild/frontend/reader.py
@@ -59,7 +59,6 @@ from .sandbox_symbols import (
     VARIABLES,
 )
 
-
 if sys.version_info.major == 2:
     text_type = unicode
     type_type = types.TypeType
@@ -734,25 +733,58 @@ class BuildReader(object):
                        ' and '.join(', '.join(matches).rsplit(', ', 1)),
                        's are' if len(matches) > 1 else ' is'))
 
-        yield sandbox
-
-        # Traverse into referenced files.
-
         # We first collect directories populated in variables.
         dir_vars = ['DIRS', 'PARALLEL_DIRS', 'TOOL_DIRS']
 
         if self.config.substs.get('ENABLE_TESTS', False) == '1':
             dir_vars.extend(['TEST_DIRS', 'TEST_TOOL_DIRS'])
 
+        dirs = [(v, sandbox[v]) for v in dir_vars if v in sandbox]
+
+        curdir = mozpath.dirname(path)
+
+        gyp_sandboxes = []
+        for target_dir in sandbox['GYP_DIRS']:
+            gyp_dir = sandbox['GYP_DIRS'][target_dir]
+            for v in ('input', 'variables'):
+                if not getattr(gyp_dir, v):
+                    raise Exception('Missing value for GYP_DIRS["%s"].%s' %
+                        (target_dir, v))
+
+            # The make backend assumes sandboxes for sub-directories are
+            # emitted after their parent, so accumulate the gyp sandboxes.
+            # We could emit the parent sandbox before processing gyp
+            # configuration, but we need to add the gyp objdirs to that sandbox
+            # first.
+            from .gyp_reader import read_from_gyp
+            gyp_sandboxes.extend(read_from_gyp(self.config,
+                                               mozpath.join(curdir, gyp_dir.input),
+                                               mozpath.join(sandbox['OBJDIR'],
+                                               target_dir),
+                                 gyp_dir.variables))
+
+        # Add the gyp subdirectories to DIRS. We don't care about trying to
+        # place some of them in PARALLEL_DIRS because they're only going to be
+        # relevant for the compile and libs tiers. The compile tier is already
+        # parallelized, and the libs tier is always serialized, and will remain
+        # so until the library linking operations are moved out of it, at which
+        # point PARALLEL_DIRS will be irrelevant anyways.
+        for gyp_sandbox in gyp_sandboxes:
+            sandbox['DIRS'].append(mozpath.relpath(gyp_sandbox['OBJDIR'], sandbox['OBJDIR']))
+
+        yield sandbox
+
+        for gyp_sandbox in gyp_sandboxes:
+            yield gyp_sandbox
+
+        # Traverse into referenced files.
+
         # It's very tempting to use a set here. Unfortunately, the recursive
         # make backend needs order preserved. Once we autogenerate all backend
         # files, we should be able to convert this to a set.
         recurse_info = OrderedDict()
-        for var in dir_vars:
-            if not var in sandbox:
-                continue
-
-            for d in sandbox[var]:
+        for var, var_dirs in dirs:
+            for d in var_dirs:
                 if d in recurse_info:
                     raise SandboxValidationError(
                         'Directory (%s) registered multiple times in %s' % (
@@ -783,7 +815,6 @@ class BuildReader(object):
                                        'parent': sandbox['RELATIVEDIR'],
                                        'var': 'DIRS'}
 
-        curdir = mozpath.dirname(path)
         for relpath, child_metadata in recurse_info.items():
             child_path = mozpath.join(curdir, relpath, 'moz.build')
 
diff --git a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
index 45d816cc2477..520fb02666ce 100644
--- a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
@@ -573,6 +573,31 @@ VARIABLES = {
         the $(DIST_SUBDIR) directory of the otherwise default value is used.
         """, 'libs'),
 
+    'GYP_DIRS': (StrictOrderingOnAppendListWithFlagsFactory({
+            'variables': dict,
+            'input': unicode,
+        }), list,
+        """Defines a list of object directories handled by gyp configurations.
+
+        Elements of this list give the relative object directory. For each
+        element of the list, GYP_DIRS may be accessed as a dictionary
+        (GYP_DIRS[foo]). The object this returns has attributes that need to be
+        set to further specify gyp processing:
+            - input, gives the path to the root gyp configuration file for that
+              object directory.
+            - variables, a dictionary containing variables and values to pass
+              to the gyp processor.
+
+        Typical use looks like:
+            GYP_DIRS += ['foo', 'bar']
+            GYP_DIRS['foo'].input = 'foo/foo.gyp'
+            GYP_DIRS['foo'].variables = {
+                'foo': 'bar',
+                (...)
+            }
+            (...)
+        """, None),
+
     'SPHINX_TREES': (dict, dict,
         """Describes what the Sphinx documentation tree will look like.
 
diff --git a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
index f5f8a78b0bb1..c913eb6c2c59 100644
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -212,7 +212,6 @@ class TestRecursiveMakeBackend(BackendTester):
 
         lines = [l.strip() for l in open(p, 'rt').readlines()[2:]]
         self.assertEqual(lines, [
-            'MOZBUILD_DERIVED := 1',
             'DIRS := dir1',
             'PARALLEL_DIRS := dir2',
             'TEST_DIRS := dir3',
@@ -243,7 +242,6 @@ class TestRecursiveMakeBackend(BackendTester):
         backend_path = mozpath.join(env.topobjdir, 'backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
         self.assertEqual(lines, [
-            'MOZBUILD_DERIVED := 1',
             'DIRS := dir',
             'PARALLEL_DIRS := p_dir',
             'DIRS += external',

From f31e7b8bc42497ae3e45616c39991232f28a56e9 Mon Sep 17 00:00:00 2001
From: Mike Hommey 
Date: Thu, 12 Dec 2013 23:41:06 +0900
Subject: [PATCH 018/459] Bug 778236 - Remove EXTERNAL_MAKE_DIRS and
 PARALLEL_EXTERNAL_MAKE_DIRS. r=gps

---
 python/mozbuild/mozbuild/backend/recursivemake.py   | 11 -----------
 python/mozbuild/mozbuild/frontend/data.py           |  4 ----
 python/mozbuild/mozbuild/frontend/emitter.py        |  2 --
 python/mozbuild/mozbuild/frontend/reader.py         |  4 ++--
 .../mozbuild/mozbuild/frontend/sandbox_symbols.py   | 12 ------------
 python/mozbuild/mozbuild/test/backend/common.py     |  5 -----
 .../backend/data/external_make_dirs/Makefile.in     |  0
 .../backend/data/external_make_dirs/dir/Makefile.in |  0
 .../backend/data/external_make_dirs/dir/moz.build   |  0
 .../data/external_make_dirs/external/Makefile.in    |  0
 .../data/external_make_dirs/external/moz.build      |  0
 .../test/backend/data/external_make_dirs/moz.build  |  8 --------
 .../data/external_make_dirs/p_dir/Makefile.in       |  0
 .../backend/data/external_make_dirs/p_dir/moz.build |  0
 .../data/external_make_dirs/p_external/Makefile.in  |  0
 .../data/external_make_dirs/p_external/moz.build    |  0
 .../mozbuild/test/backend/test_recursivemake.py     | 13 -------------
 .../test/frontend/data/traversal-all-vars/moz.build |  3 ---
 .../mozbuild/mozbuild/test/frontend/test_emitter.py |  3 ---
 .../mozbuild/mozbuild/test/frontend/test_sandbox.py |  8 --------
 20 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/Makefile.in
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/dir/Makefile.in
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/dir/moz.build
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/external/Makefile.in
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/external/moz.build
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/moz.build
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_dir/Makefile.in
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_dir/moz.build
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_external/Makefile.in
 delete mode 100644 python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_external/moz.build

diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py
index 279c390e84b2..3742ad4f340b 100644
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -891,17 +891,6 @@ class RecursiveMakeBackend(CommonBackend):
             self._traversal.add(backend_file.relobjdir,
                                 tools=relativize(obj.test_tool_dirs))
 
-        if len(obj.external_make_dirs):
-            fh.write('DIRS += %s\n' % ' '.join(obj.external_make_dirs))
-            self._traversal.add(backend_file.relobjdir,
-                                dirs=relativize(obj.external_make_dirs))
-
-        if len(obj.parallel_external_make_dirs):
-            fh.write('PARALLEL_DIRS += %s\n' %
-                ' '.join(obj.parallel_external_make_dirs))
-            self._traversal.add(backend_file.relobjdir,
-                                parallel=relativize(obj.parallel_external_make_dirs))
-
         # The directory needs to be registered whether subdirectories have been
         # registered or not.
         self._traversal.add(backend_file.relobjdir)
diff --git a/python/mozbuild/mozbuild/frontend/data.py b/python/mozbuild/mozbuild/frontend/data.py
index 2820611056dd..4ad4ba1d63ee 100644
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -102,8 +102,6 @@ class DirectoryTraversal(SandboxDerived):
         'test_tool_dirs',
         'tier_dirs',
         'tier_static_dirs',
-        'external_make_dirs',
-        'parallel_external_make_dirs',
     )
 
     def __init__(self, sandbox):
@@ -116,8 +114,6 @@ class DirectoryTraversal(SandboxDerived):
         self.test_tool_dirs = []
         self.tier_dirs = OrderedDict()
         self.tier_static_dirs = OrderedDict()
-        self.external_make_dirs = []
-        self.parallel_external_make_dirs = []
 
 
 class BaseConfigSubstitution(SandboxDerived):
diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py
index 27a9bfda88a6..d4ffb481f841 100644
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -495,8 +495,6 @@ class TreeMetadataEmitter(LoggingMixin):
         o.tool_dirs = sandbox.get('TOOL_DIRS', [])
         o.test_dirs = sandbox.get('TEST_DIRS', [])
         o.test_tool_dirs = sandbox.get('TEST_TOOL_DIRS', [])
-        o.external_make_dirs = sandbox.get('EXTERNAL_MAKE_DIRS', [])
-        o.parallel_external_make_dirs = sandbox.get('PARALLEL_EXTERNAL_MAKE_DIRS', [])
         o.is_tool_dir = sandbox.get('IS_TOOL_DIR', False)
         o.affected_tiers = sandbox.get_affected_tiers()
 
diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py
index 98613fead466..76ee9d0c866e 100644
--- a/python/mozbuild/mozbuild/frontend/reader.py
+++ b/python/mozbuild/mozbuild/frontend/reader.py
@@ -760,8 +760,8 @@ class BuildReader(object):
             gyp_sandboxes.extend(read_from_gyp(self.config,
                                                mozpath.join(curdir, gyp_dir.input),
                                                mozpath.join(sandbox['OBJDIR'],
-                                               target_dir),
-                                 gyp_dir.variables))
+                                                            target_dir),
+                                               gyp_dir.variables))
 
         # Add the gyp subdirectories to DIRS. We don't care about trying to
         # place some of them in PARALLEL_DIRS because they're only going to be
diff --git a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
index 520fb02666ce..8c70a9589524 100644
--- a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
@@ -384,18 +384,6 @@ VARIABLES = {
         populated by calling add_tier_dir().
         """, None),
 
-    'EXTERNAL_MAKE_DIRS': (list, list,
-        """Directories that build with make but don't use moz.build files.
-
-        This is like ``DIRS`` except it implies that ``make`` is used to build the
-        directory and that the directory does not define itself with moz.build
-        files.
-        """, None),
-
-    'PARALLEL_EXTERNAL_MAKE_DIRS': (list, list,
-        """Parallel version of ``EXTERNAL_MAKE_DIRS``.
-        """, None),
-
     'CONFIGURE_SUBST_FILES': (StrictOrderingOnAppendList, list,
         """Output files that will be generated using configure-like substitution.
 
diff --git a/python/mozbuild/mozbuild/test/backend/common.py b/python/mozbuild/mozbuild/test/backend/common.py
index 706eadd34d9d..7da9ac11330d 100644
--- a/python/mozbuild/mozbuild/test/backend/common.py
+++ b/python/mozbuild/mozbuild/test/backend/common.py
@@ -42,11 +42,6 @@ CONFIGS = {
             ('MOZ_BAR', 'bar'),
         ],
     },
-    'external_make_dirs': {
-        'defines': [],
-        'non_global_defines': [],
-        'substs': [],
-    },
     'substitute_config_files': {
         'defines': [],
         'non_global_defines': [],
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/Makefile.in
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/dir/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/dir/Makefile.in
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/dir/moz.build b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/dir/moz.build
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/external/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/external/Makefile.in
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/external/moz.build b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/external/moz.build
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/moz.build b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/moz.build
deleted file mode 100644
index c3faea5a47f5..000000000000
--- a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# Any copyright is dedicated to the Public Domain.
-# http://creativecommons.org/publicdomain/zero/1.0/
-
-DIRS = ['dir']
-PARALLEL_DIRS = ['p_dir']
-EXTERNAL_MAKE_DIRS = ['external']
-PARALLEL_EXTERNAL_MAKE_DIRS = ['p_external']
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_dir/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_dir/Makefile.in
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_dir/moz.build b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_dir/moz.build
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_external/Makefile.in b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_external/Makefile.in
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_external/moz.build b/python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_external/moz.build
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
index c913eb6c2c59..005c358a7bdf 100644
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -235,19 +235,6 @@ class TestRecursiveMakeBackend(BackendTester):
         self.assertEqual(os.path.getmtime(makefile_path), makefile_mtime)
         self.assertEqual(os.path.getmtime(backend_path), backend_mtime)
 
-    def test_external_make_dirs(self):
-        """Ensure we have make recursion into external make directories."""
-        env = self._consume('external_make_dirs', RecursiveMakeBackend)
-
-        backend_path = mozpath.join(env.topobjdir, 'backend.mk')
-        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
-        self.assertEqual(lines, [
-            'DIRS := dir',
-            'PARALLEL_DIRS := p_dir',
-            'DIRS += external',
-            'PARALLEL_DIRS += p_external',
-        ])
-
     def test_substitute_config_files(self):
         """Ensure substituted config files are produced."""
         env = self._consume('substitute_config_files', RecursiveMakeBackend)
diff --git a/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/moz.build b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/moz.build
index d3023636a45d..260f5be06674 100644
--- a/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/moz.build
+++ b/python/mozbuild/mozbuild/test/frontend/data/traversal-all-vars/moz.build
@@ -7,6 +7,3 @@ PARALLEL_DIRS = ['parallel']
 TEST_DIRS = ['test']
 TEST_TOOL_DIRS = ['test_tool']
 TOOL_DIRS = ['tool']
-EXTERNAL_MAKE_DIRS = ['external_make']
-PARALLEL_EXTERNAL_MAKE_DIRS = ['parallel_external_make']
-
diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
index b84d5d82a7cc..8da848d565c5 100644
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -102,9 +102,6 @@ class TestEmitterBasic(unittest.TestCase):
                 self.assertEqual(o.test_dirs, ['test'])
                 self.assertEqual(o.test_tool_dirs, ['test_tool'])
                 self.assertEqual(o.tool_dirs, ['tool'])
-                self.assertEqual(o.external_make_dirs, ['external_make'])
-                self.assertEqual(o.parallel_external_make_dirs,
-                    ['parallel_external_make'])
 
     def test_tier_simple(self):
         reader = self.reader('traversal-tier-simple')
diff --git a/python/mozbuild/mozbuild/test/frontend/test_sandbox.py b/python/mozbuild/mozbuild/test/frontend/test_sandbox.py
index f83b78b905da..4bca7010099f 100644
--- a/python/mozbuild/mozbuild/test/frontend/test_sandbox.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_sandbox.py
@@ -285,14 +285,6 @@ add_tier_dir('t1', 'bat', static=True)
 
         self.assertEqual(sandbox['DIRS'], ['foo'])
 
-    def test_external_make_dirs(self):
-        sandbox = self.sandbox()
-        sandbox.exec_source('EXTERNAL_MAKE_DIRS += ["foo"]', 'test.py')
-        sandbox.exec_source('PARALLEL_EXTERNAL_MAKE_DIRS += ["bar"]', 'test.py')
-
-        self.assertEqual(sandbox['EXTERNAL_MAKE_DIRS'], ['foo'])
-        self.assertEqual(sandbox['PARALLEL_EXTERNAL_MAKE_DIRS'], ['bar'])
-
     def test_error(self):
         sandbox = self.sandbox()
 

From f228b7ff3f819153a84b2ed88271b78fc8ae9ed4 Mon Sep 17 00:00:00 2001
From: Dave Hunt 
Date: Thu, 12 Dec 2013 10:26:37 -0500
Subject: [PATCH 019/459] Bug 949401 - Marionette python client is missing
 resources from latest marionette_client 0.7.0, r=mdas

---
 testing/marionette/client/MANIFEST.in | 1 +
 1 file changed, 1 insertion(+)

diff --git a/testing/marionette/client/MANIFEST.in b/testing/marionette/client/MANIFEST.in
index 091d4d73a649..9e70dc42a2f6 100644
--- a/testing/marionette/client/MANIFEST.in
+++ b/testing/marionette/client/MANIFEST.in
@@ -1,2 +1,3 @@
 recursive-include marionette/touch *.js
+recursive-include marionette/runner/mixins/resources *
 exclude MANIFEST.in

From 6070fe07e3ba89aacc3ee0fb521364beda1aab1d Mon Sep 17 00:00:00 2001
From: Dave Hunt 
Date: Thu, 12 Dec 2013 10:30:17 -0500
Subject: [PATCH 020/459] Bug 949406 - Bump marionette_client version to 0.7.1,
 r=mdas

---
 testing/marionette/client/setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/testing/marionette/client/setup.py b/testing/marionette/client/setup.py
index 6d8443d80464..bdd2d5bb0463 100644
--- a/testing/marionette/client/setup.py
+++ b/testing/marionette/client/setup.py
@@ -1,7 +1,7 @@
 import os
 from setuptools import setup, find_packages
 
-version = '0.7.0'
+version = '0.7.1'
 
 # get documentation from the README
 try:

From 3c69d60dd6f5d38ec12abfe9f13340c8f2048363 Mon Sep 17 00:00:00 2001
From: Hannes Verschore 
Date: Thu, 12 Dec 2013 16:43:52 +0100
Subject: [PATCH 021/459] Bug 879402 - Use template object to faster set the
 input and index properties on CreateRegExpMatchResult, r=bhackett

---
 js/src/builtin/RegExp.cpp  | 41 ++++++++++++++++++++++----------------
 js/src/jsarray.cpp         | 32 +++++++++++++++++++++++++++++
 js/src/jsarray.h           |  7 +++++++
 js/src/jsobjinlines.h      | 14 ++++++++++++-
 js/src/vm/RegExpObject.cpp | 39 +++++++++++++++++++++++++++++++++++-
 js/src/vm/RegExpObject.h   | 10 ++++++++++
 6 files changed, 124 insertions(+), 19 deletions(-)

diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp
index 4c817d66dc3b..15dded23cbdb 100644
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -18,13 +18,6 @@ using namespace js::types;
 
 using mozilla::ArrayLength;
 
-static inline bool
-DefinePropertyHelper(JSContext *cx, HandleObject obj, Handle name, HandleValue v)
-{
-    return !!baseops::DefineProperty(cx, obj, name, v,
-                                     JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
-}
-
 bool
 js::CreateRegExpMatchResult(JSContext *cx, HandleString input_, const jschar *chars, size_t length,
                             MatchPairs &matches, MutableHandleValue rval)
@@ -69,22 +62,36 @@ js::CreateRegExpMatchResult(JSContext *cx, HandleString input_, const jschar *ch
         }
     }
 
+    /* Get the templateObject that defines the shape and type of the output object */
+    JSObject *templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
+    if (!templateObject)
+        return false;
+
     /* Copy the rooted vector into the array object. */
-    RootedObject array(cx, NewDenseCopiedArray(cx, elements.length(), elements.begin()));
-    if (!array)
-        return false;
+    RootedObject arr(cx, NewDenseCopiedArrayWithTemplate(cx, elements.length(), elements.begin(),
+                                                         templateObject));
 
-    /* Set the |index| property. */
+    /* Set the |index| property. (TemplateObject positions it in slot 0) */
     RootedValue index(cx, Int32Value(matches[0].start));
-    if (!DefinePropertyHelper(cx, array, cx->names().index, index))
-        return false;
+    arr->nativeSetSlot(0, index);
 
-    /* Set the |input| property. */
+    /* Set the |input| property. (TemplateObject positions it in slot 1) */
     RootedValue inputVal(cx, StringValue(input));
-    if (!DefinePropertyHelper(cx, array, cx->names().input, inputVal))
-        return false;
+    arr->nativeSetSlot(1, inputVal);
 
-    rval.setObject(*array);
+#ifdef DEBUG
+    RootedValue test(cx);
+    RootedId id(cx, NameToId(cx->names().index));
+    if (!baseops::GetProperty(cx, arr, id, &test))
+        return false;
+    JS_ASSERT(test == index);
+    id = NameToId(cx->names().input);
+    if (!baseops::GetProperty(cx, arr, id, &test))
+        return false;
+    JS_ASSERT(test == inputVal);
+#endif
+
+    rval.setObject(*arr);
     return true;
 }
 
diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp
index 002af3e2435c..85b3f2b0b194 100644
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3295,6 +3295,38 @@ js::NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values,
     return arr;
 }
 
+ArrayObject *
+js::NewDenseCopiedArrayWithTemplate(JSContext *cx, uint32_t length, const Value *values,
+                                    JSObject *templateObject)
+{
+    gc::AllocKind allocKind = GuessArrayGCKind(length);
+    JS_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
+    allocKind = GetBackgroundAllocKind(allocKind);
+
+    RootedTypeObject type(cx, templateObject->type());
+    if (!type)
+        return nullptr;
+
+    RootedShape shape(cx, templateObject->lastProperty());
+    if (!shape)
+        return nullptr;
+
+    gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
+    Rooted arr(cx, JSObject::createArray(cx, allocKind, heap, shape, type, length));
+    if (!arr)
+        return nullptr;
+
+    if (!EnsureNewArrayElements(cx, arr, length))
+        return nullptr;
+
+    arr->setDenseInitializedLength(length);
+    arr->initDenseElements(0, values, length);
+
+    probes::CreateObject(cx, arr);
+
+    return arr;
+}
+
 #ifdef DEBUG
 bool
 js_ArrayInfo(JSContext *cx, unsigned argc, Value *vp)
diff --git a/js/src/jsarray.h b/js/src/jsarray.h
index f41faba728fd..cf64ffbefc4d 100644
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -70,6 +70,13 @@ extern ArrayObject *
 NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values, JSObject *proto = nullptr,
                     NewObjectKind newKind = GenericObject);
 
+/*
+ * Create a dense array based on templateObject from the given array values,
+ * which must be rooted.
+ */
+extern ArrayObject *
+NewDenseCopiedArrayWithTemplate(JSContext *cx, uint32_t length, const Value *values,
+                                JSObject *templateObject);
 /*
  * Determines whether a write to the given element on |obj| should fail because
  * |obj| is an Array with a non-writable length, and writing that element would
diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h
index 2eb4e64ce7bd..42c5d31eeb60 100644
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -500,12 +500,24 @@ JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::
     if (!obj)
         return nullptr;
 
+    js::HeapSlot *slots = nullptr;
+    if (size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan())) {
+        slots = cx->pod_malloc(nDynamicSlots);
+        if (!slots)
+            return nullptr;
+        js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
+    }
+
     obj->shape_.init(shape);
     obj->type_.init(type);
-    obj->slots = nullptr;
+    obj->slots = slots;
     obj->setFixedElements();
     new (obj->getElementsHeader()) js::ObjectElements(capacity, length);
 
+    size_t span = shape->slotSpan();
+    if (span)
+        obj->initializeSlotRange(0, span);
+
     return &obj->as();
 }
 
diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp
index 36edd78f11bf..f3ac68f68dff 100644
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -20,6 +20,8 @@
 #include "vm/Shape-inl.h"
 
 using namespace js;
+
+using mozilla::DebugOnly;
 using js::frontend::TokenStream;
 
 JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
@@ -630,7 +632,7 @@ RegExpShared::executeMatchOnly(JSContext *cx, const jschar *chars, size_t length
 /* RegExpCompartment */
 
 RegExpCompartment::RegExpCompartment(JSRuntime *rt)
-  : map_(rt), inUse_(rt)
+  : map_(rt), inUse_(rt), matchResultTemplateObject_(nullptr)
 {}
 
 RegExpCompartment::~RegExpCompartment()
@@ -639,6 +641,39 @@ RegExpCompartment::~RegExpCompartment()
     JS_ASSERT(inUse_.empty());
 }
 
+HeapPtrObject &
+RegExpCompartment::getOrCreateMatchResultTemplateObject(JSContext *cx)
+{
+    if (matchResultTemplateObject_)
+        return matchResultTemplateObject_;
+
+    /* Create template array object */
+    RootedObject templateObject(cx, NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
+
+    /* Set dummy index property */
+    RootedValue index(cx, Int32Value(0));
+    if (!baseops::DefineProperty(cx, templateObject, cx->names().index, index,
+                                 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
+        return matchResultTemplateObject_; // = nullptr
+
+    /* Set dummy input property */
+    RootedValue inputVal(cx, StringValue(cx->runtime()->emptyString));
+    if (!baseops::DefineProperty(cx, templateObject, cx->names().input, inputVal,
+                                 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
+        return matchResultTemplateObject_; // = nullptr
+
+    // Make sure that the properties are in the right slots.
+    DebugOnly shape = templateObject->lastProperty();
+    JS_ASSERT(shape->previous()->slot() == 0 &&
+              shape->previous()->propidRef() == NameToId(cx->names().index));
+    JS_ASSERT(shape->slot() == 1 &&
+              shape->propidRef() == NameToId(cx->names().input));
+
+    matchResultTemplateObject_ = templateObject;
+
+    return matchResultTemplateObject_;
+}
+
 bool
 RegExpCompartment::init(JSContext *cx)
 {
@@ -669,6 +704,8 @@ RegExpCompartment::sweep(JSRuntime *rt)
             e.removeFront();
         }
     }
+
+    matchResultTemplateObject_ = nullptr;
 }
 
 void
diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h
index f80541e3255a..02e6ea881ac2 100644
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -318,6 +318,13 @@ class RegExpCompartment
     typedef HashSet, RuntimeAllocPolicy> PendingSet;
     PendingSet inUse_;
 
+    /*
+     * This is the template object where the result of re.exec() is based on,
+     * if there is a result. This is used in CreateRegExpMatchResult to set
+     * the input/index properties faster.
+     */
+    HeapPtrObject matchResultTemplateObject_;
+
   public:
     RegExpCompartment(JSRuntime *rt);
     ~RegExpCompartment();
@@ -331,6 +338,9 @@ class RegExpCompartment
     /* Like 'get', but compile 'maybeOpt' (if non-null). */
     bool get(JSContext *cx, HandleAtom source, JSString *maybeOpt, RegExpGuard *g);
 
+    /* Get or create template object used to base the result of .exec() on. */
+    HeapPtrObject &getOrCreateMatchResultTemplateObject(JSContext *cx);
+
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 

From 2d7afabc14c8adbb8f48b951d2786176139d826f Mon Sep 17 00:00:00 2001
From: Paul Adenot 
Date: Mon, 9 Dec 2013 20:54:49 +0100
Subject: [PATCH 022/459] Bug 944707 - Stop locking when getting the preferred
 samplerate from the AudioStream. r=kinetik

--HG--
extra : rebase_source : bbf7b305d13f252e7c743bb9abf12da078db865b
---
 content/media/AudioStream.cpp           | 30 +++++++++----------------
 content/media/AudioStream.h             |  7 ++++--
 content/media/MediaStreamGraph.cpp      |  7 ++++++
 content/media/MediaStreamGraphImpl.h    |  4 ++++
 content/media/webaudio/AudioContext.cpp | 13 ++++++++++-
 5 files changed, 39 insertions(+), 22 deletions(-)

diff --git a/content/media/AudioStream.cpp b/content/media/AudioStream.cpp
index 9e02f77cb2df..0955272938a7 100644
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -76,6 +76,15 @@ bool AudioStream::sCubebLatencyPrefSet;
   return GetCubebContextUnlocked();
 }
 
+/*static*/ void AudioStream::InitPreferredSampleRate()
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (sPreferredSampleRate != 0 ||
+      cubeb_get_preferred_sample_rate(GetCubebContextUnlocked(), &sPreferredSampleRate) != CUBEB_OK) {
+    sPreferredSampleRate = 44100;
+  }
+}
+
 /*static*/ cubeb* AudioStream::GetCubebContextUnlocked()
 {
   sMutex.AssertCurrentThreadOwns();
@@ -266,25 +275,8 @@ int64_t AudioStream::GetWritten()
 
 /*static*/ int AudioStream::PreferredSampleRate()
 {
-  const int fallbackSampleRate = 44100;
-  StaticMutexAutoLock lock(sMutex);
-  if (sPreferredSampleRate != 0) {
-    return sPreferredSampleRate;
-  }
-
-  cubeb* cubebContext = GetCubebContextUnlocked();
-  if (!cubebContext) {
-    sPreferredSampleRate = fallbackSampleRate;
-  }
-  // Get the preferred samplerate for this platform, or fallback to something
-  // sensible if we fail. We cache the value, because this might be accessed
-  // often, and the complexity of the function call below depends on the
-  // backend used.
-  if (cubeb_get_preferred_sample_rate(cubebContext,
-                                      &sPreferredSampleRate) != CUBEB_OK) {
-    sPreferredSampleRate = fallbackSampleRate;
-  }
-
+  MOZ_ASSERT(sPreferredSampleRate,
+             "sPreferredSampleRate has not been initialized!");
   return sPreferredSampleRate;
 }
 
diff --git a/content/media/AudioStream.h b/content/media/AudioStream.h
index fc040020c32a..2739de44082c 100644
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -179,8 +179,11 @@ public:
   // Returns the maximum number of channels supported by the audio hardware.
   static int MaxNumberOfChannels();
 
-  // Returns the samplerate the systems prefer, because it is the
-  // samplerate the hardware/mixer supports.
+  // Queries the samplerate the hardware/mixer runs at, and stores it.
+  // Can be called on any thread. When this returns, it is safe to call
+  // PreferredSampleRate without locking.
+  static void InitPreferredSampleRate();
+  // Get the aformentionned sample rate. Does not lock.
   static int PreferredSampleRate();
 
   AudioStream();
diff --git a/content/media/MediaStreamGraph.cpp b/content/media/MediaStreamGraph.cpp
index 671db6eeaf2f..db8fd8c577c3 100644
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1328,6 +1328,12 @@ MediaStreamGraphImpl::ForceShutDown()
   }
 }
 
+void
+MediaStreamGraphImpl::Init()
+{
+  AudioStream::InitPreferredSampleRate();
+}
+
 namespace {
 
 class MediaStreamGraphInitThreadRunnable : public nsRunnable {
@@ -1340,6 +1346,7 @@ public:
   {
     char aLocal;
     profiler_register_thread("MediaStreamGraph", &aLocal);
+    mGraph->Init();
     mGraph->RunThread();
     return NS_OK;
   }
diff --git a/content/media/MediaStreamGraphImpl.h b/content/media/MediaStreamGraphImpl.h
index c8cd27a8b1be..76b036e1c4df 100644
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -150,6 +150,10 @@ public:
    */
   void ShutdownThreads();
 
+  /**
+   * Called before the thread runs.
+   */
+  void Init();
   // The following methods run on the graph thread (or possibly the main thread if
   // mLifecycleState > LIFECYCLE_RUNNING)
   /**
diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp
index c757d574a6b4..f269163652f7 100644
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -34,6 +34,7 @@
 #include "ConvolverNode.h"
 #include "OscillatorNode.h"
 #include "nsNetUtil.h"
+#include "AudioStream.h"
 
 namespace mozilla {
 namespace dom {
@@ -65,12 +66,22 @@ NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 static uint8_t gWebAudioOutputKey;
 
+float GetSampleRateForAudioContext(bool aIsOffline, float aSampleRate)
+{
+  if (aIsOffline) {
+    return aSampleRate;
+  } else {
+    AudioStream::InitPreferredSampleRate();
+    return static_cast(AudioStream::PreferredSampleRate());
+  }
+}
+
 AudioContext::AudioContext(nsPIDOMWindow* aWindow,
                            bool aIsOffline,
                            uint32_t aNumberOfChannels,
                            uint32_t aLength,
                            float aSampleRate)
-  : mSampleRate(aIsOffline ? aSampleRate : IdealAudioRate())
+  : mSampleRate(GetSampleRateForAudioContext(aIsOffline, aSampleRate))
   , mNumberOfChannels(aNumberOfChannels)
   , mIsOffline(aIsOffline)
   , mIsStarted(!aIsOffline)

From 371ad746eb72aac3fd9e38b71eec450c12f49d29 Mon Sep 17 00:00:00 2001
From: Paul Adenot 
Date: Thu, 12 Dec 2013 15:31:51 +0100
Subject: [PATCH 023/459] Bug 916384 - Stop calling onaudioprocess on the
 ScriptProcessorNode it has no inputs or outputs. r=roc

Quoting the spec: "audioprocess events are only dispatched if the
ScriptProcessorNode has at least one input or one output connected".

--HG--
extra : rebase_source : bbf52a183a55b75d394a885624a4080a9467fac4
---
 content/media/MediaStreamGraph.h              |  8 +++++
 .../media/webaudio/ScriptProcessorNode.cpp    | 31 +++++++++++++++++
 content/media/webaudio/test/mochitest.ini     |  1 +
 .../test_scriptProcessorNodeNotConnected.html | 33 +++++++++++++++++++
 ...st_scriptProcessorNodeZeroInputOutput.html |  2 ++
 5 files changed, 75 insertions(+)
 create mode 100644 content/media/webaudio/test/test_scriptProcessorNodeNotConnected.html

diff --git a/content/media/MediaStreamGraph.h b/content/media/MediaStreamGraph.h
index 928713309b77..8d4c802100f3 100644
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -445,6 +445,10 @@ public:
   {
     mConsumers.RemoveElement(aPort);
   }
+  uint32_t ConsumerCount()
+  {
+    return mConsumers.Length();
+  }
   const StreamBuffer& GetStreamBuffer() { return mBuffer; }
   GraphTime GetStreamBufferStartTime() { return mBufferStartTime; }
   /**
@@ -944,6 +948,10 @@ public:
   {
     return mInputs.Contains(aPort);
   }
+  uint32_t InputPortCount()
+  {
+    return mInputs.Length();
+  }
   virtual void DestroyImpl();
   /**
    * This gets called after we've computed the blocking states for all
diff --git a/content/media/webaudio/ScriptProcessorNode.cpp b/content/media/webaudio/ScriptProcessorNode.cpp
index 5b31c878e1da..94f5720dda74 100644
--- a/content/media/webaudio/ScriptProcessorNode.cpp
+++ b/content/media/webaudio/ScriptProcessorNode.cpp
@@ -67,6 +67,13 @@ private:
       return front;
     }
 
+    // Empties the buffer queue.
+    void Clear()
+    {
+      mMutex.AssertCurrentThreadOwns();
+      mBufferList.clear();
+    }
+
   private:
     typedef std::deque BufferList;
 
@@ -167,6 +174,18 @@ public:
     return mDelaySoFar == TRACK_TICKS_MAX ? 0 : mDelaySoFar;
   }
 
+  void Reset()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    mDelaySoFar = TRACK_TICKS_MAX;
+    mLatency = 0.0f;
+    {
+      MutexAutoLock lock(mOutputQueue.Lock());
+      mOutputQueue.Clear();
+    }
+    mLastEventTime = TimeStamp();
+  }
+
 private:
   OutputQueue mOutputQueue;
   // How much delay we've seen so far.  This measures the amount of delay
@@ -224,6 +243,18 @@ public:
       return;
     }
 
+    // This node is not connected to anything. Per spec, we don't fire the
+    // onaudioprocess event. We also want to clear out the input and output
+    // buffer queue, and output a null buffer.
+    if (!(aStream->ConsumerCount() ||
+          aStream->AsProcessedStream()->InputPortCount())) {
+      aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
+      mSharedBuffers->Reset();
+      mSeenNonSilenceInput = false;
+      mInputWriteIndex = 0;
+      return;
+    }
+
     // First, record our input buffer
     for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
       if (aInput.IsNull()) {
diff --git a/content/media/webaudio/test/mochitest.ini b/content/media/webaudio/test/mochitest.ini
index 4f90f304440e..2a7590942710 100644
--- a/content/media/webaudio/test/mochitest.ini
+++ b/content/media/webaudio/test/mochitest.ini
@@ -108,6 +108,7 @@ support-files =
 [test_scriptProcessorNode.html]
 [test_scriptProcessorNodeChannelCount.html]
 [test_scriptProcessorNodeZeroInputOutput.html]
+[test_scriptProcessorNodeNotConnected.html]
 [test_singleSourceDest.html]
 [test_waveShaper.html]
 [test_waveShaperNoCurve.html]
diff --git a/content/media/webaudio/test/test_scriptProcessorNodeNotConnected.html b/content/media/webaudio/test/test_scriptProcessorNodeNotConnected.html
new file mode 100644
index 000000000000..b2a0e97f8c33
--- /dev/null
+++ b/content/media/webaudio/test/test_scriptProcessorNodeNotConnected.html
@@ -0,0 +1,33 @@
+
+
+
+  Test AudioBufferSourceNode: should not fire audioprocess if not connected.
+  
+  
+  
+
+
+
+
+
+ + diff --git a/content/media/webaudio/test/test_scriptProcessorNodeZeroInputOutput.html b/content/media/webaudio/test/test_scriptProcessorNodeZeroInputOutput.html index 393ce4b385af..6ac8beda0a54 100644 --- a/content/media/webaudio/test/test_scriptProcessorNodeZeroInputOutput.html +++ b/content/media/webaudio/test/test_scriptProcessorNodeZeroInputOutput.html @@ -28,7 +28,9 @@ addLoadEvent(function() { SimpleTest.finish(); }; + sp.connect(context.destination); }; + sp.connect(context.destination); }); From e567c68e9131f48f0346be8f1b9c8b545e809079 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 12 Dec 2013 11:25:41 -0500 Subject: [PATCH 024/459] Backed out changeset a2e719af19a2 (bug 948777) because of mochitest-3 crashes on Windows XP --- netwerk/sctp/src/moz.build | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/netwerk/sctp/src/moz.build b/netwerk/sctp/src/moz.build index b4186410660e..1d7f90da74de 100644 --- a/netwerk/sctp/src/moz.build +++ b/netwerk/sctp/src/moz.build @@ -10,7 +10,7 @@ EXPORTS.mozilla.net += [ 'usrsctp.h', ] -UNIFIED_SOURCES += [ +SOURCES += [ 'netinet/sctp_asconf.c', 'netinet/sctp_auth.c', 'netinet/sctp_bsd_addr.c', @@ -20,12 +20,14 @@ UNIFIED_SOURCES += [ 'netinet/sctp_hashdriver.c', 'netinet/sctp_indata.c', 'netinet/sctp_input.c', + 'netinet/sctp_output.c', 'netinet/sctp_pcb.c', 'netinet/sctp_peeloff.c', 'netinet/sctp_sha1.c', 'netinet/sctp_ss_functions.c', 'netinet/sctp_sysctl.c', 'netinet/sctp_timer.c', + 'netinet/sctp_userspace.c', 'netinet/sctp_usrreq.c', 'netinet/sctputil.c', 'netinet6/sctp6_usrreq.c', @@ -33,19 +35,9 @@ UNIFIED_SOURCES += [ 'user_mbuf.c', 'user_recv_thread.c', 'user_sctp_timer_iterate.c', -] - -# These files cannot be built in unified mode because they rely on __FAVOR_BSD in udp.h. -SOURCES += [ - 'netinet/sctp_output.c', 'user_socket.c', ] -# This file cannot be built in unified mode because of compilation failures on Windows. -SOURCES += [ - 'netinet/sctp_userspace.c', -] - if CONFIG['OS_TARGET'] == 'Android': SOURCES += [ 'ifaddrs_android.cpp', From e9e9c7d188f476b0c2c6a3c897ecc92a24a59145 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 12 Dec 2013 11:26:24 -0500 Subject: [PATCH 025/459] Backed out changeset 6d9cf51a012a (bug 949360) because of mochitest-3 timeouts on a CLOSED TREE --- gfx/layers/ImageContainer.cpp | 16 ++++++---------- gfx/layers/ipc/SharedPlanarYCbCrImage.cpp | 20 ++++++++------------ 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index e977f69143be..8d51ce7c6944 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -490,17 +490,14 @@ PlanarYCbCrImage::CopyData(const Data& aData) mData = aData; // update buffer size - size_t size = mData.mCbCrStride * mData.mCbCrSize.height * 2 + + mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 + mData.mYStride * mData.mYSize.height; // get new buffer - mBuffer = AllocateBuffer(size); + mBuffer = AllocateBuffer(mBufferSize); if (!mBuffer) return; - // update buffer size - mBufferSize = size; - mData.mYChannel = mBuffer; mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height; mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height; @@ -539,12 +536,11 @@ PlanarYCbCrImage::SetDataNoCopy(const Data &aData) uint8_t* PlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize) { + // update buffer size + mBufferSize = aSize; + // get new buffer - mBuffer = AllocateBuffer(aSize); - if (mBuffer) { - // update buffer size - mBufferSize = aSize; - } + mBuffer = AllocateBuffer(mBufferSize); return mBuffer; } diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp index 3f7e969a1cf6..a842f5d7ee00 100644 --- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp +++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp @@ -126,16 +126,14 @@ SharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize) { NS_ABORT_IF_FALSE(!mTextureClient->IsAllocated(), "This image already has allocated data"); size_t size = YCbCrImageDataSerializer::ComputeMinBufferSize(aSize); - - // get new buffer _without_ setting mBuffer. - if (!mTextureClient->Allocate(size)) { - return nullptr; - } - // update buffer size mBufferSize = size; + // get new buffer _without_ setting mBuffer. + bool status = mTextureClient->Allocate(mBufferSize); + MOZ_ASSERT(status); YCbCrImageDataSerializer serializer(mTextureClient->GetBuffer()); + return serializer.GetData(); } @@ -250,19 +248,17 @@ DeprecatedSharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize) { NS_ABORT_IF_FALSE(!mAllocated, "This image already has allocated data"); size_t size = YCbCrImageDataSerializer::ComputeMinBufferSize(aSize); - - // get new buffer _without_ setting mBuffer. - if (!AllocateBuffer(size)) { - return nullptr; - } - // update buffer size mBufferSize = size; + // get new buffer _without_ setting mBuffer. + AllocateBuffer(mBufferSize); YCbCrImageDataSerializer serializer(mShmem.get()); + return serializer.GetData(); } + void DeprecatedSharedPlanarYCbCrImage::SetDataNoCopy(const Data &aData) { From d7d012bff76d9dcfe2ceff51476e4ae3aaa2333b Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Tue, 10 Dec 2013 22:58:51 -0800 Subject: [PATCH 026/459] Bug 949360 - Check for Image AllocateBuffer() failures. r=nical Relanded on a CLOSED TREE --- gfx/layers/ImageContainer.cpp | 16 ++++++++++------ gfx/layers/ipc/SharedPlanarYCbCrImage.cpp | 20 ++++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index 8d51ce7c6944..e977f69143be 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -490,14 +490,17 @@ PlanarYCbCrImage::CopyData(const Data& aData) mData = aData; // update buffer size - mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 + + size_t size = mData.mCbCrStride * mData.mCbCrSize.height * 2 + mData.mYStride * mData.mYSize.height; // get new buffer - mBuffer = AllocateBuffer(mBufferSize); + mBuffer = AllocateBuffer(size); if (!mBuffer) return; + // update buffer size + mBufferSize = size; + mData.mYChannel = mBuffer; mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height; mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height; @@ -536,11 +539,12 @@ PlanarYCbCrImage::SetDataNoCopy(const Data &aData) uint8_t* PlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize) { - // update buffer size - mBufferSize = aSize; - // get new buffer - mBuffer = AllocateBuffer(mBufferSize); + mBuffer = AllocateBuffer(aSize); + if (mBuffer) { + // update buffer size + mBufferSize = aSize; + } return mBuffer; } diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp index a842f5d7ee00..3f7e969a1cf6 100644 --- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp +++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp @@ -126,14 +126,16 @@ SharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize) { NS_ABORT_IF_FALSE(!mTextureClient->IsAllocated(), "This image already has allocated data"); size_t size = YCbCrImageDataSerializer::ComputeMinBufferSize(aSize); + + // get new buffer _without_ setting mBuffer. + if (!mTextureClient->Allocate(size)) { + return nullptr; + } + // update buffer size mBufferSize = size; - // get new buffer _without_ setting mBuffer. - bool status = mTextureClient->Allocate(mBufferSize); - MOZ_ASSERT(status); YCbCrImageDataSerializer serializer(mTextureClient->GetBuffer()); - return serializer.GetData(); } @@ -248,17 +250,19 @@ DeprecatedSharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize) { NS_ABORT_IF_FALSE(!mAllocated, "This image already has allocated data"); size_t size = YCbCrImageDataSerializer::ComputeMinBufferSize(aSize); + + // get new buffer _without_ setting mBuffer. + if (!AllocateBuffer(size)) { + return nullptr; + } + // update buffer size mBufferSize = size; - // get new buffer _without_ setting mBuffer. - AllocateBuffer(mBufferSize); YCbCrImageDataSerializer serializer(mShmem.get()); - return serializer.GetData(); } - void DeprecatedSharedPlanarYCbCrImage::SetDataNoCopy(const Data &aData) { From 5e3b1f18b34db90d77c9f7f3d2fc0ed278eb5e9f Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Thu, 12 Dec 2013 08:42:24 -0800 Subject: [PATCH 027/459] Bug 932982 - Trace type constraints and allow preserving jitcode in GCs without also marking all type information, r=billm,jandem. --- js/public/MemoryMetrics.h | 1 - js/src/gc/Marking.cpp | 46 +---- js/src/gc/RootMarking.cpp | 5 - js/src/gc/Statistics.cpp | 1 - js/src/gc/Statistics.h | 1 - js/src/gc/Zone.cpp | 31 +-- js/src/gc/Zone.h | 2 - js/src/jit/CodeGenerator.cpp | 6 +- js/src/jit/Ion.cpp | 17 +- js/src/jit/Ion.h | 2 +- js/src/jit/IonBuilder.cpp | 6 +- js/src/jit/IonCode.h | 3 + js/src/jit/MIR.cpp | 2 +- js/src/jscompartment.cpp | 11 +- js/src/jscompartment.h | 3 - js/src/jsgc.cpp | 14 +- js/src/jsgc.h | 7 +- js/src/jsinfer.cpp | 329 +++++++++++++++--------------- js/src/jsinfer.h | 122 ++++------- js/src/jsinferinlines.h | 78 ++----- js/src/vm/MemoryMetrics.cpp | 1 - js/xpconnect/src/XPCJSRuntime.cpp | 4 - 22 files changed, 258 insertions(+), 434 deletions(-) diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index 95ac91b9f6d5..f974259467d5 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -393,7 +393,6 @@ struct CompartmentStats macro(Other, NotLiveGCThing, baselineStubsOptimized) \ macro(Other, NotLiveGCThing, ionData) \ macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \ - macro(Other, NotLiveGCThing, typeInferencePendingArrays) \ macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \ macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \ macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \ diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index b0c8a6555988..d0e3bde29e8a 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1122,14 +1122,11 @@ gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape) static void ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) { - /* Don't mark properties for singletons. They'll be purged by the GC. */ - if (!type->singleton) { - unsigned count = type->getPropertyCount(); - for (unsigned i = 0; i < count; i++) { - types::Property *prop = type->getProperty(i); - if (prop && JSID_IS_STRING(prop->id)) - PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); - } + unsigned count = type->getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + types::Property *prop = type->getProperty(i); + if (prop && JSID_IS_STRING(prop->id)) + PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); } if (TaggedProto(type->proto).isObject()) @@ -1350,7 +1347,7 @@ GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp) } void -GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr) +GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr) { if (tag == TypeTag) { ScanTypeObject(this, reinterpret_cast(addr)); @@ -1364,35 +1361,6 @@ GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t ad pushObject(obj); } else if (tag == IonCodeTag) { MarkChildren(this, reinterpret_cast(addr)); - } else if (tag == ArenaTag) { - ArenaHeader *aheader = reinterpret_cast(addr); - AllocKind thingKind = aheader->getAllocKind(); - size_t thingSize = Arena::thingSize(thingKind); - - for ( ; aheader; aheader = aheader->next) { - Arena *arena = aheader->getArena(); - FreeSpan firstSpan(aheader->getFirstFreeSpan()); - const FreeSpan *span = &firstSpan; - - for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) { - JS_ASSERT(thing <= arena->thingsEnd()); - if (thing == span->first) { - if (!span->hasNext()) - break; - thing = span->last; - span = span->nextSpan(); - } else { - JSObject *object = reinterpret_cast(thing); - if (object->hasSingletonType() && object->markIfUnmarked(getMarkColor())) - pushObject(object); - budget.step(); - } - } - if (budget.isOverBudget()) { - pushArenaList(aheader); - return; - } - } } } @@ -1430,7 +1398,7 @@ GCMarker::processMarkStackTop(SliceBudget &budget) goto scan_obj; } - processMarkStackOther(budget, tag, addr); + processMarkStackOther(tag, addr); return; scan_value_array: diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 8ae3e85fd559..541ad84d996c 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -726,11 +726,6 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting()) continue; - if (IS_GC_MARKING_TRACER(trc) && zone->isPreservingCode()) { - gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_TYPES); - zone->markTypes(trc); - } - /* Do not discard scripts with counts while profiling. */ if (rt->profilingScripts && !rt->isHeapMinorCollecting()) { for (CellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) { diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 7744b61d53f5..be76eee734a1 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -276,7 +276,6 @@ static const PhaseInfo phases[] = { { PHASE_PURGE, "Purge", PHASE_NO_PARENT }, { PHASE_MARK, "Mark", PHASE_NO_PARENT }, { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MARK }, - { PHASE_MARK_TYPES, "Mark Types", PHASE_MARK_ROOTS }, { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK }, { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT }, { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP }, diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 3240fd238f53..1029fd52fe8e 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -28,7 +28,6 @@ enum Phase { PHASE_PURGE, PHASE_MARK, PHASE_MARK_ROOTS, - PHASE_MARK_TYPES, PHASE_MARK_DELAYED, PHASE_SWEEP, PHASE_SWEEP_MARK, diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index ad0c2fae7fde..539faabbd7c8 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -80,35 +80,6 @@ Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon) needsBarrier_ = needs; } -void -Zone::markTypes(JSTracer *trc) -{ - /* - * Mark all scripts, type objects and singleton JS objects in the - * compartment. These can be referred to directly by type sets, which we - * cannot modify while code which depends on these type sets is active. - */ - JS_ASSERT(isPreservingCode()); - - for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { - JSScript *script = i.get(); - MarkScriptRoot(trc, &script, "mark_types_script"); - JS_ASSERT(script == i.get()); - } - - for (size_t thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) { - ArenaHeader *aheader = allocator.arenas.getFirstArena(static_cast(thingKind)); - if (aheader) - trc->runtime->gcMarker.pushArenaList(aheader); - } - - for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { - types::TypeObject *type = i.get(); - MarkTypeObjectRoot(trc, &type, "mark_types_scan"); - JS_ASSERT(type == i.get()); - } -} - void Zone::resetGCMallocBytes() { @@ -144,7 +115,7 @@ Zone::sweep(FreeOp *fop, bool releaseTypes) if (active) releaseTypes = false; - if (!isPreservingCode()) { + { gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); types.sweep(fop, releaseTypes); } diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index ebad06d0b6c9..a2fe6a22a022 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -310,8 +310,6 @@ struct Zone : public JS::shadow::Zone, js_ReportAllocationOverflow(nullptr); } - void markTypes(JSTracer *trc); - js::types::TypeZone types; void sweep(js::FreeOp *fop, bool releaseTypes); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index cc1095ff392b..c15f164ec681 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -5890,7 +5890,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) safepoints_.size(), callTargets.length(), patchableBackedges_.length(), optimizationLevel); if (!ionScript) { - recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); + recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); return false; } @@ -5915,7 +5915,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) // Use js_free instead of IonScript::Destroy: the cache list and // backedge list are still uninitialized. js_free(ionScript); - recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); + recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); return false; } @@ -5936,7 +5936,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) /* resetUses */ false, /* cancelOffThread*/ false)) { js_free(ionScript); - recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); + recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); return false; } } diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 92a1e4aed529..546e31c1ca8c 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2508,7 +2508,7 @@ jit::InvalidateAll(FreeOp *fop, Zone *zone) void -jit::Invalidate(types::TypeCompartment &types, FreeOp *fop, +jit::Invalidate(types::TypeZone &types, FreeOp *fop, const Vector &invalid, bool resetUses, bool cancelOffThread) { @@ -2591,7 +2591,7 @@ void jit::Invalidate(JSContext *cx, const Vector &invalid, bool resetUses, bool cancelOffThread) { - jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses, + jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses, cancelOffThread); } @@ -2638,14 +2638,13 @@ FinishInvalidationOf(FreeOp *fop, JSScript *script, IonScript *ionScript, bool p else script->setIonScript(nullptr); - // If this script has Ion code on the stack, invalidation() will return - // true. In this case we have to wait until destroying it. - if (!ionScript->invalidated()) { - types::TypeCompartment &types = script->compartment()->types; - ionScript->recompileInfo().compilerOutput(types)->invalidate(); + types::TypeZone &types = script->zone()->types; + ionScript->recompileInfo().compilerOutput(types)->invalidate(); + // If this script has Ion code on the stack, invalidated() will return + // true. In this case we have to wait until destroying it. + if (!ionScript->invalidated()) jit::IonScript::Destroy(fop, ionScript); - } } void @@ -2664,8 +2663,6 @@ jit::FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp) // Free optimized baseline stubs. if (comp->jitCompartment()) comp->jitCompartment()->optimizedStubSpace()->free(); - - comp->types.clearCompilerOutputs(fop); } void diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index f516f1e12698..780fc993901f 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -127,7 +127,7 @@ IonExecStatus IonCannon(JSContext *cx, RunState &state); IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args); // Walk the stack and invalidate active Ion frames for the invalid scripts. -void Invalidate(types::TypeCompartment &types, FreeOp *fop, +void Invalidate(types::TypeZone &types, FreeOp *fop, const Vector &invalid, bool resetUses = true, bool cancelOffThread = true); void Invalidate(JSContext *cx, const Vector &invalid, bool resetUses = true, diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 24eeae4a238e..2ca4b692c083 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6321,7 +6321,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc types::HeapTypeSetKey property = staticType->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || - property.configured(constraints(), staticType)) + property.configured(constraints())) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. @@ -6411,7 +6411,7 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name) types::HeapTypeSetKey property = staticType->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || - property.configured(constraints(), staticType)) + property.configured(constraints())) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. @@ -7861,7 +7861,7 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name, *property = type->property(id); return property->maybeTypes() && property->maybeTypes()->definiteProperty() && - !property->configured(constraints(), type); + !property->configured(constraints()); } bool diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index d3ecaa94223e..286cbeb9e882 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -531,6 +531,9 @@ struct IonScript const types::RecompileInfo& recompileInfo() const { return recompileInfo_; } + types::RecompileInfo& recompileInfoRef() { + return recompileInfo_; + } OptimizationLevel optimizationLevel() const { return optimizationLevel_; } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index aad9234a367f..882b468f6a7b 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -3025,7 +3025,7 @@ jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, // Check if the property has been reconfigured or is a getter. types::HeapTypeSetKey property = object->property(NameToId(name)); - if (property.configured(constraints, object)) + if (property.configured(constraints)) return false; } } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index a4132018c96f..f9dd807606db 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -48,7 +48,6 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options = #endif global_(nullptr), enterCompartmentDepth(0), - lastCodeRelease(0), data(nullptr), objectMetadataCallback(nullptr), lastAnimationTime(0), @@ -545,13 +544,6 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes) WeakMapBase::sweepCompartment(this); } - if (zone()->isPreservingCode()) { - gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); - types.sweepShapes(fop); - } else { - JS_ASSERT(!types.constrainedOutputs); - } - NativeIterator *ni = enumerators->next(); while (ni != enumerators) { JSObject *iterObj = ni->iterObj(); @@ -864,7 +856,6 @@ JSCompartment::clearTraps(FreeOp *fop) void JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *tiPendingArrays, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, @@ -876,7 +867,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *baselineStubsOptimized) { *compartmentObject += mallocSizeOf(this); - types.addSizeOfExcludingThis(mallocSizeOf, tiPendingArrays, tiAllocationSiteTables, + types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, tiArrayTypeTables, tiObjectTypeTables); *shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf) + initialShapes.sizeOfExcludingThis(mallocSizeOf) diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 15a93afe1280..f44c2b09e390 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -187,8 +187,6 @@ struct JSCompartment */ void adoptWorkerAllocator(js::Allocator *workerAllocator); - - int64_t lastCodeRelease; bool activeAnalysis; /* Type information about the scripts and objects in this compartment. */ @@ -221,7 +219,6 @@ struct JSCompartment public: void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *tiPendingArrays, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index dd3cde985825..27242bdcdb39 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2854,13 +2854,9 @@ ShouldPreserveJITCode(JSCompartment *comp, int64_t currentTime) if (rt->alwaysPreserveCode) return true; - if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime && - comp->lastCodeRelease + (PRMJ_USEC_PER_SEC * 300) >= currentTime) - { + if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime) return true; - } - comp->lastCodeRelease = currentTime; return false; } @@ -3939,12 +3935,12 @@ BeginSweepingZoneGroup(JSRuntime *rt) bool releaseTypes = ReleaseObservedTypes(rt); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex); - c->sweep(&fop, releaseTypes); + c->sweep(&fop, releaseTypes && !c->zone()->isPreservingCode()); } for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex); - zone->sweep(&fop, releaseTypes); + zone->sweep(&fop, releaseTypes && !zone->isPreservingCode()); } } @@ -5293,10 +5289,6 @@ js::ReleaseAllJITCode(FreeOp *fop) jit::FinishDiscardBaselineScript(fop, script); } } - - /* Sweep now invalidated compiler outputs from each compartment. */ - for (CompartmentsIter comp(fop->runtime(), SkipAtoms); !comp.done(); comp.next()) - comp->types.clearCompilerOutputs(fop); #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 2e5cbe360930..9159f31ec024 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1091,7 +1091,6 @@ struct GCMarker : public JSTracer { ObjectTag, TypeTag, XmlTag, - ArenaTag, SavedValueArrayTag, IonCodeTag, LastTag = IonCodeTag @@ -1119,10 +1118,6 @@ struct GCMarker : public JSTracer { pushTaggedPtr(ObjectTag, obj); } - void pushArenaList(gc::ArenaHeader *firstArena) { - pushTaggedPtr(ArenaTag, firstArena); - } - void pushType(types::TypeObject *type) { pushTaggedPtr(TypeTag, type); } @@ -1229,7 +1224,7 @@ struct GCMarker : public JSTracer { bool restoreValueArray(JSObject *obj, void **vpp, void **endp); void saveValueRanges(); inline void processMarkStackTop(SliceBudget &budget); - void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr); + void processMarkStackOther(uintptr_t tag, uintptr_t addr); void appendGrayRoot(void *thing, JSGCTraceKind kind); diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 0c1fe0167a3c..51c19af54640 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -243,14 +243,6 @@ types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &val if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) return true; - /* - * If we called in here while resolving a type constraint, we may be in the - * middle of resolving a standard class and the type sets will not be updated - * until the outer TypeSet::add finishes. - */ - if (cx->compartment()->types.pendingCount) - return true; - Type type = GetValueType(value); AutoEnterAnalysis enter(cx); @@ -742,12 +734,12 @@ class TypeCompilerConstraint : public TypeConstraint void newType(JSContext *cx, TypeSet *source, Type type) { if (data.invalidateOnNewType(type)) - cx->compartment()->types.addPendingRecompile(cx, compilation); + cx->zone()->types.addPendingRecompile(cx, compilation); } void newPropertyState(JSContext *cx, TypeSet *source) { if (data.invalidateOnNewPropertyState(source)) - cx->compartment()->types.addPendingRecompile(cx, compilation); + cx->zone()->types.addPendingRecompile(cx, compilation); } void newObjectState(JSContext *cx, TypeObject *object) { @@ -755,7 +747,16 @@ class TypeCompilerConstraint : public TypeConstraint // will be sent on changes to its state, so always invalidate any // associated compilations. if (object->unknownProperties() || data.invalidateOnNewObjectState(object)) - cx->compartment()->types.addPendingRecompile(cx, compilation); + cx->zone()->types.addPendingRecompile(cx, compilation); + } + + TypeConstraint *sweep(TypeZone &zone) { + if (data.shouldSweep() || compilation.shouldSweep(zone)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_ >(compilation, data); + if (!res) + zone.setPendingNukeTypes(); + return res; } }; @@ -908,13 +909,21 @@ class TypeConstraintFreezeStack : public TypeConstraint const char *kind() { return "freezeStack"; } - void newType(JSContext *cx, TypeSet *source, Type type) - { + void newType(JSContext *cx, TypeSet *source, Type type) { /* * Unlike TypeConstraintFreeze, triggering this constraint once does * not disable it on future changes to the type set. */ - cx->compartment()->types.addPendingRecompile(cx, script_); + cx->zone()->types.addPendingRecompile(cx, script_); + } + + TypeConstraint *sweep(TypeZone &zone) { + if (IsScriptAboutToBeFinalized(&script_)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_(script_); + if (!res) + zone.setPendingNukeTypes(); + return res; } }; @@ -929,15 +938,22 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu CompilerOutput co(script, executionMode); - TypeCompartment &types = cx->compartment()->types; - if (!types.constrainedOutputs) { - types.constrainedOutputs = cx->new_< Vector >(cx); - if (!types.constrainedOutputs) + TypeZone &types = cx->zone()->types; + if (!types.compilerOutputs) { + types.compilerOutputs = cx->new_< Vector >(cx); + if (!types.compilerOutputs) return false; } - uint32_t index = types.constrainedOutputs->length(); - if (!types.constrainedOutputs->append(co)) +#ifdef DEBUG + for (size_t i = 0; i < types.compilerOutputs->length(); i++) { + const CompilerOutput &co = (*types.compilerOutputs)[i]; + JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode); + } +#endif + + uint32_t index = types.compilerOutputs->length(); + if (!types.compilerOutputs->append(co)) return false; *precompileInfo = RecompileInfo(index); @@ -979,8 +995,8 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu array[i].add(cx, cx->typeLifoAlloc().new_(entry.script), false); } - if (!succeeded || types.constrainedOutputs->back().pendingInvalidation()) { - types.constrainedOutputs->back().invalidate(); + if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) { + types.compilerOutputs->back().invalidate(); script->resetUseCount(); return false; } @@ -1009,6 +1025,8 @@ class ConstraintDataFreeze ? property.maybeTypes()->isSubset(expected) : property.maybeTypes()->empty(); } + + bool shouldSweep() { return false; } }; } /* anonymous namespace */ @@ -1195,6 +1213,8 @@ class ConstraintDataFreezeObjectFlags { return !invalidateOnNewObjectState(property.object()->maybeType()); } + + bool shouldSweep() { return false; } }; } /* anonymous namespace */ @@ -1289,6 +1309,8 @@ class ConstraintDataFreezeObjectForInlinedCall { return true; } + + bool shouldSweep() { return false; } }; // Constraint which triggers recompilation when the template object for a @@ -1315,6 +1337,11 @@ class ConstraintDataFreezeObjectForNewScriptTemplate { return !invalidateOnNewObjectState(property.object()->maybeType()); } + + bool shouldSweep() { + // Note: |templateObject| is only used for equality testing. + return false; + } }; // Constraint which triggers recompilation when the underlying data pointer for @@ -1341,6 +1368,11 @@ class ConstraintDataFreezeObjectForTypedArrayBuffer { return !invalidateOnNewObjectState(property.object()->maybeType()); } + + bool shouldSweep() { + // Note: |viewData| is only used for equality testing. + return false; + } }; } /* anonymous namespace */ @@ -1413,10 +1445,7 @@ namespace { class ConstraintDataFreezeConfiguredProperty { public: - TypeObjectKey *object; - - ConstraintDataFreezeConfiguredProperty(TypeObjectKey *object) - : object(object) + ConstraintDataFreezeConfiguredProperty() {} const char *kind() { return "freezeConfiguredProperty"; } @@ -1430,31 +1459,16 @@ class ConstraintDataFreezeConfiguredProperty bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) { - // Everywhere compiled code depends on definite properties associated - // with a type object's newScript, we need to make sure there are - // constraints in place which will mark those properties as configured - // should the definite properties be invalidated. - TypeObject *type = object->isSingleObject() - ? object->asSingleObject()->type() - : object->asTypeObject(); - if (type->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { - type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; - if (type->hasNewScript()) { - CheckNewScriptProperties(cx, type, type->newScript()->fun); - } else { - JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED); - type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; - } - } - return !property.maybeTypes()->configuredProperty(); } + + bool shouldSweep() { return false; } }; } /* anonymous namespace */ bool -HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type) +HeapTypeSetKey::configured(CompilerConstraintList *constraints) { if (maybeTypes() && maybeTypes()->configuredProperty()) return true; @@ -1463,7 +1477,7 @@ HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *t typedef CompilerConstraintInstance T; constraints->add(alloc->new_(alloc, *this, - ConstraintDataFreezeConfiguredProperty(type))); + ConstraintDataFreezeConfiguredProperty())); return false; } @@ -1986,7 +2000,7 @@ PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj) if (type->unknownProperties()) return true; HeapTypeSetKey index = type->property(JSID_VOID); - if (index.configured(constraints, type) || index.isOwnProperty(constraints)) + if (index.configured(constraints) || index.isOwnProperty(constraints)) return true; obj = obj->getProto(); } while (obj); @@ -2024,27 +2038,8 @@ types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, return PrototypeHasIndexedProperty(constraints, proto); } -bool -TypeCompartment::growPendingArray(JSContext *cx) -{ - unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2); - PendingWork *newArray = js_pod_calloc(newCapacity); - if (!newArray) { - cx->compartment()->types.setPendingNukeTypes(cx); - return false; - } - - PodCopy(newArray, pendingArray, pendingCount); - js_free(pendingArray); - - pendingArray = newArray; - pendingCapacity = newCapacity; - - return true; -} - void -TypeCompartment::processPendingRecompiles(FreeOp *fop) +TypeZone::processPendingRecompiles(FreeOp *fop) { if (!pendingRecompiles) return; @@ -2095,11 +2090,9 @@ TypeZone::nukeTypes(FreeOp *fop) */ JS_ASSERT(pendingNukeTypes); - for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) { - if (comp->types.pendingRecompiles) { - fop->free_(comp->types.pendingRecompiles); - comp->types.pendingRecompiles = nullptr; - } + if (pendingRecompiles) { + fop->free_(pendingRecompiles); + pendingRecompiles = nullptr; } inferenceEnabled = false; @@ -2119,7 +2112,7 @@ TypeZone::nukeTypes(FreeOp *fop) } void -TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) +TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info) { CompilerOutput *co = info.compilerOutput(cx); if (!co || !co->isValid() || co->pendingInvalidation()) @@ -2145,7 +2138,7 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) } void -TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script) +TypeZone::addPendingRecompile(JSContext *cx, JSScript *script) { JS_ASSERT(script); @@ -3146,6 +3139,15 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint } void newType(JSContext *cx, TypeSet *source, Type type) {} + + TypeConstraint *sweep(TypeZone &zone) { + if (IsTypeObjectAboutToBeFinalized(&object)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_(object); + if (!res) + zone.setPendingNukeTypes(); + return res; + } }; bool @@ -3192,6 +3194,15 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint if (source->baseFlags() || source->getObjectCount() > 1) object->clearAddendum(cx); } + + TypeConstraint *sweep(TypeZone &zone) { + if (IsTypeObjectAboutToBeFinalized(&object)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_(object); + if (!res) + zone.setPendingNukeTypes(); + return res; + } }; void @@ -3941,8 +3952,19 @@ ConstraintTypeSet::sweep(Zone *zone) } } - /* All constraints are wiped out on each GC. */ + /* + * Type constraints only hold weak references. Copy constraints referring + * to data that is still live into the zone's new arena. + */ + TypeConstraint *constraint = constraintList; constraintList = nullptr; + while (constraint) { + if (TypeConstraint *copy = constraint->sweep(zone->types)) { + copy->next = constraintList; + constraintList = copy; + } + constraint = constraint->next; + } } inline void @@ -3962,18 +3984,6 @@ TypeObject::clearProperties() inline void TypeObject::sweep(FreeOp *fop) { - if (singleton) { - JS_ASSERT(!hasNewScript()); - - /* - * All properties can be discarded. We will regenerate them as needed - * as code gets reanalyzed. - */ - clearProperties(); - - return; - } - if (!isMarked()) { if (addendum) fop->free_(addendum); @@ -3996,6 +4006,14 @@ TypeObject::sweep(FreeOp *fop) for (unsigned i = 0; i < oldCapacity; i++) { Property *prop = oldArray[i]; if (prop) { + if (singleton && !prop->types.constraintList) { + /* + * Don't copy over properties of singleton objects which + * don't have associated constraints. The contents of these + * type sets will be regenerated as necessary. + */ + continue; + } Property *newProp = typeLifoAlloc.new_(*prop); if (newProp) { Property **pentry = @@ -4015,12 +4033,17 @@ TypeObject::sweep(FreeOp *fop) setBasePropertyCount(propertyCount); } else if (propertyCount == 1) { Property *prop = (Property *) propertySet; - Property *newProp = typeLifoAlloc.new_(*prop); - if (newProp) { - propertySet = (Property **) newProp; - newProp->types.sweep(zone()); + if (singleton && !prop->types.constraintList) { + // Skip, as above. + clearProperties(); } else { - zone()->types.setPendingNukeTypes(); + Property *newProp = typeLifoAlloc.new_(*prop); + if (newProp) { + propertySet = (Property **) newProp; + newProp->types.sweep(zone()); + } else { + zone()->types.setPendingNukeTypes(); + } } } @@ -4028,14 +4051,6 @@ TypeObject::sweep(FreeOp *fop) for (unsigned i = 0; i < basePropertyCount(); i++) JS_ASSERT(propertySet[i]); } - - /* - * The GC will clear out the constraints ensuring the correctness of the - * newScript information, these constraints will need to be regenerated - * the next time we compile code which depends on this info. - */ - if (hasNewScript()) - flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE; } void @@ -4119,52 +4134,6 @@ TypeCompartment::sweep(FreeOp *fop) e.rekeyFront(key); } } - - /* - * The pending array is reset on GC, it can grow large (75+ KB) and is easy - * to reallocate if the compartment becomes active again. - */ - if (pendingArray) - fop->free_(pendingArray); - - pendingArray = nullptr; - pendingCapacity = 0; -} - -void -TypeCompartment::sweepShapes(FreeOp *fop) -{ - /* - * Sweep any weak shape references that may be finalized even if a GC is - * preserving type information. - */ - if (objectTypeTable) { - for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) { - const ObjectTableKey &key = e.front().key(); - ObjectTableEntry &entry = e.front().value(); - - if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) { - fop->free_(key.properties); - fop->free_(entry.types); - e.removeFront(); - } - } - } -} - -void -TypeCompartment::clearCompilerOutputs(FreeOp *fop) -{ - if (constrainedOutputs) { - fop->delete_(constrainedOutputs); - constrainedOutputs = nullptr; - } - - if (pendingRecompiles) { - JS_ASSERT(pendingRecompiles->length() == 0); - fop->delete_(pendingRecompiles); - pendingRecompiles = nullptr; - } } void @@ -4193,7 +4162,6 @@ JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table) TypeCompartment::~TypeCompartment() { - js_free(pendingArray); js_delete(arrayTypeTable); js_delete(objectTypeTable); js_delete(allocationSiteTable); @@ -4212,12 +4180,6 @@ TypeScript::Sweep(FreeOp *fop, JSScript *script) /* Remove constraints and references to dead objects from the persistent type sets. */ for (unsigned i = 0; i < num; i++) typeArray[i].sweep(compartment->zone()); - - /* - * Freeze constraints on stack type sets need to be regenerated the next - * time the script is analyzed. - */ - script->clearHasFreezeConstraints(); } void @@ -4234,20 +4196,10 @@ Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePoo void TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *pendingArrays, size_t *allocationSiteTables, size_t *arrayTypeTables, size_t *objectTypeTables) { - /* Pending arrays are cleared on GC along with the analysis pool. */ - *pendingArrays += mallocSizeOf(pendingArray); - - /* - * TypeCompartment::pendingRecompiles is non-nullptr only while inference - * code is running. - */ - JS_ASSERT(!pendingRecompiles); - if (allocationSiteTable) *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); @@ -4289,6 +4241,8 @@ TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const TypeZone::TypeZone(Zone *zone) : zone_(zone), typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), + compilerOutputs(nullptr), + pendingRecompiles(nullptr), pendingNukeTypes(false), inferenceEnabled(false) { @@ -4296,6 +4250,8 @@ TypeZone::TypeZone(Zone *zone) TypeZone::~TypeZone() { + js_delete(compilerOutputs); + js_delete(pendingRecompiles); } void @@ -4312,11 +4268,21 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize()); oldAlloc.steal(&typeLifoAlloc); - /* - * Sweep analysis information and everything depending on it from the - * compartment, including all remaining mjit code if inference is - * enabled in the compartment. - */ + /* Sweep and find compressed indexes for each compiler output. */ + size_t newCompilerOutputCount = 0; + if (compilerOutputs) { + for (size_t i = 0; i < compilerOutputs->length(); i++) { + CompilerOutput &output = (*compilerOutputs)[i]; + if (output.isValid()) { + JSScript *script = output.script(); + if (IsScriptAboutToBeFinalized(&script)) + output.invalidate(); + else + output.setSweepIndex(newCompilerOutputCount++); + } + } + } + if (inferenceEnabled) { gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI); @@ -4328,6 +4294,21 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) if (releaseTypes) { script->types->destroy(); script->types = nullptr; + + /* + * Freeze constraints on stack type sets need to be + * regenerated the next time the script is analyzed. + */ + script->clearHasFreezeConstraints(); + + JS_ASSERT(!script->hasIonScript()); + JS_ASSERT(!script->hasParallelIonScript()); + } else { + /* Update the recompile indexes in any IonScripts still on the script. */ + if (script->hasIonScript()) + script->ionScript()->recompileInfoRef().shouldSweep(*this); + if (script->hasParallelIonScript()) + script->parallelIonScript()->recompileInfoRef().shouldSweep(*this); } } } @@ -4347,6 +4328,20 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) comp->types.sweep(fop); } + if (compilerOutputs) { + size_t sweepIndex = 0; + for (size_t i = 0; i < compilerOutputs->length(); i++) { + CompilerOutput output = (*compilerOutputs)[i]; + if (output.isValid()) { + JS_ASSERT(sweepIndex == output.sweepIndex()); + output.setSweepIndex(0); + (*compilerOutputs)[sweepIndex++] = output; + } + } + JS_ASSERT(sweepIndex == newCompilerOutputCount); + JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount)); + } + { gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS); for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) { diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 690631dfa7ad..38c3be127e82 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -177,7 +177,7 @@ namespace analyze { namespace types { -class TypeCompartment; +class TypeZone; class TypeSet; class TypeObjectKey; @@ -278,28 +278,10 @@ inline Type GetValueType(const Value &val); * Type information about the values observed within scripts and about the * contents of the heap is accumulated as the program executes. Compilation * accumulates constraints relating type information on the heap with the - * compilations that should be invalidated when those types change. This data - * is periodically cleared to reduce memory usage. - * - * GCs may clear both analysis information and jitcode. Sometimes GCs will - * preserve all information and code, and will not collect any scripts, type - * objects or singleton JS objects. - * - * The following data is cleared by all non-preserving GCs: - * - * - The ScriptAnalysis for each analyzed script and data from each analysis - * pass performed. - * - * - Property type sets for singleton JS objects. - * - * - Type constraints and dead references in all type sets. - * - * The following data is occasionally cleared by non-preserving GCs: - * - * - TypeScripts and their type sets are occasionally destroyed, per a timer. - * - * - When a JSScript or TypeObject is swept, type information for its contents - * is destroyed. + * compilations that should be invalidated when those types change. Type + * information and constraints are allocated in the zone's typeLifoAlloc, + * and on GC all data referring to live things is copied into a new allocator. + * Thus, type set and constraints only hold weak references. */ /* @@ -334,6 +316,12 @@ public: * state. */ virtual void newObjectState(JSContext *cx, TypeObject *object) {} + + /* + * If the data this constraint refers to is still live, copy it into the + * zone's new allocator. Type constraints only hold weak references. + */ + virtual TypeConstraint *sweep(TypeZone &zone) = 0; }; /* Flags and other state stored in TypeSet::flags */ @@ -394,13 +382,6 @@ enum { /* If set, addendum information should not be installed on this object. */ OBJECT_FLAG_ADDENDUM_CLEARED = 0x2, - /* - * If set, type constraints covering the correctness of the newScript - * definite properties need to be regenerated before compiling any jitcode - * which depends on this information. - */ - OBJECT_FLAG_NEW_SCRIPT_REGENERATE = 0x4, - /* * Whether we have ensured all type sets in the compartment contain * ANYOBJECT instead of this object. @@ -869,12 +850,6 @@ struct TypeTypedObject : public TypeObjectAddendum * information is sensitive to changes in the property's type. Future changes * to the property (whether those uncovered by analysis or those occurring * in the VM) will treat these properties like those of any other type object. - * - * When a GC occurs, we wipe out all analysis information for all the - * compartment's scripts, so can destroy all properties on singleton type - * objects at the same time. If there is no reference on the stack to the - * type object itself, the type object is also destroyed, and the JS object - * reverts to having a lazy type. */ /* Type information about an object accessed by a script. */ @@ -1316,7 +1291,7 @@ class HeapTypeSetKey void freeze(CompilerConstraintList *constraints); JSValueType knownTypeTag(CompilerConstraintList *constraints); - bool configured(CompilerConstraintList *constraints, TypeObjectKey *type); + bool configured(CompilerConstraintList *constraints); bool isOwnProperty(CompilerConstraintList *constraints); bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other); JSObject *singleton(CompilerConstraintList *constraints); @@ -1339,6 +1314,10 @@ class CompilerOutput // Whether this compilation is about to be invalidated. bool pendingInvalidation_ : 1; + // During sweeping, the list of compiler outputs is compacted and invalidated + // outputs are removed. This gives the new index for a valid compiler output. + uint32_t sweepIndex_ : 29; + public: CompilerOutput() : script_(nullptr), mode_(SequentialExecution), pendingInvalidation_(false) @@ -1366,6 +1345,15 @@ class CompilerOutput bool pendingInvalidation() { return pendingInvalidation_; } + + void setSweepIndex(uint32_t index) { + if (index >= 1 << 29) + MOZ_CRASH(); + sweepIndex_ = index; + } + uint32_t sweepIndex() { + return sweepIndex_; + } }; class RecompileInfo @@ -1380,8 +1368,9 @@ class RecompileInfo bool operator == (const RecompileInfo &o) const { return outputIndex == o.outputIndex; } - CompilerOutput *compilerOutput(TypeCompartment &types) const; + CompilerOutput *compilerOutput(TypeZone &types) const; CompilerOutput *compilerOutput(JSContext *cx) const; + bool shouldSweep(TypeZone &types); }; /* Type information for a compartment. */ @@ -1389,32 +1378,9 @@ struct TypeCompartment { /* Constraint solving worklist structures. */ - /* - * Worklist of types which need to be propagated to constraints. We use a - * worklist to avoid blowing the native stack. - */ - struct PendingWork - { - TypeConstraint *constraint; - ConstraintTypeSet *source; - Type type; - }; - PendingWork *pendingArray; - unsigned pendingCount; - unsigned pendingCapacity; - - /* Whether we are currently resolving the pending worklist. */ - bool resolving; - /* Number of scripts in this compartment. */ unsigned scriptCount; - /* Valid & Invalid script referenced by type constraints. */ - Vector *constrainedOutputs; - - /* Pending recompilations to perform before execution of JIT code can resume. */ - Vector *pendingRecompiles; - /* Table for referencing types of objects keyed to an allocation site. */ AllocationSiteTable *allocationSiteTable; @@ -1438,14 +1404,6 @@ struct TypeCompartment inline JSCompartment *compartment(); - /* Add a type to register with a list of constraints. */ - inline void addPending(JSContext *cx, TypeConstraint *constraint, - ConstraintTypeSet *source, Type type); - bool growPendingArray(JSContext *cx); - - /* Resolve pending type registrations, excluding delayed ones. */ - inline void resolvePending(JSContext *cx); - /* Prints results of this compartment if spew is enabled or force is set. */ void print(JSContext *cx, bool force); @@ -1461,26 +1419,16 @@ struct TypeCompartment /* Get or make an object for an allocation site, and add to the allocation site table. */ TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key); - void processPendingRecompiles(FreeOp *fop); - /* Mark all types as needing destruction once inference has 'finished'. */ void setPendingNukeTypes(ExclusiveContext *cx); - /* Mark a script as needing recompilation once inference has finished. */ - void addPendingRecompile(JSContext *cx, const RecompileInfo &info); - void addPendingRecompile(JSContext *cx, JSScript *script); - /* Mark any type set containing obj as having a generic object type. */ void markSetsUnknown(JSContext *cx, TypeObject *obj); void sweep(FreeOp *fop); - void sweepShapes(FreeOp *fop); - void clearCompilerOutputs(FreeOp *fop); - void finalizeObjects(); void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *pendingArrays, size_t *allocationSiteTables, size_t *arrayTypeTables, size_t *objectTypeTables); @@ -1496,6 +1444,16 @@ struct TypeZone static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024; js::LifoAlloc typeLifoAlloc; + /* + * All Ion compilations that have occured in this zone, for indexing via + * RecompileInfo. This includes both valid and invalid compilations, though + * invalidated compilations are swept on GC. + */ + Vector *compilerOutputs; + + /* Pending recompilations to perform before execution of JIT code can resume. */ + Vector *pendingRecompiles; + /* * Bit set if all current types must be marked as unknown, and all scripts * recompiled. Caused by OOM failure within inference operations. @@ -1516,6 +1474,12 @@ struct TypeZone /* Mark all types as needing destruction once inference has 'finished'. */ void setPendingNukeTypes(); + /* Mark a script as needing recompilation once inference has finished. */ + void addPendingRecompile(JSContext *cx, const RecompileInfo &info); + void addPendingRecompile(JSContext *cx, JSScript *script); + + void processPendingRecompiles(FreeOp *fop); + void nukeTypes(FreeOp *fop); }; diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index c4e07abd3271..328daf0a076c 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -104,17 +104,29 @@ CompilerOutput::ion() const } inline CompilerOutput* -RecompileInfo::compilerOutput(TypeCompartment &types) const +RecompileInfo::compilerOutput(TypeZone &types) const { - if (!types.constrainedOutputs || outputIndex >= types.constrainedOutputs->length()) + if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length()) return nullptr; - return &(*types.constrainedOutputs)[outputIndex]; + return &(*types.compilerOutputs)[outputIndex]; } inline CompilerOutput* RecompileInfo::compilerOutput(JSContext *cx) const { - return compilerOutput(cx->compartment()->types); + return compilerOutput(cx->zone()->types); +} + +inline bool +RecompileInfo::shouldSweep(TypeZone &types) +{ + CompilerOutput *output = compilerOutput(types); + if (!output || !output->isValid()) + return true; + + // Update this info for the output's new index in the zone's compiler outputs. + outputIndex = output->sweepIndex(); + return false; } ///////////////////////////////////////////////////////////////////// @@ -288,14 +300,13 @@ struct AutoEnterAnalysis * If there are no more type inference activations on the stack, * process any triggered recompilations. Note that we should not be * invoking any scripted code while type inference is running. - * :TODO: assert this. */ if (!compartment->activeAnalysis) { - TypeCompartment *types = &compartment->types; - if (compartment->zone()->types.pendingNukeTypes) - compartment->zone()->types.nukeTypes(freeOp); - else if (types->pendingRecompiles) - types->processPendingRecompiles(freeOp); + TypeZone &types = compartment->zone()->types; + if (types.pendingNukeTypes) + types.nukeTypes(freeOp); + else if (types.pendingRecompiles) + types.processPendingRecompiles(freeOp); } } @@ -852,50 +863,6 @@ TypeCompartment::compartment() return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); } -inline void -TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, - ConstraintTypeSet *source, Type type) -{ - JS_ASSERT(this == &cx->compartment()->types); - JS_ASSERT(!cx->runtime()->isHeapBusy()); - - InferSpew(ISpewOps, "pending: %sC%p%s %s", - InferSpewColor(constraint), constraint, InferSpewColorReset(), - TypeString(type)); - - if ((pendingCount == pendingCapacity) && !growPendingArray(cx)) - return; - - PendingWork &pending = pendingArray[pendingCount++]; - pending.constraint = constraint; - pending.source = source; - pending.type = type; -} - -inline void -TypeCompartment::resolvePending(JSContext *cx) -{ - JS_ASSERT(this == &cx->compartment()->types); - - if (resolving) { - /* There is an active call further up resolving the worklist. */ - return; - } - - resolving = true; - - /* Handle all pending type registrations. */ - while (pendingCount) { - const PendingWork &pending = pendingArray[--pendingCount]; - InferSpew(ISpewOps, "resolve: %sC%p%s %s", - InferSpewColor(pending.constraint), pending.constraint, - InferSpewColorReset(), TypeString(pending.type)); - pending.constraint->newType(cx, pending.source, pending.type); - } - - resolving = false; -} - ///////////////////////////////////////////////////////////////////// // TypeSet ///////////////////////////////////////////////////////////////////// @@ -1219,10 +1186,9 @@ ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type) if (JSContext *cx = cxArg->maybeJSContext()) { TypeConstraint *constraint = constraintList; while (constraint) { - cx->compartment()->types.addPending(cx, constraint, this, type); + constraint->newType(cx, this, type); constraint = constraint->next; } - cx->compartment()->types.resolvePending(cx); } else { JS_ASSERT(!constraintList); } diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index 3ba7d433c6f8..ee09906df5bd 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -215,7 +215,6 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment) // Measure the compartment object itself, and things hanging off it. compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_, - &cStats.typeInferencePendingArrays, &cStats.typeInferenceAllocationSiteTables, &cStats.typeInferenceArrayTypeTables, &cStats.typeInferenceObjectTypeTables, diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 1a12e11d83b3..b225830e8efe 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2122,10 +2122,6 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats, cStats.typeInferenceTypeScripts, "Memory used by type sets associated with scripts."); - ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/pending-arrays"), - cStats.typeInferencePendingArrays, - "Memory used for solving constraints during type inference."); - ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"), cStats.typeInferenceAllocationSiteTables, "Memory indexing type objects associated with allocation sites."); From df442b71cb7ff5d931bebd28041ff7361d3145fa Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Thu, 12 Dec 2013 17:43:49 +0100 Subject: [PATCH 028/459] Bug 879402 - Fix possible leak in createArray and make it work for ggc, r=bhackett --- js/src/jsobjinlines.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 42c5d31eeb60..4bda696f6ac1 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -494,12 +494,6 @@ JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc:: */ JS_ASSERT(js::gc::GetGCKindSlots(kind) >= js::ObjectElements::VALUES_PER_HEADER); - uint32_t capacity = js::gc::GetGCKindSlots(kind) - js::ObjectElements::VALUES_PER_HEADER; - - JSObject *obj = js_NewGCObject(cx, kind, heap); - if (!obj) - return nullptr; - js::HeapSlot *slots = nullptr; if (size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan())) { slots = cx->pod_malloc(nDynamicSlots); @@ -508,6 +502,19 @@ JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc:: js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots); } + JSObject *obj = js_NewGCObject(cx, kind, heap); + if (!obj) { + js_free(slots); + return nullptr; + } + +#ifdef JSGC_GENERATIONAL + if (slots && heap != js::gc::TenuredHeap) + cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, slots); +#endif + + uint32_t capacity = js::gc::GetGCKindSlots(kind) - js::ObjectElements::VALUES_PER_HEADER; + obj->shape_.init(shape); obj->type_.init(type); obj->slots = slots; From 55cc6f590018a0ba23f5ec1c0cb928fcf0f068fb Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 12 Dec 2013 13:56:32 -0500 Subject: [PATCH 029/459] Backed out changeset a29d7f5ddde6 (bug 932982) for ggc asserts. --- js/public/MemoryMetrics.h | 1 + js/src/gc/Marking.cpp | 46 ++++- js/src/gc/RootMarking.cpp | 5 + js/src/gc/Statistics.cpp | 1 + js/src/gc/Statistics.h | 1 + js/src/gc/Zone.cpp | 31 ++- js/src/gc/Zone.h | 2 + js/src/jit/CodeGenerator.cpp | 6 +- js/src/jit/Ion.cpp | 17 +- js/src/jit/Ion.h | 2 +- js/src/jit/IonBuilder.cpp | 6 +- js/src/jit/IonCode.h | 3 - js/src/jit/MIR.cpp | 2 +- js/src/jscompartment.cpp | 11 +- js/src/jscompartment.h | 3 + js/src/jsgc.cpp | 14 +- js/src/jsgc.h | 7 +- js/src/jsinfer.cpp | 329 +++++++++++++++--------------- js/src/jsinfer.h | 122 +++++++---- js/src/jsinferinlines.h | 78 +++++-- js/src/vm/MemoryMetrics.cpp | 1 + js/xpconnect/src/XPCJSRuntime.cpp | 4 + 22 files changed, 434 insertions(+), 258 deletions(-) diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index f974259467d5..95ac91b9f6d5 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -393,6 +393,7 @@ struct CompartmentStats macro(Other, NotLiveGCThing, baselineStubsOptimized) \ macro(Other, NotLiveGCThing, ionData) \ macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \ + macro(Other, NotLiveGCThing, typeInferencePendingArrays) \ macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \ macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \ macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \ diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index d0e3bde29e8a..b0c8a6555988 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1122,11 +1122,14 @@ gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape) static void ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) { - unsigned count = type->getPropertyCount(); - for (unsigned i = 0; i < count; i++) { - types::Property *prop = type->getProperty(i); - if (prop && JSID_IS_STRING(prop->id)) - PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); + /* Don't mark properties for singletons. They'll be purged by the GC. */ + if (!type->singleton) { + unsigned count = type->getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + types::Property *prop = type->getProperty(i); + if (prop && JSID_IS_STRING(prop->id)) + PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); + } } if (TaggedProto(type->proto).isObject()) @@ -1347,7 +1350,7 @@ GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp) } void -GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr) +GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr) { if (tag == TypeTag) { ScanTypeObject(this, reinterpret_cast(addr)); @@ -1361,6 +1364,35 @@ GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr) pushObject(obj); } else if (tag == IonCodeTag) { MarkChildren(this, reinterpret_cast(addr)); + } else if (tag == ArenaTag) { + ArenaHeader *aheader = reinterpret_cast(addr); + AllocKind thingKind = aheader->getAllocKind(); + size_t thingSize = Arena::thingSize(thingKind); + + for ( ; aheader; aheader = aheader->next) { + Arena *arena = aheader->getArena(); + FreeSpan firstSpan(aheader->getFirstFreeSpan()); + const FreeSpan *span = &firstSpan; + + for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) { + JS_ASSERT(thing <= arena->thingsEnd()); + if (thing == span->first) { + if (!span->hasNext()) + break; + thing = span->last; + span = span->nextSpan(); + } else { + JSObject *object = reinterpret_cast(thing); + if (object->hasSingletonType() && object->markIfUnmarked(getMarkColor())) + pushObject(object); + budget.step(); + } + } + if (budget.isOverBudget()) { + pushArenaList(aheader); + return; + } + } } } @@ -1398,7 +1430,7 @@ GCMarker::processMarkStackTop(SliceBudget &budget) goto scan_obj; } - processMarkStackOther(tag, addr); + processMarkStackOther(budget, tag, addr); return; scan_value_array: diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 541ad84d996c..8ae3e85fd559 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -726,6 +726,11 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting()) continue; + if (IS_GC_MARKING_TRACER(trc) && zone->isPreservingCode()) { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_TYPES); + zone->markTypes(trc); + } + /* Do not discard scripts with counts while profiling. */ if (rt->profilingScripts && !rt->isHeapMinorCollecting()) { for (CellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) { diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index be76eee734a1..7744b61d53f5 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -276,6 +276,7 @@ static const PhaseInfo phases[] = { { PHASE_PURGE, "Purge", PHASE_NO_PARENT }, { PHASE_MARK, "Mark", PHASE_NO_PARENT }, { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MARK }, + { PHASE_MARK_TYPES, "Mark Types", PHASE_MARK_ROOTS }, { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK }, { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT }, { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP }, diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 1029fd52fe8e..3240fd238f53 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -28,6 +28,7 @@ enum Phase { PHASE_PURGE, PHASE_MARK, PHASE_MARK_ROOTS, + PHASE_MARK_TYPES, PHASE_MARK_DELAYED, PHASE_SWEEP, PHASE_SWEEP_MARK, diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 539faabbd7c8..ad0c2fae7fde 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -80,6 +80,35 @@ Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon) needsBarrier_ = needs; } +void +Zone::markTypes(JSTracer *trc) +{ + /* + * Mark all scripts, type objects and singleton JS objects in the + * compartment. These can be referred to directly by type sets, which we + * cannot modify while code which depends on these type sets is active. + */ + JS_ASSERT(isPreservingCode()); + + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + MarkScriptRoot(trc, &script, "mark_types_script"); + JS_ASSERT(script == i.get()); + } + + for (size_t thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) { + ArenaHeader *aheader = allocator.arenas.getFirstArena(static_cast(thingKind)); + if (aheader) + trc->runtime->gcMarker.pushArenaList(aheader); + } + + for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { + types::TypeObject *type = i.get(); + MarkTypeObjectRoot(trc, &type, "mark_types_scan"); + JS_ASSERT(type == i.get()); + } +} + void Zone::resetGCMallocBytes() { @@ -115,7 +144,7 @@ Zone::sweep(FreeOp *fop, bool releaseTypes) if (active) releaseTypes = false; - { + if (!isPreservingCode()) { gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); types.sweep(fop, releaseTypes); } diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index a2fe6a22a022..ebad06d0b6c9 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -310,6 +310,8 @@ struct Zone : public JS::shadow::Zone, js_ReportAllocationOverflow(nullptr); } + void markTypes(JSTracer *trc); + js::types::TypeZone types; void sweep(js::FreeOp *fop, bool releaseTypes); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index c15f164ec681..cc1095ff392b 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -5890,7 +5890,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) safepoints_.size(), callTargets.length(), patchableBackedges_.length(), optimizationLevel); if (!ionScript) { - recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); + recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); return false; } @@ -5915,7 +5915,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) // Use js_free instead of IonScript::Destroy: the cache list and // backedge list are still uninitialized. js_free(ionScript); - recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); + recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); return false; } @@ -5936,7 +5936,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) /* resetUses */ false, /* cancelOffThread*/ false)) { js_free(ionScript); - recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); + recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); return false; } } diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 546e31c1ca8c..92a1e4aed529 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2508,7 +2508,7 @@ jit::InvalidateAll(FreeOp *fop, Zone *zone) void -jit::Invalidate(types::TypeZone &types, FreeOp *fop, +jit::Invalidate(types::TypeCompartment &types, FreeOp *fop, const Vector &invalid, bool resetUses, bool cancelOffThread) { @@ -2591,7 +2591,7 @@ void jit::Invalidate(JSContext *cx, const Vector &invalid, bool resetUses, bool cancelOffThread) { - jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses, + jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses, cancelOffThread); } @@ -2638,13 +2638,14 @@ FinishInvalidationOf(FreeOp *fop, JSScript *script, IonScript *ionScript, bool p else script->setIonScript(nullptr); - types::TypeZone &types = script->zone()->types; - ionScript->recompileInfo().compilerOutput(types)->invalidate(); - - // If this script has Ion code on the stack, invalidated() will return + // If this script has Ion code on the stack, invalidation() will return // true. In this case we have to wait until destroying it. - if (!ionScript->invalidated()) + if (!ionScript->invalidated()) { + types::TypeCompartment &types = script->compartment()->types; + ionScript->recompileInfo().compilerOutput(types)->invalidate(); + jit::IonScript::Destroy(fop, ionScript); + } } void @@ -2663,6 +2664,8 @@ jit::FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp) // Free optimized baseline stubs. if (comp->jitCompartment()) comp->jitCompartment()->optimizedStubSpace()->free(); + + comp->types.clearCompilerOutputs(fop); } void diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index 780fc993901f..f516f1e12698 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -127,7 +127,7 @@ IonExecStatus IonCannon(JSContext *cx, RunState &state); IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args); // Walk the stack and invalidate active Ion frames for the invalid scripts. -void Invalidate(types::TypeZone &types, FreeOp *fop, +void Invalidate(types::TypeCompartment &types, FreeOp *fop, const Vector &invalid, bool resetUses = true, bool cancelOffThread = true); void Invalidate(JSContext *cx, const Vector &invalid, bool resetUses = true, diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 2ca4b692c083..24eeae4a238e 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6321,7 +6321,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc types::HeapTypeSetKey property = staticType->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || - property.configured(constraints())) + property.configured(constraints(), staticType)) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. @@ -6411,7 +6411,7 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name) types::HeapTypeSetKey property = staticType->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || - property.configured(constraints())) + property.configured(constraints(), staticType)) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. @@ -7861,7 +7861,7 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name, *property = type->property(id); return property->maybeTypes() && property->maybeTypes()->definiteProperty() && - !property->configured(constraints()); + !property->configured(constraints(), type); } bool diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index 286cbeb9e882..d3ecaa94223e 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -531,9 +531,6 @@ struct IonScript const types::RecompileInfo& recompileInfo() const { return recompileInfo_; } - types::RecompileInfo& recompileInfoRef() { - return recompileInfo_; - } OptimizationLevel optimizationLevel() const { return optimizationLevel_; } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 882b468f6a7b..aad9234a367f 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -3025,7 +3025,7 @@ jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, // Check if the property has been reconfigured or is a getter. types::HeapTypeSetKey property = object->property(NameToId(name)); - if (property.configured(constraints)) + if (property.configured(constraints, object)) return false; } } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index f9dd807606db..a4132018c96f 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -48,6 +48,7 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options = #endif global_(nullptr), enterCompartmentDepth(0), + lastCodeRelease(0), data(nullptr), objectMetadataCallback(nullptr), lastAnimationTime(0), @@ -544,6 +545,13 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes) WeakMapBase::sweepCompartment(this); } + if (zone()->isPreservingCode()) { + gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); + types.sweepShapes(fop); + } else { + JS_ASSERT(!types.constrainedOutputs); + } + NativeIterator *ni = enumerators->next(); while (ni != enumerators) { JSObject *iterObj = ni->iterObj(); @@ -856,6 +864,7 @@ JSCompartment::clearTraps(FreeOp *fop) void JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, + size_t *tiPendingArrays, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, @@ -867,7 +876,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *baselineStubsOptimized) { *compartmentObject += mallocSizeOf(this); - types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, + types.addSizeOfExcludingThis(mallocSizeOf, tiPendingArrays, tiAllocationSiteTables, tiArrayTypeTables, tiObjectTypeTables); *shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf) + initialShapes.sizeOfExcludingThis(mallocSizeOf) diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index f44c2b09e390..15a93afe1280 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -187,6 +187,8 @@ struct JSCompartment */ void adoptWorkerAllocator(js::Allocator *workerAllocator); + + int64_t lastCodeRelease; bool activeAnalysis; /* Type information about the scripts and objects in this compartment. */ @@ -219,6 +221,7 @@ struct JSCompartment public: void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, + size_t *tiPendingArrays, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 27242bdcdb39..dd3cde985825 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2854,9 +2854,13 @@ ShouldPreserveJITCode(JSCompartment *comp, int64_t currentTime) if (rt->alwaysPreserveCode) return true; - if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime) + if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime && + comp->lastCodeRelease + (PRMJ_USEC_PER_SEC * 300) >= currentTime) + { return true; + } + comp->lastCodeRelease = currentTime; return false; } @@ -3935,12 +3939,12 @@ BeginSweepingZoneGroup(JSRuntime *rt) bool releaseTypes = ReleaseObservedTypes(rt); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex); - c->sweep(&fop, releaseTypes && !c->zone()->isPreservingCode()); + c->sweep(&fop, releaseTypes); } for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex); - zone->sweep(&fop, releaseTypes && !zone->isPreservingCode()); + zone->sweep(&fop, releaseTypes); } } @@ -5289,6 +5293,10 @@ js::ReleaseAllJITCode(FreeOp *fop) jit::FinishDiscardBaselineScript(fop, script); } } + + /* Sweep now invalidated compiler outputs from each compartment. */ + for (CompartmentsIter comp(fop->runtime(), SkipAtoms); !comp.done(); comp.next()) + comp->types.clearCompilerOutputs(fop); #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 9159f31ec024..2e5cbe360930 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1091,6 +1091,7 @@ struct GCMarker : public JSTracer { ObjectTag, TypeTag, XmlTag, + ArenaTag, SavedValueArrayTag, IonCodeTag, LastTag = IonCodeTag @@ -1118,6 +1119,10 @@ struct GCMarker : public JSTracer { pushTaggedPtr(ObjectTag, obj); } + void pushArenaList(gc::ArenaHeader *firstArena) { + pushTaggedPtr(ArenaTag, firstArena); + } + void pushType(types::TypeObject *type) { pushTaggedPtr(TypeTag, type); } @@ -1224,7 +1229,7 @@ struct GCMarker : public JSTracer { bool restoreValueArray(JSObject *obj, void **vpp, void **endp); void saveValueRanges(); inline void processMarkStackTop(SliceBudget &budget); - void processMarkStackOther(uintptr_t tag, uintptr_t addr); + void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr); void appendGrayRoot(void *thing, JSGCTraceKind kind); diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 51c19af54640..0c1fe0167a3c 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -243,6 +243,14 @@ types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &val if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) return true; + /* + * If we called in here while resolving a type constraint, we may be in the + * middle of resolving a standard class and the type sets will not be updated + * until the outer TypeSet::add finishes. + */ + if (cx->compartment()->types.pendingCount) + return true; + Type type = GetValueType(value); AutoEnterAnalysis enter(cx); @@ -734,12 +742,12 @@ class TypeCompilerConstraint : public TypeConstraint void newType(JSContext *cx, TypeSet *source, Type type) { if (data.invalidateOnNewType(type)) - cx->zone()->types.addPendingRecompile(cx, compilation); + cx->compartment()->types.addPendingRecompile(cx, compilation); } void newPropertyState(JSContext *cx, TypeSet *source) { if (data.invalidateOnNewPropertyState(source)) - cx->zone()->types.addPendingRecompile(cx, compilation); + cx->compartment()->types.addPendingRecompile(cx, compilation); } void newObjectState(JSContext *cx, TypeObject *object) { @@ -747,16 +755,7 @@ class TypeCompilerConstraint : public TypeConstraint // will be sent on changes to its state, so always invalidate any // associated compilations. if (object->unknownProperties() || data.invalidateOnNewObjectState(object)) - cx->zone()->types.addPendingRecompile(cx, compilation); - } - - TypeConstraint *sweep(TypeZone &zone) { - if (data.shouldSweep() || compilation.shouldSweep(zone)) - return nullptr; - TypeConstraint *res = zone.typeLifoAlloc.new_ >(compilation, data); - if (!res) - zone.setPendingNukeTypes(); - return res; + cx->compartment()->types.addPendingRecompile(cx, compilation); } }; @@ -909,21 +908,13 @@ class TypeConstraintFreezeStack : public TypeConstraint const char *kind() { return "freezeStack"; } - void newType(JSContext *cx, TypeSet *source, Type type) { + void newType(JSContext *cx, TypeSet *source, Type type) + { /* * Unlike TypeConstraintFreeze, triggering this constraint once does * not disable it on future changes to the type set. */ - cx->zone()->types.addPendingRecompile(cx, script_); - } - - TypeConstraint *sweep(TypeZone &zone) { - if (IsScriptAboutToBeFinalized(&script_)) - return nullptr; - TypeConstraint *res = zone.typeLifoAlloc.new_(script_); - if (!res) - zone.setPendingNukeTypes(); - return res; + cx->compartment()->types.addPendingRecompile(cx, script_); } }; @@ -938,22 +929,15 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu CompilerOutput co(script, executionMode); - TypeZone &types = cx->zone()->types; - if (!types.compilerOutputs) { - types.compilerOutputs = cx->new_< Vector >(cx); - if (!types.compilerOutputs) + TypeCompartment &types = cx->compartment()->types; + if (!types.constrainedOutputs) { + types.constrainedOutputs = cx->new_< Vector >(cx); + if (!types.constrainedOutputs) return false; } -#ifdef DEBUG - for (size_t i = 0; i < types.compilerOutputs->length(); i++) { - const CompilerOutput &co = (*types.compilerOutputs)[i]; - JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode); - } -#endif - - uint32_t index = types.compilerOutputs->length(); - if (!types.compilerOutputs->append(co)) + uint32_t index = types.constrainedOutputs->length(); + if (!types.constrainedOutputs->append(co)) return false; *precompileInfo = RecompileInfo(index); @@ -995,8 +979,8 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu array[i].add(cx, cx->typeLifoAlloc().new_(entry.script), false); } - if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) { - types.compilerOutputs->back().invalidate(); + if (!succeeded || types.constrainedOutputs->back().pendingInvalidation()) { + types.constrainedOutputs->back().invalidate(); script->resetUseCount(); return false; } @@ -1025,8 +1009,6 @@ class ConstraintDataFreeze ? property.maybeTypes()->isSubset(expected) : property.maybeTypes()->empty(); } - - bool shouldSweep() { return false; } }; } /* anonymous namespace */ @@ -1213,8 +1195,6 @@ class ConstraintDataFreezeObjectFlags { return !invalidateOnNewObjectState(property.object()->maybeType()); } - - bool shouldSweep() { return false; } }; } /* anonymous namespace */ @@ -1309,8 +1289,6 @@ class ConstraintDataFreezeObjectForInlinedCall { return true; } - - bool shouldSweep() { return false; } }; // Constraint which triggers recompilation when the template object for a @@ -1337,11 +1315,6 @@ class ConstraintDataFreezeObjectForNewScriptTemplate { return !invalidateOnNewObjectState(property.object()->maybeType()); } - - bool shouldSweep() { - // Note: |templateObject| is only used for equality testing. - return false; - } }; // Constraint which triggers recompilation when the underlying data pointer for @@ -1368,11 +1341,6 @@ class ConstraintDataFreezeObjectForTypedArrayBuffer { return !invalidateOnNewObjectState(property.object()->maybeType()); } - - bool shouldSweep() { - // Note: |viewData| is only used for equality testing. - return false; - } }; } /* anonymous namespace */ @@ -1445,7 +1413,10 @@ namespace { class ConstraintDataFreezeConfiguredProperty { public: - ConstraintDataFreezeConfiguredProperty() + TypeObjectKey *object; + + ConstraintDataFreezeConfiguredProperty(TypeObjectKey *object) + : object(object) {} const char *kind() { return "freezeConfiguredProperty"; } @@ -1459,16 +1430,31 @@ class ConstraintDataFreezeConfiguredProperty bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) { + // Everywhere compiled code depends on definite properties associated + // with a type object's newScript, we need to make sure there are + // constraints in place which will mark those properties as configured + // should the definite properties be invalidated. + TypeObject *type = object->isSingleObject() + ? object->asSingleObject()->type() + : object->asTypeObject(); + if (type->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { + type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; + if (type->hasNewScript()) { + CheckNewScriptProperties(cx, type, type->newScript()->fun); + } else { + JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED); + type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; + } + } + return !property.maybeTypes()->configuredProperty(); } - - bool shouldSweep() { return false; } }; } /* anonymous namespace */ bool -HeapTypeSetKey::configured(CompilerConstraintList *constraints) +HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type) { if (maybeTypes() && maybeTypes()->configuredProperty()) return true; @@ -1477,7 +1463,7 @@ HeapTypeSetKey::configured(CompilerConstraintList *constraints) typedef CompilerConstraintInstance T; constraints->add(alloc->new_(alloc, *this, - ConstraintDataFreezeConfiguredProperty())); + ConstraintDataFreezeConfiguredProperty(type))); return false; } @@ -2000,7 +1986,7 @@ PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj) if (type->unknownProperties()) return true; HeapTypeSetKey index = type->property(JSID_VOID); - if (index.configured(constraints) || index.isOwnProperty(constraints)) + if (index.configured(constraints, type) || index.isOwnProperty(constraints)) return true; obj = obj->getProto(); } while (obj); @@ -2038,8 +2024,27 @@ types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, return PrototypeHasIndexedProperty(constraints, proto); } +bool +TypeCompartment::growPendingArray(JSContext *cx) +{ + unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2); + PendingWork *newArray = js_pod_calloc(newCapacity); + if (!newArray) { + cx->compartment()->types.setPendingNukeTypes(cx); + return false; + } + + PodCopy(newArray, pendingArray, pendingCount); + js_free(pendingArray); + + pendingArray = newArray; + pendingCapacity = newCapacity; + + return true; +} + void -TypeZone::processPendingRecompiles(FreeOp *fop) +TypeCompartment::processPendingRecompiles(FreeOp *fop) { if (!pendingRecompiles) return; @@ -2090,9 +2095,11 @@ TypeZone::nukeTypes(FreeOp *fop) */ JS_ASSERT(pendingNukeTypes); - if (pendingRecompiles) { - fop->free_(pendingRecompiles); - pendingRecompiles = nullptr; + for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) { + if (comp->types.pendingRecompiles) { + fop->free_(comp->types.pendingRecompiles); + comp->types.pendingRecompiles = nullptr; + } } inferenceEnabled = false; @@ -2112,7 +2119,7 @@ TypeZone::nukeTypes(FreeOp *fop) } void -TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info) +TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) { CompilerOutput *co = info.compilerOutput(cx); if (!co || !co->isValid() || co->pendingInvalidation()) @@ -2138,7 +2145,7 @@ TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info) } void -TypeZone::addPendingRecompile(JSContext *cx, JSScript *script) +TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script) { JS_ASSERT(script); @@ -3139,15 +3146,6 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint } void newType(JSContext *cx, TypeSet *source, Type type) {} - - TypeConstraint *sweep(TypeZone &zone) { - if (IsTypeObjectAboutToBeFinalized(&object)) - return nullptr; - TypeConstraint *res = zone.typeLifoAlloc.new_(object); - if (!res) - zone.setPendingNukeTypes(); - return res; - } }; bool @@ -3194,15 +3192,6 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint if (source->baseFlags() || source->getObjectCount() > 1) object->clearAddendum(cx); } - - TypeConstraint *sweep(TypeZone &zone) { - if (IsTypeObjectAboutToBeFinalized(&object)) - return nullptr; - TypeConstraint *res = zone.typeLifoAlloc.new_(object); - if (!res) - zone.setPendingNukeTypes(); - return res; - } }; void @@ -3952,19 +3941,8 @@ ConstraintTypeSet::sweep(Zone *zone) } } - /* - * Type constraints only hold weak references. Copy constraints referring - * to data that is still live into the zone's new arena. - */ - TypeConstraint *constraint = constraintList; + /* All constraints are wiped out on each GC. */ constraintList = nullptr; - while (constraint) { - if (TypeConstraint *copy = constraint->sweep(zone->types)) { - copy->next = constraintList; - constraintList = copy; - } - constraint = constraint->next; - } } inline void @@ -3984,6 +3962,18 @@ TypeObject::clearProperties() inline void TypeObject::sweep(FreeOp *fop) { + if (singleton) { + JS_ASSERT(!hasNewScript()); + + /* + * All properties can be discarded. We will regenerate them as needed + * as code gets reanalyzed. + */ + clearProperties(); + + return; + } + if (!isMarked()) { if (addendum) fop->free_(addendum); @@ -4006,14 +3996,6 @@ TypeObject::sweep(FreeOp *fop) for (unsigned i = 0; i < oldCapacity; i++) { Property *prop = oldArray[i]; if (prop) { - if (singleton && !prop->types.constraintList) { - /* - * Don't copy over properties of singleton objects which - * don't have associated constraints. The contents of these - * type sets will be regenerated as necessary. - */ - continue; - } Property *newProp = typeLifoAlloc.new_(*prop); if (newProp) { Property **pentry = @@ -4033,17 +4015,12 @@ TypeObject::sweep(FreeOp *fop) setBasePropertyCount(propertyCount); } else if (propertyCount == 1) { Property *prop = (Property *) propertySet; - if (singleton && !prop->types.constraintList) { - // Skip, as above. - clearProperties(); + Property *newProp = typeLifoAlloc.new_(*prop); + if (newProp) { + propertySet = (Property **) newProp; + newProp->types.sweep(zone()); } else { - Property *newProp = typeLifoAlloc.new_(*prop); - if (newProp) { - propertySet = (Property **) newProp; - newProp->types.sweep(zone()); - } else { - zone()->types.setPendingNukeTypes(); - } + zone()->types.setPendingNukeTypes(); } } @@ -4051,6 +4028,14 @@ TypeObject::sweep(FreeOp *fop) for (unsigned i = 0; i < basePropertyCount(); i++) JS_ASSERT(propertySet[i]); } + + /* + * The GC will clear out the constraints ensuring the correctness of the + * newScript information, these constraints will need to be regenerated + * the next time we compile code which depends on this info. + */ + if (hasNewScript()) + flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE; } void @@ -4134,6 +4119,52 @@ TypeCompartment::sweep(FreeOp *fop) e.rekeyFront(key); } } + + /* + * The pending array is reset on GC, it can grow large (75+ KB) and is easy + * to reallocate if the compartment becomes active again. + */ + if (pendingArray) + fop->free_(pendingArray); + + pendingArray = nullptr; + pendingCapacity = 0; +} + +void +TypeCompartment::sweepShapes(FreeOp *fop) +{ + /* + * Sweep any weak shape references that may be finalized even if a GC is + * preserving type information. + */ + if (objectTypeTable) { + for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) { + const ObjectTableKey &key = e.front().key(); + ObjectTableEntry &entry = e.front().value(); + + if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) { + fop->free_(key.properties); + fop->free_(entry.types); + e.removeFront(); + } + } + } +} + +void +TypeCompartment::clearCompilerOutputs(FreeOp *fop) +{ + if (constrainedOutputs) { + fop->delete_(constrainedOutputs); + constrainedOutputs = nullptr; + } + + if (pendingRecompiles) { + JS_ASSERT(pendingRecompiles->length() == 0); + fop->delete_(pendingRecompiles); + pendingRecompiles = nullptr; + } } void @@ -4162,6 +4193,7 @@ JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table) TypeCompartment::~TypeCompartment() { + js_free(pendingArray); js_delete(arrayTypeTable); js_delete(objectTypeTable); js_delete(allocationSiteTable); @@ -4180,6 +4212,12 @@ TypeScript::Sweep(FreeOp *fop, JSScript *script) /* Remove constraints and references to dead objects from the persistent type sets. */ for (unsigned i = 0; i < num; i++) typeArray[i].sweep(compartment->zone()); + + /* + * Freeze constraints on stack type sets need to be regenerated the next + * time the script is analyzed. + */ + script->clearHasFreezeConstraints(); } void @@ -4196,10 +4234,20 @@ Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePoo void TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, + size_t *pendingArrays, size_t *allocationSiteTables, size_t *arrayTypeTables, size_t *objectTypeTables) { + /* Pending arrays are cleared on GC along with the analysis pool. */ + *pendingArrays += mallocSizeOf(pendingArray); + + /* + * TypeCompartment::pendingRecompiles is non-nullptr only while inference + * code is running. + */ + JS_ASSERT(!pendingRecompiles); + if (allocationSiteTable) *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); @@ -4241,8 +4289,6 @@ TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const TypeZone::TypeZone(Zone *zone) : zone_(zone), typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), - compilerOutputs(nullptr), - pendingRecompiles(nullptr), pendingNukeTypes(false), inferenceEnabled(false) { @@ -4250,8 +4296,6 @@ TypeZone::TypeZone(Zone *zone) TypeZone::~TypeZone() { - js_delete(compilerOutputs); - js_delete(pendingRecompiles); } void @@ -4268,21 +4312,11 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize()); oldAlloc.steal(&typeLifoAlloc); - /* Sweep and find compressed indexes for each compiler output. */ - size_t newCompilerOutputCount = 0; - if (compilerOutputs) { - for (size_t i = 0; i < compilerOutputs->length(); i++) { - CompilerOutput &output = (*compilerOutputs)[i]; - if (output.isValid()) { - JSScript *script = output.script(); - if (IsScriptAboutToBeFinalized(&script)) - output.invalidate(); - else - output.setSweepIndex(newCompilerOutputCount++); - } - } - } - + /* + * Sweep analysis information and everything depending on it from the + * compartment, including all remaining mjit code if inference is + * enabled in the compartment. + */ if (inferenceEnabled) { gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI); @@ -4294,21 +4328,6 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) if (releaseTypes) { script->types->destroy(); script->types = nullptr; - - /* - * Freeze constraints on stack type sets need to be - * regenerated the next time the script is analyzed. - */ - script->clearHasFreezeConstraints(); - - JS_ASSERT(!script->hasIonScript()); - JS_ASSERT(!script->hasParallelIonScript()); - } else { - /* Update the recompile indexes in any IonScripts still on the script. */ - if (script->hasIonScript()) - script->ionScript()->recompileInfoRef().shouldSweep(*this); - if (script->hasParallelIonScript()) - script->parallelIonScript()->recompileInfoRef().shouldSweep(*this); } } } @@ -4328,20 +4347,6 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) comp->types.sweep(fop); } - if (compilerOutputs) { - size_t sweepIndex = 0; - for (size_t i = 0; i < compilerOutputs->length(); i++) { - CompilerOutput output = (*compilerOutputs)[i]; - if (output.isValid()) { - JS_ASSERT(sweepIndex == output.sweepIndex()); - output.setSweepIndex(0); - (*compilerOutputs)[sweepIndex++] = output; - } - } - JS_ASSERT(sweepIndex == newCompilerOutputCount); - JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount)); - } - { gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS); for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) { diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 38c3be127e82..690631dfa7ad 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -177,7 +177,7 @@ namespace analyze { namespace types { -class TypeZone; +class TypeCompartment; class TypeSet; class TypeObjectKey; @@ -278,10 +278,28 @@ inline Type GetValueType(const Value &val); * Type information about the values observed within scripts and about the * contents of the heap is accumulated as the program executes. Compilation * accumulates constraints relating type information on the heap with the - * compilations that should be invalidated when those types change. Type - * information and constraints are allocated in the zone's typeLifoAlloc, - * and on GC all data referring to live things is copied into a new allocator. - * Thus, type set and constraints only hold weak references. + * compilations that should be invalidated when those types change. This data + * is periodically cleared to reduce memory usage. + * + * GCs may clear both analysis information and jitcode. Sometimes GCs will + * preserve all information and code, and will not collect any scripts, type + * objects or singleton JS objects. + * + * The following data is cleared by all non-preserving GCs: + * + * - The ScriptAnalysis for each analyzed script and data from each analysis + * pass performed. + * + * - Property type sets for singleton JS objects. + * + * - Type constraints and dead references in all type sets. + * + * The following data is occasionally cleared by non-preserving GCs: + * + * - TypeScripts and their type sets are occasionally destroyed, per a timer. + * + * - When a JSScript or TypeObject is swept, type information for its contents + * is destroyed. */ /* @@ -316,12 +334,6 @@ public: * state. */ virtual void newObjectState(JSContext *cx, TypeObject *object) {} - - /* - * If the data this constraint refers to is still live, copy it into the - * zone's new allocator. Type constraints only hold weak references. - */ - virtual TypeConstraint *sweep(TypeZone &zone) = 0; }; /* Flags and other state stored in TypeSet::flags */ @@ -382,6 +394,13 @@ enum { /* If set, addendum information should not be installed on this object. */ OBJECT_FLAG_ADDENDUM_CLEARED = 0x2, + /* + * If set, type constraints covering the correctness of the newScript + * definite properties need to be regenerated before compiling any jitcode + * which depends on this information. + */ + OBJECT_FLAG_NEW_SCRIPT_REGENERATE = 0x4, + /* * Whether we have ensured all type sets in the compartment contain * ANYOBJECT instead of this object. @@ -850,6 +869,12 @@ struct TypeTypedObject : public TypeObjectAddendum * information is sensitive to changes in the property's type. Future changes * to the property (whether those uncovered by analysis or those occurring * in the VM) will treat these properties like those of any other type object. + * + * When a GC occurs, we wipe out all analysis information for all the + * compartment's scripts, so can destroy all properties on singleton type + * objects at the same time. If there is no reference on the stack to the + * type object itself, the type object is also destroyed, and the JS object + * reverts to having a lazy type. */ /* Type information about an object accessed by a script. */ @@ -1291,7 +1316,7 @@ class HeapTypeSetKey void freeze(CompilerConstraintList *constraints); JSValueType knownTypeTag(CompilerConstraintList *constraints); - bool configured(CompilerConstraintList *constraints); + bool configured(CompilerConstraintList *constraints, TypeObjectKey *type); bool isOwnProperty(CompilerConstraintList *constraints); bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other); JSObject *singleton(CompilerConstraintList *constraints); @@ -1314,10 +1339,6 @@ class CompilerOutput // Whether this compilation is about to be invalidated. bool pendingInvalidation_ : 1; - // During sweeping, the list of compiler outputs is compacted and invalidated - // outputs are removed. This gives the new index for a valid compiler output. - uint32_t sweepIndex_ : 29; - public: CompilerOutput() : script_(nullptr), mode_(SequentialExecution), pendingInvalidation_(false) @@ -1345,15 +1366,6 @@ class CompilerOutput bool pendingInvalidation() { return pendingInvalidation_; } - - void setSweepIndex(uint32_t index) { - if (index >= 1 << 29) - MOZ_CRASH(); - sweepIndex_ = index; - } - uint32_t sweepIndex() { - return sweepIndex_; - } }; class RecompileInfo @@ -1368,9 +1380,8 @@ class RecompileInfo bool operator == (const RecompileInfo &o) const { return outputIndex == o.outputIndex; } - CompilerOutput *compilerOutput(TypeZone &types) const; + CompilerOutput *compilerOutput(TypeCompartment &types) const; CompilerOutput *compilerOutput(JSContext *cx) const; - bool shouldSweep(TypeZone &types); }; /* Type information for a compartment. */ @@ -1378,9 +1389,32 @@ struct TypeCompartment { /* Constraint solving worklist structures. */ + /* + * Worklist of types which need to be propagated to constraints. We use a + * worklist to avoid blowing the native stack. + */ + struct PendingWork + { + TypeConstraint *constraint; + ConstraintTypeSet *source; + Type type; + }; + PendingWork *pendingArray; + unsigned pendingCount; + unsigned pendingCapacity; + + /* Whether we are currently resolving the pending worklist. */ + bool resolving; + /* Number of scripts in this compartment. */ unsigned scriptCount; + /* Valid & Invalid script referenced by type constraints. */ + Vector *constrainedOutputs; + + /* Pending recompilations to perform before execution of JIT code can resume. */ + Vector *pendingRecompiles; + /* Table for referencing types of objects keyed to an allocation site. */ AllocationSiteTable *allocationSiteTable; @@ -1404,6 +1438,14 @@ struct TypeCompartment inline JSCompartment *compartment(); + /* Add a type to register with a list of constraints. */ + inline void addPending(JSContext *cx, TypeConstraint *constraint, + ConstraintTypeSet *source, Type type); + bool growPendingArray(JSContext *cx); + + /* Resolve pending type registrations, excluding delayed ones. */ + inline void resolvePending(JSContext *cx); + /* Prints results of this compartment if spew is enabled or force is set. */ void print(JSContext *cx, bool force); @@ -1419,16 +1461,26 @@ struct TypeCompartment /* Get or make an object for an allocation site, and add to the allocation site table. */ TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key); + void processPendingRecompiles(FreeOp *fop); + /* Mark all types as needing destruction once inference has 'finished'. */ void setPendingNukeTypes(ExclusiveContext *cx); + /* Mark a script as needing recompilation once inference has finished. */ + void addPendingRecompile(JSContext *cx, const RecompileInfo &info); + void addPendingRecompile(JSContext *cx, JSScript *script); + /* Mark any type set containing obj as having a generic object type. */ void markSetsUnknown(JSContext *cx, TypeObject *obj); void sweep(FreeOp *fop); + void sweepShapes(FreeOp *fop); + void clearCompilerOutputs(FreeOp *fop); + void finalizeObjects(); void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, + size_t *pendingArrays, size_t *allocationSiteTables, size_t *arrayTypeTables, size_t *objectTypeTables); @@ -1444,16 +1496,6 @@ struct TypeZone static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024; js::LifoAlloc typeLifoAlloc; - /* - * All Ion compilations that have occured in this zone, for indexing via - * RecompileInfo. This includes both valid and invalid compilations, though - * invalidated compilations are swept on GC. - */ - Vector *compilerOutputs; - - /* Pending recompilations to perform before execution of JIT code can resume. */ - Vector *pendingRecompiles; - /* * Bit set if all current types must be marked as unknown, and all scripts * recompiled. Caused by OOM failure within inference operations. @@ -1474,12 +1516,6 @@ struct TypeZone /* Mark all types as needing destruction once inference has 'finished'. */ void setPendingNukeTypes(); - /* Mark a script as needing recompilation once inference has finished. */ - void addPendingRecompile(JSContext *cx, const RecompileInfo &info); - void addPendingRecompile(JSContext *cx, JSScript *script); - - void processPendingRecompiles(FreeOp *fop); - void nukeTypes(FreeOp *fop); }; diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 328daf0a076c..c4e07abd3271 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -104,29 +104,17 @@ CompilerOutput::ion() const } inline CompilerOutput* -RecompileInfo::compilerOutput(TypeZone &types) const +RecompileInfo::compilerOutput(TypeCompartment &types) const { - if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length()) + if (!types.constrainedOutputs || outputIndex >= types.constrainedOutputs->length()) return nullptr; - return &(*types.compilerOutputs)[outputIndex]; + return &(*types.constrainedOutputs)[outputIndex]; } inline CompilerOutput* RecompileInfo::compilerOutput(JSContext *cx) const { - return compilerOutput(cx->zone()->types); -} - -inline bool -RecompileInfo::shouldSweep(TypeZone &types) -{ - CompilerOutput *output = compilerOutput(types); - if (!output || !output->isValid()) - return true; - - // Update this info for the output's new index in the zone's compiler outputs. - outputIndex = output->sweepIndex(); - return false; + return compilerOutput(cx->compartment()->types); } ///////////////////////////////////////////////////////////////////// @@ -300,13 +288,14 @@ struct AutoEnterAnalysis * If there are no more type inference activations on the stack, * process any triggered recompilations. Note that we should not be * invoking any scripted code while type inference is running. + * :TODO: assert this. */ if (!compartment->activeAnalysis) { - TypeZone &types = compartment->zone()->types; - if (types.pendingNukeTypes) - types.nukeTypes(freeOp); - else if (types.pendingRecompiles) - types.processPendingRecompiles(freeOp); + TypeCompartment *types = &compartment->types; + if (compartment->zone()->types.pendingNukeTypes) + compartment->zone()->types.nukeTypes(freeOp); + else if (types->pendingRecompiles) + types->processPendingRecompiles(freeOp); } } @@ -863,6 +852,50 @@ TypeCompartment::compartment() return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); } +inline void +TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, + ConstraintTypeSet *source, Type type) +{ + JS_ASSERT(this == &cx->compartment()->types); + JS_ASSERT(!cx->runtime()->isHeapBusy()); + + InferSpew(ISpewOps, "pending: %sC%p%s %s", + InferSpewColor(constraint), constraint, InferSpewColorReset(), + TypeString(type)); + + if ((pendingCount == pendingCapacity) && !growPendingArray(cx)) + return; + + PendingWork &pending = pendingArray[pendingCount++]; + pending.constraint = constraint; + pending.source = source; + pending.type = type; +} + +inline void +TypeCompartment::resolvePending(JSContext *cx) +{ + JS_ASSERT(this == &cx->compartment()->types); + + if (resolving) { + /* There is an active call further up resolving the worklist. */ + return; + } + + resolving = true; + + /* Handle all pending type registrations. */ + while (pendingCount) { + const PendingWork &pending = pendingArray[--pendingCount]; + InferSpew(ISpewOps, "resolve: %sC%p%s %s", + InferSpewColor(pending.constraint), pending.constraint, + InferSpewColorReset(), TypeString(pending.type)); + pending.constraint->newType(cx, pending.source, pending.type); + } + + resolving = false; +} + ///////////////////////////////////////////////////////////////////// // TypeSet ///////////////////////////////////////////////////////////////////// @@ -1186,9 +1219,10 @@ ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type) if (JSContext *cx = cxArg->maybeJSContext()) { TypeConstraint *constraint = constraintList; while (constraint) { - constraint->newType(cx, this, type); + cx->compartment()->types.addPending(cx, constraint, this, type); constraint = constraint->next; } + cx->compartment()->types.resolvePending(cx); } else { JS_ASSERT(!constraintList); } diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index ee09906df5bd..3ba7d433c6f8 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -215,6 +215,7 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment) // Measure the compartment object itself, and things hanging off it. compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_, + &cStats.typeInferencePendingArrays, &cStats.typeInferenceAllocationSiteTables, &cStats.typeInferenceArrayTypeTables, &cStats.typeInferenceObjectTypeTables, diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index b225830e8efe..1a12e11d83b3 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2122,6 +2122,10 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats, cStats.typeInferenceTypeScripts, "Memory used by type sets associated with scripts."); + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/pending-arrays"), + cStats.typeInferencePendingArrays, + "Memory used for solving constraints during type inference."); + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"), cStats.typeInferenceAllocationSiteTables, "Memory indexing type objects associated with allocation sites."); From f89b3f4cd4bb848a3095987c6f1fa2a3da3d020d Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 12 Dec 2013 13:56:50 -0500 Subject: [PATCH 030/459] Backed out 6 changesets (bug 939614) for Hf timeouts on a CLOSED TREE. Backed out changeset 50cc92857bf7 (bug 939614) Backed out changeset 62a6568217da (bug 939614) Backed out changeset 31d0d40d4a7c (bug 939614) Backed out changeset 2b87186ddb6d (bug 939614) Backed out changeset fa2005c9ca02 (bug 939614) Backed out changeset 7d3c745a811f (bug 939614) --- js/src/builtin/TestingFunctions.cpp | 4 +- js/src/jit/AsmJS.cpp | 4 +- js/src/jit/Bailouts.cpp | 2 +- js/src/jit/BaselineCompiler.cpp | 3 +- js/src/jit/BaselineJIT.cpp | 2 +- js/src/jit/CodeGenerator.cpp | 26 +-- js/src/jit/CodeGenerator.h | 2 - js/src/jit/Ion.cpp | 181 +++++++-------- js/src/jit/Ion.h | 242 ++++++++++++++++++++- js/src/jit/IonAnalysis.cpp | 9 +- js/src/jit/IonBuilder.cpp | 99 +++------ js/src/jit/IonBuilder.h | 14 +- js/src/jit/IonCode.h | 10 +- js/src/jit/IonFrames.cpp | 2 +- js/src/jit/IonOptimizationLevels.cpp | 142 ------------ js/src/jit/IonOptimizationLevels.h | 220 ------------------- js/src/jit/JitOptions.cpp | 162 -------------- js/src/jit/JitOptions.h | 88 -------- js/src/jit/LIR-Common.h | 17 -- js/src/jit/LOpcodes.h | 1 - js/src/jit/LinearScan.cpp | 4 +- js/src/jit/LiveRangeAllocator.h | 2 +- js/src/jit/Lowering.cpp | 13 +- js/src/jit/Lowering.h | 1 - js/src/jit/MCallOptimize.cpp | 3 - js/src/jit/MIR.h | 35 --- js/src/jit/MIRGenerator.h | 8 +- js/src/jit/MIRGraph.cpp | 4 +- js/src/jit/MOpcodes.h | 3 +- js/src/jit/ParallelSafetyAnalysis.cpp | 1 - js/src/jit/RangeAnalysis.cpp | 2 +- js/src/jit/UnreachableCodeElimination.cpp | 4 +- js/src/jit/shared/CodeGenerator-shared.cpp | 2 +- js/src/jit/shared/Lowering-shared-inl.h | 4 +- js/src/jsapi.cpp | 17 +- js/src/jsscript.h | 6 +- js/src/jsworkers.cpp | 1 + js/src/moz.build | 2 - js/src/shell/js.cpp | 73 +++---- js/src/vm/ForkJoin.cpp | 4 +- 40 files changed, 443 insertions(+), 976 deletions(-) delete mode 100644 js/src/jit/IonOptimizationLevels.cpp delete mode 100644 js/src/jit/IonOptimizationLevels.h delete mode 100644 js/src/jit/JitOptions.cpp delete mode 100644 js/src/jit/JitOptions.h diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index c6261995b272..8d1a8b1a91f2 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -956,7 +956,7 @@ static bool EnableOsiPointRegisterChecks(JSContext *, unsigned, jsval *vp) { #if defined(JS_ION) && defined(CHECK_OSIPOINT_REGISTERS) - jit::js_JitOptions.checkOsiPointRegisters = true; + jit::js_IonOptions.checkOsiPointRegisters = true; #endif JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; @@ -1125,7 +1125,7 @@ SetIonCheckGraphCoherency(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); #ifdef JS_ION - jit::js_JitOptions.checkGraphConsistency = ToBoolean(args.get(0)); + jit::js_IonOptions.checkGraphConsistency = ToBoolean(args.get(0)); #endif args.rval().setUndefined(); return true; diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index c58c82ea132b..f7e70f9c1109 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -1936,9 +1936,7 @@ class FunctionCompiler graph_ = lifo_.new_(alloc_); info_ = lifo_.new_(locals_.count(), SequentialExecution); - const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS); - mirGen_ = lifo_.new_(CompileCompartment::get(cx()->compartment()), alloc_, - graph_, info_, optimizationInfo); + mirGen_ = lifo_.new_(CompileCompartment::get(cx()->compartment()), alloc_, graph_, info_); if (!newBlock(/* pred = */ nullptr, &curBlock_, fn_)) return false; diff --git a/js/src/jit/Bailouts.cpp b/js/src/jit/Bailouts.cpp index c00f3a893936..fe49af0216ca 100644 --- a/js/src/jit/Bailouts.cpp +++ b/js/src/jit/Bailouts.cpp @@ -196,7 +196,7 @@ jit::CheckFrequentBailouts(JSContext *cx, JSScript *script) // we compile this script LICM will be disabled. IonScript *ionScript = script->ionScript(); - if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold && + if (ionScript->numBailouts() >= js_IonOptions.frequentBailoutThreshold && !script->hadFrequentBailouts()) { script->setHadFrequentBailouts(); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index c10a24191f7b..febad931dae9 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -637,8 +637,7 @@ BaselineCompiler::emitUseCountIncrement() Label skipCall; - const OptimizationInfo *info = js_IonOptimizations.get(js_IonOptimizations.firstLevel()); - uint32_t minUses = info->usesBeforeCompile(script, pc); + uint32_t minUses = UsesBeforeIonRecompile(script, pc); masm.branch32(Assembler::LessThan, countReg, Imm32(minUses), &skipCall); masm.branchPtr(Assembler::Equal, diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 7cf1bc170c7d..0956e75f4ca6 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -276,7 +276,7 @@ CanEnterBaselineJIT(JSContext *cx, HandleScript script, bool osr) if (IsJSDEnabled(cx) || cx->runtime()->parallelWarmup > 0) { if (osr) return Method_Skipped; - } else if (script->incUseCount() <= js_JitOptions.baselineUsesBeforeCompile) { + } else if (script->incUseCount() <= js_IonOptions.baselineUsesBeforeCompile) { return Method_Skipped; } diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index cc1095ff392b..7cbe48d16495 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -22,7 +22,6 @@ #include "jit/ExecutionModeInlines.h" #include "jit/IonCaches.h" #include "jit/IonLinker.h" -#include "jit/IonOptimizationLevels.h" #include "jit/IonSpewer.h" #include "jit/MIRGenerator.h" #include "jit/MoveEmitter.h" @@ -5858,9 +5857,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) { RootedScript script(cx, gen->info().script()); ExecutionMode executionMode = gen->info().executionMode(); - OptimizationLevel optimizationLevel = gen->optimizationInfo().level(); - - JS_ASSERT_IF(HasIonScript(script, executionMode), executionMode == SequentialExecution); + JS_ASSERT(!HasIonScript(script, executionMode)); // Check to make sure we didn't have a mid-build invalidation. If so, we // will trickle to jit::Compile() and return Method_Skipped. @@ -5888,7 +5885,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) safepointIndices_.length(), osiIndices_.length(), cacheList_.length(), runtimeData_.length(), safepoints_.size(), callTargets.length(), - patchableBackedges_.length(), optimizationLevel); + patchableBackedges_.length()); if (!ionScript) { recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); return false; @@ -7931,24 +7928,5 @@ CodeGenerator::visitAssertRangeV(LAssertRangeV *ins) return true; } -typedef bool (*RecompileFn)(JSContext *); -static const VMFunction RecompileFnInfo = FunctionInfo(Recompile); - -bool -CodeGenerator::visitRecompileCheck(LRecompileCheck *ins) -{ - Register useCount = ToRegister(ins->scratch()); - - masm.movePtr(ImmPtr(ins->mir()->script()->addressOfUseCount()), useCount); - Address ptr(useCount, 0); - masm.add32(Imm32(1), ptr); - - OutOfLineCode *ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(useCount)); - masm.branch32(Assembler::Above, ptr, Imm32(ins->mir()->useCount()), ool->entry()); - masm.bind(ool->rejoin()); - - return true; -} - } // namespace jit } // namespace js diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 5a42247fd604..e522f27da234 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -336,8 +336,6 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitAssertRangeF(LAssertRangeF *ins); bool visitAssertRangeV(LAssertRangeV *ins); - bool visitRecompileCheck(LRecompileCheck *ins); - IonScriptCounts *extractUnassociatedScriptCounts() { IonScriptCounts *counts = unassociatedScriptCounts_; unassociatedScriptCounts_ = nullptr; // prevent delete in dtor diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 92a1e4aed529..ba3ee46a154d 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -27,7 +27,6 @@ #include "jit/ExecutionModeInlines.h" #include "jit/IonAnalysis.h" #include "jit/IonBuilder.h" -#include "jit/IonOptimizationLevels.h" #include "jit/IonSpewer.h" #include "jit/JitCompartment.h" #include "jit/LICM.h" @@ -50,6 +49,9 @@ using namespace js; using namespace js::jit; +// Global variables. +IonOptions jit::js_IonOptions; + // Assert that IonCode is gc::Cell aligned. JS_STATIC_ASSERT(sizeof(IonCode) % gc::CellSize == 0); @@ -759,8 +761,7 @@ IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo, uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize, size_t bailoutEntries, size_t constants, size_t safepointIndices, size_t osiIndices, size_t cacheEntries, size_t runtimeSize, - size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries, - OptimizationLevel optimizationLevel) + size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries) { static const int DataAlignment = sizeof(void *); @@ -847,7 +848,6 @@ IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo, script->frameSize_ = frameSize; script->recompileInfo_ = recompileInfo; - script->optimizationLevel_ = optimizationLevel; return script; } @@ -1233,9 +1233,7 @@ OptimizeMIR(MIRGenerator *mir) // Alias analysis is required for LICM and GVN so that we don't move // loads across stores. - if (mir->optimizationInfo().licmEnabled() || - mir->optimizationInfo().gvnEnabled()) - { + if (js_IonOptions.licm || js_IonOptions.gvn) { AliasAnalysis analysis(mir, graph); if (!analysis.analyze()) return false; @@ -1255,8 +1253,8 @@ OptimizeMIR(MIRGenerator *mir) return false; } - if (mir->optimizationInfo().gvnEnabled()) { - ValueNumberer gvn(mir, graph, mir->optimizationInfo().gvnKind() == GVN_Optimistic); + if (js_IonOptions.gvn) { + ValueNumberer gvn(mir, graph, js_IonOptions.gvnIsOptimistic); if (!gvn.analyze()) return false; IonSpewPass("GVN"); @@ -1266,7 +1264,7 @@ OptimizeMIR(MIRGenerator *mir) return false; } - if (mir->optimizationInfo().uceEnabled()) { + if (js_IonOptions.uce) { UnreachableCodeElimination uce(mir, graph); if (!uce.analyze()) return false; @@ -1277,7 +1275,7 @@ OptimizeMIR(MIRGenerator *mir) if (mir->shouldCancel("UCE")) return false; - if (mir->optimizationInfo().licmEnabled()) { + if (js_IonOptions.licm) { // LICM can hoist instructions from conditional branches and trigger // repeated bailouts. Disable it if this script is known to bailout // frequently. @@ -1294,7 +1292,7 @@ OptimizeMIR(MIRGenerator *mir) } } - if (mir->optimizationInfo().rangeAnalysisEnabled()) { + if (js_IonOptions.rangeAnalysis) { RangeAnalysis r(mir, graph); if (!r.addBetaNodes()) return false; @@ -1320,7 +1318,7 @@ OptimizeMIR(MIRGenerator *mir) if (mir->shouldCancel("RA De-Beta")) return false; - if (mir->optimizationInfo().uceEnabled()) { + if (js_IonOptions.uce) { bool shouldRunUCE = false; if (!r.prepareForUCE(&shouldRunUCE)) return false; @@ -1352,7 +1350,7 @@ OptimizeMIR(MIRGenerator *mir) return false; } - if (mir->optimizationInfo().eaaEnabled()) { + if (js_IonOptions.eaa) { EffectiveAddressAnalysis eaa(graph); if (!eaa.analyze()) return false; @@ -1374,7 +1372,7 @@ OptimizeMIR(MIRGenerator *mir) // Passes after this point must not move instructions; these analyses // depend on knowing the final order in which instructions will execute. - if (mir->optimizationInfo().edgeCaseAnalysisEnabled()) { + if (js_IonOptions.edgeCaseAnalysis && !mir->compilingAsmJS()) { EdgeCaseAnalysis edgeCaseAnalysis(mir, graph); if (!edgeCaseAnalysis.analyzeLate()) return false; @@ -1385,7 +1383,7 @@ OptimizeMIR(MIRGenerator *mir) return false; } - if (mir->optimizationInfo().eliminateRedundantChecksEnabled()) { + if (!mir->compilingAsmJS()) { // Note: check elimination has to run after all other passes that move // instructions. Since check uses are replaced with the actual index, // code motion after this pass could incorrectly move a load or store @@ -1418,7 +1416,7 @@ GenerateLIR(MIRGenerator *mir) AllocationIntegrityState integrity(*lir); - switch (mir->optimizationInfo().registerAllocator()) { + switch (js_IonOptions.registerAllocator) { case RegisterAllocator_LSRA: { #ifdef DEBUG if (!integrity.record()) @@ -1630,8 +1628,7 @@ TrackPropertiesForSingletonScopes(JSContext *cx, JSScript *script, BaselineFrame static AbortReason IonCompile(JSContext *cx, JSScript *script, BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing, - ExecutionMode executionMode, bool recompile, - OptimizationLevel optimizationLevel) + ExecutionMode executionMode, bool recompile) { #if JS_TRACE_LOGGING AutoTraceLog logger(TraceLogging::defaultLogger(), @@ -1639,8 +1636,6 @@ IonCompile(JSContext *cx, JSScript *script, TraceLogging::ION_COMPILE_STOP, script); #endif - JS_ASSERT(optimizationLevel > Optimization_DontCompile); - TrackPropertiesForSingletonScopes(cx, script, baselineFrame); LifoAlloc *alloc = cx->new_(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); @@ -1688,13 +1683,10 @@ IonCompile(JSContext *cx, JSScript *script, if (!constraints) return AbortReason_Alloc; - const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(optimizationLevel); - IonBuilder *builder = alloc->new_((JSContext *) nullptr, CompileCompartment::get(cx->compartment()), temp, graph, constraints, - &inspector, info, optimizationInfo, - baselineFrameInspector); + &inspector, info, baselineFrameInspector); if (!builder) return AbortReason_Alloc; @@ -1705,7 +1697,7 @@ IonCompile(JSContext *cx, JSScript *script, IonSpewNewFunction(graph, builderScript); mozilla::Maybe protect; - if (js_JitOptions.checkThreadSafety && + if (js_IonOptions.checkThreadSafety && cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL && !cx->runtime()->profilingScripts && !cx->runtime()->spsProfiler.enabled()) @@ -1726,15 +1718,15 @@ IonCompile(JSContext *cx, JSScript *script, return builder->abortReason(); } - if (recompile) { - JS_ASSERT(executionMode == SequentialExecution); - builderScript->ionScript()->setRecompiling(); - } else { - SetIonScript(builder->script(), executionMode, ION_COMPILING_SCRIPT); - } - // If possible, compile the script off thread. if (OffThreadCompilationAvailable(cx)) { + if (recompile) { + JS_ASSERT(executionMode == SequentialExecution); + builderScript->ionScript()->setRecompiling(); + } else { + SetIonScript(builder->script(), executionMode, ION_COMPILING_SCRIPT); + } + if (!StartOffThreadIonCompile(cx, builder)) { IonSpew(IonSpew_Abort, "Unable to start off-thread ion compilation."); return AbortReason_Alloc; @@ -1802,10 +1794,21 @@ CheckScript(JSContext *cx, JSScript *script, bool osr) return true; } +// Longer scripts can only be compiled off thread, as these compilations +// can be expensive and stall the main thread for too long. +static const uint32_t MAX_OFF_THREAD_SCRIPT_SIZE = 100 * 1000; +static const uint32_t MAX_MAIN_THREAD_SCRIPT_SIZE = 2 * 1000; +static const uint32_t MAX_MAIN_THREAD_LOCALS_AND_ARGS = 256; + +// DOM Worker runtimes don't have off thread compilation, but can also compile +// larger scripts since this doesn't stall the main thread. +static const uint32_t MAX_DOM_WORKER_SCRIPT_SIZE = 16 * 1000; +static const uint32_t MAX_DOM_WORKER_LOCALS_AND_ARGS = 2048; + static MethodStatus CheckScriptSize(JSContext *cx, JSScript* script) { - if (!js_JitOptions.limitScriptSize) + if (!js_IonOptions.limitScriptSize) return Method_Compiled; if (script->length() > MAX_OFF_THREAD_SCRIPT_SIZE) { @@ -1864,17 +1867,6 @@ CanIonCompileScript(JSContext *cx, HandleScript script, bool osr) return CheckScriptSize(cx, script) == Method_Compiled; } -static OptimizationLevel -GetOptimizationLevel(HandleScript script, jsbytecode *pc, ExecutionMode executionMode) -{ - if (executionMode == ParallelExecution) - return Optimization_Normal; - - JS_ASSERT(executionMode == SequentialExecution); - - return js_IonOptimizations.levelForScript(script, pc); -} - static MethodStatus Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc, bool constructing, ExecutionMode executionMode) @@ -1882,8 +1874,6 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode JS_ASSERT(jit::IsIonEnabled(cx)); JS_ASSERT(jit::IsBaselineEnabled(cx)); JS_ASSERT_IF(osrPc != nullptr, (JSOp)*osrPc == JSOP_LOOPENTRY); - JS_ASSERT_IF(executionMode == ParallelExecution, !osrFrame && !osrPc); - JS_ASSERT_IF(executionMode == ParallelExecution, !HasIonScript(script, executionMode)); if (!script->hasBaselineScript()) return Method_Skipped; @@ -1905,50 +1895,23 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode } bool recompile = false; - OptimizationLevel optimizationLevel = GetOptimizationLevel(script, osrPc, executionMode); - if (optimizationLevel == Optimization_DontCompile) - return Method_Skipped; IonScript *scriptIon = GetIonScript(script, executionMode); if (scriptIon) { if (!scriptIon->method()) return Method_CantCompile; + return Method_Compiled; + } - MethodStatus failedState = Method_Compiled; - - // If we keep failing to enter the script due to an OSR pc mismatch, - // recompile with the right pc. - if (osrPc && script->ionScript()->osrPc() != osrPc) { - uint32_t count = script->ionScript()->incrOsrPcMismatchCounter(); - if (count <= js_JitOptions.osrPcMismatchesBeforeRecompile) - return Method_Skipped; - - failedState = Method_Skipped; - } - - // Don't recompile/overwrite higher optimized code, - // with a lower optimization level. - if (optimizationLevel < scriptIon->optimizationLevel()) - return failedState; - - if (optimizationLevel == scriptIon->optimizationLevel() && - (!osrPc || script->ionScript()->osrPc() == osrPc)) - { - return failedState; - } - - // Don't start compiling if already compiling - if (scriptIon->isRecompiling()) - return failedState; - - if (osrPc) - script->ionScript()->resetOsrPcMismatchCounter(); - - recompile = true; + if (executionMode == SequentialExecution) { + // Use getUseCount instead of incUseCount to avoid bumping the + // use count twice. + if (script->getUseCount() < UsesBeforeIonRecompile(script, osrPc ? osrPc : script->code())) + return Method_Skipped; } AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode, - recompile, optimizationLevel); + recompile); if (reason == AbortReason_Error) return Method_Error; @@ -1989,7 +1952,7 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame, return Method_Skipped; // Optionally ignore on user request. - if (!js_JitOptions.osr) + if (!js_IonOptions.osr) return Method_Skipped; // Mark as forbidden if frame can't be handled. @@ -1998,11 +1961,7 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame, return Method_CantCompile; } - // Attempt compilation. - // - Returns Method_Compiled if the right ionscript is present - // (Meaning it was present or a sequantial compile finished) - // - Returns Method_Skipped if pc doesn't match - // (This means a background thread compilation with that pc could have started or not.) + // Attempt compilation. Returns Method_Compiled if already compiled. RootedScript rscript(cx, script); MethodStatus status = Compile(cx, rscript, osrFrame, pc, isConstructing, SequentialExecution); if (status != Method_Compiled) { @@ -2011,6 +1970,20 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame, return status; } + if (script->ionScript()->osrPc() != pc) { + // If we keep failing to enter the script due to an OSR pc mismatch, + // invalidate the script to force a recompile. + uint32_t count = script->ionScript()->incrOsrPcMismatchCounter(); + + if (count > js_IonOptions.osrPcMismatchesBeforeRecompile) { + if (!Invalidate(cx, script, SequentialExecution, true)) + return Method_Error; + } + return Method_Skipped; + } + + script->ionScript()->resetOsrPcMismatchCounter(); + return Method_Compiled; } @@ -2072,7 +2045,7 @@ jit::CanEnter(JSContext *cx, RunState &state) // If --ion-eager is used, compile with Baseline first, so that we // can directly enter IonMonkey. RootedScript rscript(cx, script); - if (js_JitOptions.eagerCompilation && !rscript->hasBaselineScript()) { + if (js_IonOptions.eagerCompilation && !rscript->hasBaselineScript()) { MethodStatus status = CanEnterBaselineMethod(cx, state); if (status != Method_Compiled) return status; @@ -2725,6 +2698,36 @@ jit::ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode) MOZ_ASSUME_UNREACHABLE("No such execution mode"); } +uint32_t +jit::UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc) +{ + JS_ASSERT(pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY); + + uint32_t minUses = js_IonOptions.usesBeforeCompile; + + // If the script is too large to compile on the main thread, we can still + // compile it off thread. In these cases, increase the use count threshold + // to improve the compilation's type information and hopefully avoid later + // recompilation. + + if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE) + minUses = minUses * (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE); + + uint32_t numLocalsAndArgs = analyze::TotalSlots(script); + if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS) + minUses = minUses * (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS); + + if (JSOp(*pc) != JSOP_LOOPENTRY || js_IonOptions.eagerCompilation) + return minUses; + + // It's more efficient to enter outer loops, rather than inner loops, via OSR. + // To accomplish this, we use a slightly higher threshold for inner loops. + // Note that the loop depth is always > 0 so we will prefer non-OSR over OSR. + uint32_t loopDepth = GET_UINT8(pc); + JS_ASSERT(loopDepth > 0); + return minUses + loopDepth * 100; +} + void AutoFlushCache::updateTop(uintptr_t p, size_t len) { diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index f516f1e12698..37ad4e778387 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -16,13 +16,248 @@ #include "jit/CompileInfo.h" #include "jit/CompileWrappers.h" -#include "jit/JitOptions.h" namespace js { namespace jit { class TempAllocator; +// Possible register allocators which may be used. +enum IonRegisterAllocator { + RegisterAllocator_LSRA, + RegisterAllocator_Backtracking, + RegisterAllocator_Stupid +}; + +struct IonOptions +{ + // Toggles whether global value numbering is used. + // + // Default: true + bool gvn; + + // Toggles whether global value numbering is optimistic (true) or + // pessimistic (false). + // + // Default: true + bool gvnIsOptimistic; + + // Toggles whether loop invariant code motion is performed. + // + // Default: true + bool licm; + + // Toggles whether functions may be entered at loop headers. + // + // Default: true + bool osr; + + // Toggles whether large scripts are rejected. + // + // Default: true + bool limitScriptSize; + + // Describes which register allocator to use. + // + // Default: LSRA + IonRegisterAllocator registerAllocator; + + // Toggles whether inlining is performed. + // + // Default: true + bool inlining; + + // Toggles whether Edge Case Analysis is used. + // + // Default: true + bool edgeCaseAnalysis; + + // Toggles whether Range Analysis is used. + // + // Default: true + bool rangeAnalysis; + + // Whether to enable extra code to perform dynamic validation of + // RangeAnalysis results. + // + // Default: false + bool checkRangeAnalysis; + + // Whether to protect the GC heap during Ion compilation and ensure that + // only threadsafe operations are performed on it. + // + // Default: false + bool checkThreadSafety; + + // Whether to perform expensive graph-consistency DEBUG-only assertions. + // It can be useful to disable this to reduce DEBUG-compile time of large + // asm.js programs. + // + // Default: true + bool checkGraphConsistency; + + // Toggles whether Unreachable Code Elimination is performed. + // + // Default: true + bool uce; + + // Toggles whether Effective Address Analysis is performed. + // + // Default: true + bool eaa; + +#ifdef CHECK_OSIPOINT_REGISTERS + // Emit extra code to verify live regs at the start of a VM call + // are not modified before its OsiPoint. + // + // Default: false + bool checkOsiPointRegisters; +#endif + + // How many invocations or loop iterations are needed before functions + // are compiled with the baseline compiler. + // + // Default: 10 + uint32_t baselineUsesBeforeCompile; + + // How many invocations or loop iterations are needed before functions + // are compiled. + // + // Default: 1,000 + uint32_t usesBeforeCompile; + + // How many invocations or loop iterations are needed before calls + // are inlined, as a fraction of usesBeforeCompile. + // + // Default: .125 + double usesBeforeInliningFactor; + + // How many times we will try to enter a script via OSR before + // invalidating the script. + // + // Default: 6,000 + uint32_t osrPcMismatchesBeforeRecompile; + + // Number of bailouts without invalidation before we set + // JSScript::hadFrequentBailouts and invalidate. + // + // Default: 10 + uint32_t frequentBailoutThreshold; + + // Number of exception bailouts (resuming into catch/finally block) before + // we invalidate and forbid Ion compilation. + // + // Default: 10 + uint32_t exceptionBailoutThreshold; + + // Whether Ion should compile try-catch statements. + // + // Default: true + bool compileTryCatch; + + // How many actual arguments are accepted on the C stack. + // + // Default: 4,096 + uint32_t maxStackArgs; + + // The maximum inlining depth. + // + // Default: 3 + uint32_t maxInlineDepth; + + // The maximum inlining depth for functions. + // + // Inlining small functions has almost no compiling overhead + // and removes the otherwise needed call overhead. + // The value is currently very low. + // Actually it is only needed to make sure we don't blow out the stack. + // + // Default: 10 + uint32_t smallFunctionMaxInlineDepth; + + // The bytecode length limit for small function. + // + // The default for this was arrived at empirically via benchmarking. + // We may want to tune it further after other optimizations have gone + // in. + // + // Default: 100 + uint32_t smallFunctionMaxBytecodeLength; + + // The maximum number of functions to polymorphically inline at a call site. + // + // Default: 4 + uint32_t polyInlineMax; + + // The maximum total bytecode size of an inline call site. + // + // Default: 1000 + uint32_t inlineMaxTotalBytecodeLength; + + // Whether functions are compiled immediately. + // + // Default: false + bool eagerCompilation; + + // How many uses of a parallel kernel before we attempt compilation. + // + // Default: 1 + uint32_t usesBeforeCompilePar; + + // The maximum bytecode length the caller may have, + // before we stop inlining large functions in that caller. + // + // Default: 10000 + uint32_t inliningMaxCallerBytecodeLength; + + void setEagerCompilation() { + eagerCompilation = true; + usesBeforeCompile = 0; + baselineUsesBeforeCompile = 0; + } + + MOZ_CONSTEXPR IonOptions() + : gvn(true), + gvnIsOptimistic(true), + licm(true), + osr(true), + limitScriptSize(true), + registerAllocator(RegisterAllocator_LSRA), + inlining(true), + edgeCaseAnalysis(true), + rangeAnalysis(true), + checkRangeAnalysis(false), + checkThreadSafety(false), + checkGraphConsistency(true), + uce(true), + eaa(true), +#ifdef CHECK_OSIPOINT_REGISTERS + checkOsiPointRegisters(false), +#endif + baselineUsesBeforeCompile(10), + usesBeforeCompile(1000), + usesBeforeInliningFactor(.125), + osrPcMismatchesBeforeRecompile(6000), + frequentBailoutThreshold(10), + exceptionBailoutThreshold(10), + compileTryCatch(true), + maxStackArgs(4096), + maxInlineDepth(3), + smallFunctionMaxInlineDepth(10), + smallFunctionMaxBytecodeLength(100), + polyInlineMax(4), + inlineMaxTotalBytecodeLength(1000), + eagerCompilation(false), + usesBeforeCompilePar(1), + inliningMaxCallerBytecodeLength(10000) + { + } + + uint32_t usesBeforeInlining() { + return usesBeforeCompile * usesBeforeInliningFactor; + } +}; + enum MethodStatus { Method_Error, @@ -73,6 +308,8 @@ class IonContext int assemblerCount_; }; +extern IonOptions js_IonOptions; + // Initialize Ion statically for all JSRuntimes. bool InitializeIon(); @@ -174,11 +411,12 @@ IsIonInlinablePC(jsbytecode *pc) { inline bool TooManyArguments(unsigned nargs) { - return (nargs >= SNAPSHOT_MAX_NARGS || nargs > js_JitOptions.maxStackArgs); + return (nargs >= SNAPSHOT_MAX_NARGS || nargs > js_IonOptions.maxStackArgs); } void ForbidCompilation(JSContext *cx, JSScript *script); void ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode); +uint32_t UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc); void PurgeCaches(JSScript *script, JS::Zone *zone); size_t SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index a685efa82c89..839c3ecb2305 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -12,7 +12,6 @@ #include "jit/BaselineJIT.h" #include "jit/Ion.h" #include "jit/IonBuilder.h" -#include "jit/IonOptimizationLevels.h" #include "jit/LIR.h" #include "jit/Lowering.h" #include "jit/MIRGraph.h" @@ -1334,7 +1333,7 @@ void jit::AssertGraphCoherency(MIRGraph &graph) { #ifdef DEBUG - if (!js_JitOptions.checkGraphConsistency) + if (!js_IonOptions.checkGraphConsistency) return; AssertBasicGraphCoherency(graph); AssertReversePostOrder(graph); @@ -1349,7 +1348,7 @@ jit::AssertExtendedGraphCoherency(MIRGraph &graph) // are split) #ifdef DEBUG - if (!js_JitOptions.checkGraphConsistency) + if (!js_IonOptions.checkGraphConsistency) return; AssertGraphCoherency(graph); @@ -2159,12 +2158,10 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun, AutoTempAllocatorRooter root(cx, &temp); - const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_Normal); - types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp); BaselineInspector inspector(script); IonBuilder builder(cx, CompileCompartment::get(cx->compartment()), &temp, &graph, constraints, - &inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr); + &inspector, &info, /* baselineFrame = */ nullptr); if (!builder.build()) { if (builder.abortReason() == AbortReason_Alloc) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 24eeae4a238e..28b2285a8fdc 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -18,7 +18,6 @@ #include "jit/BaselineInspector.h" #include "jit/ExecutionModeInlines.h" #include "jit/Ion.h" -#include "jit/IonOptimizationLevels.h" #include "jit/IonSpewer.h" #include "jit/Lowering.h" #include "jit/MIRGraph.h" @@ -102,13 +101,11 @@ jit::NewBaselineFrameInspector(TempAllocator *temp, BaselineFrame *frame) return inspector; } -IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, - MIRGraph *graph, types::CompilerConstraintList *constraints, - BaselineInspector *inspector, CompileInfo *info, - const OptimizationInfo *optimizationInfo, - BaselineFrameInspector *baselineFrame, size_t inliningDepth, - uint32_t loopDepth) - : MIRGenerator(comp, temp, graph, info, optimizationInfo), +IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, MIRGraph *graph, + types::CompilerConstraintList *constraints, + BaselineInspector *inspector, CompileInfo *info, BaselineFrameInspector *baselineFrame, + size_t inliningDepth, uint32_t loopDepth) + : MIRGenerator(comp, temp, graph, info), backgroundCodegen_(nullptr), analysisContext(analysisContext), baselineFrame_(baselineFrame), @@ -330,9 +327,6 @@ IonBuilder::DontInline(JSScript *targetScript, const char *reason) IonBuilder::InliningDecision IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo) { - if (!optimizationInfo().inlineInterpreted()) - return InliningDecision_DontInline; - if (!target->isInterpreted()) return DontInline(nullptr, "Non-interpreted target"); @@ -609,17 +603,8 @@ IonBuilder::build() if (!current) return false; -#ifdef DEBUG - if (info().executionMode() == SequentialExecution && script()->hasIonScript()) { - IonSpew(IonSpew_Scripts, "Recompiling script %s:%d (%p) (usecount=%d, level=%s)", - script()->filename(), script()->lineno(), (void *)script(), - (int)script()->getUseCount(), OptimizationLevelString(optimizationInfo().level())); - } else { - IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d, level=%s)", - script()->filename(), script()->lineno(), (void *)script(), - (int)script()->getUseCount(), OptimizationLevelString(optimizationInfo().level())); - } -#endif + IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d)", + script()->filename(), script()->lineno(), (void *)script(), (int)script()->getUseCount()); if (!initParameters()) return false; @@ -706,8 +691,6 @@ IonBuilder::build() current->add(lazyArguments_); } - insertRecompileCheck(); - if (!traverseBytecode()) return false; @@ -865,8 +848,6 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi current->add(lazyArguments_); } - insertRecompileCheck(); - if (!traverseBytecode()) return false; @@ -2047,7 +2028,7 @@ IonBuilder::restartLoop(CFGState state) { spew("New types at loop header, restarting loop body"); - if (js_JitOptions.limitScriptSize) { + if (js_IonOptions.limitScriptSize) { if (++numLoopRestarts_ >= MAX_LOOP_RESTARTS) return ControlStatus_Abort; } @@ -3358,7 +3339,6 @@ IonBuilder::jsop_loophead(jsbytecode *pc) assertValidLoopHeadOp(pc); current->add(MInterruptCheck::New(alloc())); - insertRecompileCheck(); return true; } @@ -3447,7 +3427,7 @@ IonBuilder::jsop_try() { JS_ASSERT(JSOp(*pc) == JSOP_TRY); - if (!js_JitOptions.compileTryCatch) + if (!js_IonOptions.compileTryCatch) return abort("Try-catch support disabled"); // Try-finally is not yet supported. @@ -3866,9 +3846,9 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) AutoAccumulateReturns aar(graph(), returns); // Build the graph. - IonBuilder inlineBuilder(analysisContext, compartment, &alloc(), &graph(), constraints(), - &inspector, info, &optimizationInfo(), nullptr, inliningDepth_ + 1, - loopDepth_); + IonBuilder inlineBuilder(analysisContext, compartment, + &alloc(), &graph(), constraints(), &inspector, info, nullptr, + inliningDepth_ + 1, loopDepth_); if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) { if (analysisContext && analysisContext->isExceptionPending()) { IonSpew(IonSpew_Abort, "Inline builder raised exception."); @@ -3980,9 +3960,19 @@ IonBuilder::patchInlinedReturns(CallInfo &callInfo, MIRGraphReturns &returns, MB return phi; } +static bool +IsSmallFunction(JSScript *script) +{ + return script->length() <= js_IonOptions.smallFunctionMaxBytecodeLength; +} + IonBuilder::InliningDecision IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo) { + // Only inline when inlining is enabled. + if (!inliningEnabled()) + return InliningDecision_DontInline; + // When there is no target, inlining is impossible. if (target == nullptr) return InliningDecision_DontInline; @@ -4002,30 +3992,30 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo) // Skip heuristics if we have an explicit hint to inline. if (!targetScript->shouldInline()) { // Cap the inlining depth. - if (js_JitOptions.isSmallFunction(targetScript)) { - if (inliningDepth_ >= optimizationInfo().smallFunctionMaxInlineDepth()) + if (IsSmallFunction(targetScript)) { + if (inliningDepth_ >= js_IonOptions.smallFunctionMaxInlineDepth) return DontInline(targetScript, "Vetoed: exceeding allowed inline depth"); } else { - if (inliningDepth_ >= optimizationInfo().maxInlineDepth()) + if (inliningDepth_ >= js_IonOptions.maxInlineDepth) return DontInline(targetScript, "Vetoed: exceeding allowed inline depth"); if (targetScript->hasLoops()) return DontInline(targetScript, "Vetoed: big function that contains a loop"); // Caller must not be excessively large. - if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) + if (script()->length() >= js_IonOptions.inliningMaxCallerBytecodeLength) return DontInline(targetScript, "Vetoed: caller excessively large"); } // Callee must not be excessively large. // This heuristic also applies to the callsite as a whole. - if (targetScript->length() > optimizationInfo().inlineMaxTotalBytecodeLength()) + if (targetScript->length() > js_IonOptions.inlineMaxTotalBytecodeLength) return DontInline(targetScript, "Vetoed: callee excessively large"); // Callee must have been called a few times to have somewhat stable // type information, except for definite properties analysis, // as the caller has not run yet. - if (targetScript->getUseCount() < optimizationInfo().usesBeforeInlining() && + if (targetScript->getUseCount() < js_IonOptions.usesBeforeInlining() && info().executionMode() != DefinitePropertiesAnalysis) { return DontInline(targetScript, "Vetoed: callee is insufficiently hot."); @@ -4068,7 +4058,7 @@ IonBuilder::selectInliningTargets(ObjectVector &targets, CallInfo &callInfo, Boo // Enforce a maximum inlined bytecode limit at the callsite. if (inlineable && target->isInterpreted()) { totalSize += target->nonLazyScript()->length(); - if (totalSize > optimizationInfo().inlineMaxTotalBytecodeLength()) + if (totalSize > js_IonOptions.inlineMaxTotalBytecodeLength) inlineable = false; } @@ -4162,6 +4152,9 @@ IonBuilder::InliningStatus IonBuilder::inlineCallsite(ObjectVector &targets, ObjectVector &originals, bool lambda, CallInfo &callInfo) { + if (!inliningEnabled()) + return InliningStatus_NotInlined; + if (targets.length() == 0) return InliningStatus_NotInlined; @@ -5998,34 +5991,6 @@ ClassHasResolveHook(CompileCompartment *comp, const Class *clasp, PropertyName * return true; } -void -IonBuilder::insertRecompileCheck() -{ - // PJS doesn't recompile and doesn't need recompile checks. - if (info().executionMode() != SequentialExecution) - return; - - // No need for recompile checks if this is the highest optimization level. - OptimizationLevel curLevel = optimizationInfo().level(); - if (js_IonOptimizations.isLastLevel(curLevel)) - return; - - // Add recompile check. - - // Get the topmost builder. The topmost script will get recompiled when - // usecount is high enough to justify a higher optimization level. - IonBuilder *topBuilder = this; - while (topBuilder->callerBuilder_) - topBuilder = topBuilder->callerBuilder_; - - // Add recompile check to recompile when the usecount reaches the usecount - // of the next optimization level. - OptimizationLevel nextLevel = js_IonOptimizations.nextLevel(curLevel); - const OptimizationInfo *info = js_IonOptimizations.get(nextLevel); - uint32_t useCount = info->usesBeforeCompile(topBuilder->script()); - current->add(MRecompileCheck::New(alloc(), topBuilder->script(), useCount)); -} - JSObject * IonBuilder::testSingletonProperty(JSObject *obj, PropertyName *name) { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index d2af9d5b8b89..2404e147d2df 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -13,7 +13,6 @@ // JSScript. #include "jit/BytecodeAnalysis.h" -#include "jit/IonOptimizationLevels.h" #include "jit/MIR.h" #include "jit/MIRGenerator.h" #include "jit/MIRGraph.h" @@ -212,10 +211,9 @@ class IonBuilder : public MIRGenerator static int CmpSuccessors(const void *a, const void *b); public: - IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, - MIRGraph *graph, types::CompilerConstraintList *constraints, - BaselineInspector *inspector, CompileInfo *info, - const OptimizationInfo *optimizationInfo, BaselineFrameInspector *baselineFrame, + IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, MIRGraph *graph, + types::CompilerConstraintList *constraints, + BaselineInspector *inspector, CompileInfo *info, BaselineFrameInspector *baselineFrame, size_t inliningDepth = 0, uint32_t loopDepth = 0); bool build(); @@ -232,6 +230,10 @@ class IonBuilder : public MIRGenerator bool abort(const char *message, ...); void spew(const char *message); + static bool inliningEnabled() { + return js_IonOptions.inlining; + } + JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes); bool getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing, ObjectVector &targets, uint32_t maxTargets, bool *gotLambda); @@ -323,8 +325,6 @@ class IonBuilder : public MIRGenerator bool resumeAfter(MInstruction *ins); bool maybeInsertResume(); - void insertRecompileCheck(); - bool initParameters(); void rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex); void rewriteParameters(); diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index d3ecaa94223e..8167d1fa80e9 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -14,7 +14,6 @@ #include "jstypes.h" #include "gc/Heap.h" -#include "jit/IonOptimizationLevels.h" #include "jit/IonTypes.h" namespace JSC { @@ -262,9 +261,6 @@ struct IonScript // Identifier of the compilation which produced this code. types::RecompileInfo recompileInfo_; - // The optimization level this script was compiled in. - OptimizationLevel optimizationLevel_; - // Number of times we tried to enter this script via OSR but failed due to // a LOOPENTRY pc other than osrPc_. uint32_t osrPcMismatchCounter_; @@ -339,8 +335,7 @@ struct IonScript size_t snapshotsSize, size_t snapshotEntries, size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries, size_t cacheEntries, size_t runtimeSize, size_t safepointsSize, - size_t callTargetEntries, size_t backedgeEntries, - OptimizationLevel optimizationLevel); + size_t callTargetEntries, size_t backedgeEntries); static void Trace(JSTracer *trc, IonScript *script); static void Destroy(FreeOp *fop, IonScript *script); @@ -531,9 +526,6 @@ struct IonScript const types::RecompileInfo& recompileInfo() const { return recompileInfo_; } - OptimizationLevel optimizationLevel() const { - return optimizationLevel_; - } uint32_t incrOsrPcMismatchCounter() { return ++osrPcMismatchCounter_; } diff --git a/js/src/jit/IonFrames.cpp b/js/src/jit/IonFrames.cpp index 66beb292251b..cecde0dee5cc 100644 --- a/js/src/jit/IonFrames.cpp +++ b/js/src/jit/IonFrames.cpp @@ -1061,7 +1061,7 @@ static void MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations) { #ifdef CHECK_OSIPOINT_REGISTERS - if (js_JitOptions.checkOsiPointRegisters) { + if (js_IonOptions.checkOsiPointRegisters) { // GC can modify spilled registers, breaking our register checks. // To handle this, we disable these checks for the current VM call // when a GC happens. diff --git a/js/src/jit/IonOptimizationLevels.cpp b/js/src/jit/IonOptimizationLevels.cpp deleted file mode 100644 index cd2009d03acd..000000000000 --- a/js/src/jit/IonOptimizationLevels.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "jit/IonOptimizationLevels.h" - -#include "jsanalyze.h" -#include "jsscript.h" - -using namespace js; -using namespace js::jit; - -namespace js { -namespace jit { - -OptimizationInfos js_IonOptimizations; - -void -OptimizationInfo::initNormalOptimizationInfo() -{ - level_ = Optimization_Normal; - - eaa_ = true; - edgeCaseAnalysis_ = true; - eliminateRedundantChecks_ = true; - inlineInterpreted_ = true; - inlineNative_ = true; - gvn_ = true; - gvnKind_ = GVN_Optimistic; - licm_ = true; - uce_ = true; - rangeAnalysis_ = true; - registerAllocator_ = RegisterAllocator_LSRA; - - inlineMaxTotalBytecodeLength_ = 1000; - inliningMaxCallerBytecodeLength_ = 10000; - maxInlineDepth_ = 3; - smallFunctionMaxInlineDepth_ = 10; - usesBeforeCompile_ = 1000; - usesBeforeInliningFactor_ = 0.125; -} - -void -OptimizationInfo::initAsmjsOptimizationInfo() -{ - // The AsmJS optimization level - // Disables some passes that don't work well with asmjs. - - // Take normal option values for not specified values. - initNormalOptimizationInfo(); - - level_ = Optimization_AsmJS; - edgeCaseAnalysis_ = false; - eliminateRedundantChecks_ = false; -} - -uint32_t -OptimizationInfo::usesBeforeCompile(JSScript *script, jsbytecode *pc) const -{ - JS_ASSERT(pc == nullptr || pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY); - - if (pc == script->code()) - pc = nullptr; - - uint32_t minUses = usesBeforeCompile_; - if (js_JitOptions.forceDefaultIonUsesBeforeCompile) - minUses = js_JitOptions.forcedDefaultIonUsesBeforeCompile; - - // If the script is too large to compile on the main thread, we can still - // compile it off thread. In these cases, increase the use count threshold - // to improve the compilation's type information and hopefully avoid later - // recompilation. - - if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE) - minUses = minUses * (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE); - - uint32_t numLocalsAndArgs = analyze::TotalSlots(script); - if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS) - minUses = minUses * (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS); - - if (!pc || js_JitOptions.eagerCompilation) - return minUses; - - // It's more efficient to enter outer loops, rather than inner loops, via OSR. - // To accomplish this, we use a slightly higher threshold for inner loops. - // Note that the loop depth is always > 0 so we will prefer non-OSR over OSR. - uint32_t loopDepth = GET_UINT8(pc); - JS_ASSERT(loopDepth > 0); - return minUses + loopDepth * 100; -} - -OptimizationInfos::OptimizationInfos() -{ - infos_[Optimization_Normal - 1].initNormalOptimizationInfo(); - infos_[Optimization_AsmJS - 1].initAsmjsOptimizationInfo(); -} - -OptimizationLevel -OptimizationInfos::nextLevel(OptimizationLevel level) const -{ - JS_ASSERT(!isLastLevel(level)); - switch (level) { - case Optimization_DontCompile: - return Optimization_Normal; - default: - MOZ_ASSUME_UNREACHABLE("Unknown optimization level."); - } -} - -OptimizationLevel -OptimizationInfos::firstLevel() const -{ - return nextLevel(Optimization_DontCompile); -} - -bool -OptimizationInfos::isLastLevel(OptimizationLevel level) const -{ - return level == Optimization_Normal; -} - -OptimizationLevel -OptimizationInfos::levelForScript(JSScript *script, jsbytecode *pc) const -{ - OptimizationLevel prev = Optimization_DontCompile; - - while (!isLastLevel(prev)) { - OptimizationLevel level = nextLevel(prev); - const OptimizationInfo *info = get(level); - if (script->getUseCount() < info->usesBeforeCompile(script, pc)) - return prev; - - prev = level; - } - - return prev; -} - -} // namespace jit -} // namespace js diff --git a/js/src/jit/IonOptimizationLevels.h b/js/src/jit/IonOptimizationLevels.h deleted file mode 100644 index b939bfbfdd30..000000000000 --- a/js/src/jit/IonOptimizationLevels.h +++ /dev/null @@ -1,220 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jit_IonOptimizationLevels_h -#define jit_IonOptimizationLevels_h - -#include "jsbytecode.h" -#include "jstypes.h" - -#include "jit/JitOptions.h" -#include "js/TypeDecls.h" - -namespace js { -namespace jit { - -enum OptimizationLevel -{ - Optimization_DontCompile, - Optimization_Normal, - Optimization_AsmJS, - Optimization_Count -}; - -#ifdef DEBUG -inline const char * -OptimizationLevelString(OptimizationLevel level) -{ - switch (level) { - case Optimization_DontCompile: - return "Optimization_DontCompile"; - case Optimization_Normal: - return "Optimization_Normal"; - case Optimization_AsmJS: - return "Optimization_AsmJS"; - default: - MOZ_ASSUME_UNREACHABLE("Invalid OptimizationLevel"); - } -} -#endif - -class OptimizationInfo -{ - public: - OptimizationLevel level_; - - // Toggles whether Effective Address Analysis is performed. - bool eaa_; - - // Toggles whether Edge Case Analysis is used. - bool edgeCaseAnalysis_; - - // Toggles whether redundant checks get removed. - bool eliminateRedundantChecks_; - - // Toggles whether interpreted scripts get inlined. - bool inlineInterpreted_; - - // Toggles whether native scripts get inlined. - bool inlineNative_; - - // Toggles whether global value numbering is used. - bool gvn_; - - // Toggles whether global value numbering is optimistic or pessimistic. - IonGvnKind gvnKind_; - - // Toggles whether loop invariant code motion is performed. - bool licm_; - - // Toggles whether Unreachable Code Elimination is performed. - bool uce_; - - // Toggles whether Range Analysis is used. - bool rangeAnalysis_; - - // Describes which register allocator to use. - IonRegisterAllocator registerAllocator_; - - // The maximum total bytecode size of an inline call site. - uint32_t inlineMaxTotalBytecodeLength_; - - // The maximum bytecode length the caller may have, - // before we stop inlining large functions in that caller. - uint32_t inliningMaxCallerBytecodeLength_; - - // The maximum inlining depth. - uint32_t maxInlineDepth_; - - // The maximum inlining depth for functions. - // - // Inlining small functions has almost no compiling overhead - // and removes the otherwise needed call overhead. - // The value is currently very low. - // Actually it is only needed to make sure we don't blow out the stack. - uint32_t smallFunctionMaxInlineDepth_; - - // How many invocations or loop iterations are needed before functions - // are compiled. - uint32_t usesBeforeCompile_; - - // How many invocations or loop iterations are needed before calls - // are inlined, as a fraction of usesBeforeCompile. - double usesBeforeInliningFactor_; - - OptimizationInfo() - { } - - void initNormalOptimizationInfo(); - void initAsmjsOptimizationInfo(); - - OptimizationLevel level() const { - return level_; - } - - bool inlineInterpreted() const { - return inlineInterpreted_ && !js_JitOptions.disableInlining; - } - - bool inlineNative() const { - return inlineNative_ && !js_JitOptions.disableInlining; - } - - uint32_t usesBeforeCompile(JSScript *script, jsbytecode *pc = nullptr) const; - - bool gvnEnabled() const { - return gvn_ && !js_JitOptions.disableGvn; - } - - bool licmEnabled() const { - return licm_ && !js_JitOptions.disableLicm; - } - - bool uceEnabled() const { - return uce_ && !js_JitOptions.disableUce; - } - - bool rangeAnalysisEnabled() const { - return rangeAnalysis_ && !js_JitOptions.disableRangeAnalysis; - } - - bool eaaEnabled() const { - return eaa_ && !js_JitOptions.disableEaa; - } - - bool edgeCaseAnalysisEnabled() const { - return edgeCaseAnalysis_ && !js_JitOptions.disableEdgeCaseAnalysis; - } - - bool eliminateRedundantChecksEnabled() const { - return eliminateRedundantChecks_; - } - - IonGvnKind gvnKind() const { - if (!js_JitOptions.forceGvnKind) - return gvnKind_; - return js_JitOptions.forcedGvnKind; - } - - IonRegisterAllocator registerAllocator() const { - if (!js_JitOptions.forceRegisterAllocator) - return registerAllocator_; - return js_JitOptions.forcedRegisterAllocator; - } - - uint32_t smallFunctionMaxInlineDepth() const { - return smallFunctionMaxInlineDepth_; - } - - bool isSmallFunction(JSScript *script) const; - - uint32_t maxInlineDepth() const { - return maxInlineDepth_; - } - - uint32_t inlineMaxTotalBytecodeLength() const { - return inlineMaxTotalBytecodeLength_; - } - - uint32_t inliningMaxCallerBytecodeLength() const { - return inlineMaxTotalBytecodeLength_; - } - - uint32_t usesBeforeInlining() const { - uint32_t usesBeforeCompile = usesBeforeCompile_; - if (js_JitOptions.forceDefaultIonUsesBeforeCompile) - usesBeforeCompile = js_JitOptions.forcedDefaultIonUsesBeforeCompile; - return usesBeforeCompile * usesBeforeInliningFactor_; - } -}; - -class OptimizationInfos -{ - private: - OptimizationInfo infos_[Optimization_Count - 1]; - - public: - OptimizationInfos(); - - const OptimizationInfo *get(OptimizationLevel level) const { - JS_ASSERT(level < Optimization_Count); - JS_ASSERT(level != Optimization_DontCompile); - - return &infos_[level - 1]; - } - - OptimizationLevel nextLevel(OptimizationLevel level) const; - OptimizationLevel firstLevel() const; - bool isLastLevel(OptimizationLevel level) const; - OptimizationLevel levelForScript(JSScript *script, jsbytecode *pc = nullptr) const; -}; - -extern OptimizationInfos js_IonOptimizations; - -} // namespace jit -} // namespace js - -#endif /* jit_IonOptimizationLevels_h */ diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp deleted file mode 100644 index a7d6dc1a6e67..000000000000 --- a/js/src/jit/JitOptions.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "jit/JitOptions.h" - -#include "jsscript.h" - -using namespace js; -using namespace js::jit; - -namespace js { -namespace jit { - -JitOptions js_JitOptions; - -JitOptions::JitOptions() -{ - // Whether to perform expensive graph-consistency DEBUG-only assertions. - // It can be useful to disable this to reduce DEBUG-compile time of large - // asm.js programs. - checkGraphConsistency = true; - -#ifdef CHECK_OSIPOINT_REGISTERS - // Emit extra code to verify live regs at the start of a VM call - // are not modified before its OsiPoint. - checkOsiPointRegisters = false; -#endif - - // Whether to enable extra code to perform dynamic validation of - // RangeAnalysis results. - checkRangeAnalysis = false; - - // Whether to protect the GC heap during Ion compilation and ensure that - // only threadsafe operations are performed on it. - checkThreadSafety = false; - - // Whether Ion should compile try-catch statements. - compileTryCatch = true; - - // Toggle whether global value numbering is globally disabled. - disableGvn = false; - - // Toggles whether loop invariant code motion is globally disabled. - disableLicm = false; - - // Toggles whether inlining is globally disabled. - disableInlining = false; - - // Toggles whether Edge Case Analysis is gobally disabled. - disableEdgeCaseAnalysis = false; - - // Toggles whether Range Analysis is globally disabled. - disableRangeAnalysis = false; - - // Toggles whether Unreachable Code Elimination is globally disabled. - disableUce = false; - - // Toggles whether Effective Address Analysis is globally disabled. - disableEaa = false; - - // Whether functions are compiled immediately. - eagerCompilation = false; - - // Force how many invocation or loop iterations are needed before compiling - // a function with the highest ionmonkey optimization level. - // (i.e. OptimizationLevel_Normal) - forceDefaultIonUsesBeforeCompile = false; - forcedDefaultIonUsesBeforeCompile = 1000; - - // Force the GVN kind to be optimistic or pessimistic instead of letting - // the optimization pass decide. - forceGvnKind = false; - forcedGvnKind = GVN_Optimistic; - - // Force the used register allocator instead of letting the - // optimization pass decide. - forceRegisterAllocator = false; - forcedRegisterAllocator = RegisterAllocator_LSRA; - - // Toggles whether large scripts are rejected. - limitScriptSize = true; - - // Toggles whether functions may be entered at loop headers. - osr = true; - - // How many invocations or loop iterations are needed before functions - // are compiled with the baseline compiler. - baselineUsesBeforeCompile = 10; - - // Number of exception bailouts (resuming into catch/finally block) before - // we invalidate and forbid Ion compilation. - exceptionBailoutThreshold = 10; - - // Number of bailouts without invalidation before we set - // JSScript::hadFrequentBailouts and invalidate. - frequentBailoutThreshold = 10; - - // How many actual arguments are accepted on the C stack. - maxStackArgs = 4096; - - // How many times we will try to enter a script via OSR before - // invalidating the script. - osrPcMismatchesBeforeRecompile = 6000; - - // The bytecode length limit for small function. - // - // The default for this was arrived at empirically via benchmarking. - // We may want to tune it further after other optimizations have gone - // in. - smallFunctionMaxBytecodeLength_ = 100; - - // How many uses of a parallel kernel before we attempt compilation. - usesBeforeCompilePar = 1; -} - -bool -JitOptions::isSmallFunction(JSScript *script) const -{ - return script->length() <= smallFunctionMaxBytecodeLength_; -} - -void -JitOptions::setEagerCompilation() -{ - eagerCompilation = true; - baselineUsesBeforeCompile = 0; - forceDefaultIonUsesBeforeCompile = true; - forcedDefaultIonUsesBeforeCompile = 0; -} - -void -JitOptions::setUsesBeforeCompile(uint32_t useCount) -{ - forceDefaultIonUsesBeforeCompile = true; - forcedDefaultIonUsesBeforeCompile = useCount; - - // Undo eager compilation - if (eagerCompilation && useCount != 0) { - jit::JitOptions defaultValues; - eagerCompilation = false; - baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile; - } -} - -void -JitOptions::resetUsesBeforeCompile() -{ - forceDefaultIonUsesBeforeCompile = false; - - // Undo eager compilation - if (eagerCompilation) { - jit::JitOptions defaultValues; - eagerCompilation = false; - baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile; - } -} - -} // namespace jit -} // namespace js diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h deleted file mode 100644 index a6f3d5843063..000000000000 --- a/js/src/jit/JitOptions.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jit_JitOptions_h -#define jit_JitOptions_h - -#include "jit/IonTypes.h" -#include "js/TypeDecls.h" - -#ifdef JS_ION - -namespace js { -namespace jit { - -// Longer scripts can only be compiled off thread, as these compilations -// can be expensive and stall the main thread for too long. -static const uint32_t MAX_OFF_THREAD_SCRIPT_SIZE = 100 * 1000; -static const uint32_t MAX_MAIN_THREAD_SCRIPT_SIZE = 2 * 1000; -static const uint32_t MAX_MAIN_THREAD_LOCALS_AND_ARGS = 256; - -// DOM Worker runtimes don't have off thread compilation, but can also compile -// larger scripts since this doesn't stall the main thread. -static const uint32_t MAX_DOM_WORKER_SCRIPT_SIZE = 16 * 1000; -static const uint32_t MAX_DOM_WORKER_LOCALS_AND_ARGS = 2048; - -// Possible register allocators which may be used. -enum IonRegisterAllocator { - RegisterAllocator_LSRA, - RegisterAllocator_Backtracking, - RegisterAllocator_Stupid -}; - -enum IonGvnKind { - GVN_Optimistic, - GVN_Pessimistic -}; - -struct JitOptions -{ - bool checkGraphConsistency; -#ifdef CHECK_OSIPOINT_REGISTERS - bool checkOsiPointRegisters; -#endif - bool checkRangeAnalysis; - bool checkThreadSafety; - bool compileTryCatch; - bool disableGvn; - bool disableLicm; - bool disableInlining; - bool disableEdgeCaseAnalysis; - bool disableRangeAnalysis; - bool disableUce; - bool disableEaa; - bool eagerCompilation; - bool forceDefaultIonUsesBeforeCompile; - uint32_t forcedDefaultIonUsesBeforeCompile; - bool forceGvnKind; - IonGvnKind forcedGvnKind; - bool forceRegisterAllocator; - IonRegisterAllocator forcedRegisterAllocator; - bool limitScriptSize; - bool osr; - uint32_t baselineUsesBeforeCompile; - uint32_t exceptionBailoutThreshold; - uint32_t frequentBailoutThreshold; - uint32_t maxStackArgs; - uint32_t osrPcMismatchesBeforeRecompile; - uint32_t smallFunctionMaxBytecodeLength_; - uint32_t usesBeforeCompilePar; - - JitOptions(); - bool isSmallFunction(JSScript *script) const; - void setEagerCompilation(); - void setUsesBeforeCompile(uint32_t useCount); - void resetUsesBeforeCompile(); -}; - -extern JitOptions js_JitOptions; - -} // namespace jit -} // namespace js - -#endif // JS_ION - -#endif /* jit_JitOptions_h */ diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 7ed29e1ac609..13c803502d6c 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -5768,23 +5768,6 @@ class LAssertRangeV : public LInstructionHelper<0, BOX_PIECES, 3> } }; -class LRecompileCheck : public LInstructionHelper<0, 0, 1> -{ - public: - LIR_HEADER(RecompileCheck) - - LRecompileCheck(const LDefinition &scratch) { - setTemp(0, scratch); - } - - const LDefinition *scratch() { - return getTemp(0); - } - MRecompileCheck *mir() { - return mir_->toRecompileCheck(); - } -}; - } // namespace jit } // namespace js diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 2b13b1ad1f66..44d9db360964 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -280,7 +280,6 @@ _(AsmJSCall) \ _(AsmJSCheckOverRecursed) \ _(CheckInterruptPar) \ - _(RecompileCheck) \ _(AssertRangeI) \ _(AssertRangeD) \ _(AssertRangeF) \ diff --git a/js/src/jit/LinearScan.cpp b/js/src/jit/LinearScan.cpp index 154de5762109..cd22f4d36818 100644 --- a/js/src/jit/LinearScan.cpp +++ b/js/src/jit/LinearScan.cpp @@ -1128,7 +1128,7 @@ LinearScanAllocator::canCoexist(LiveInterval *a, LiveInterval *b) void LinearScanAllocator::validateIntervals() { - if (!js_JitOptions.checkGraphConsistency) + if (!js_IonOptions.checkGraphConsistency) return; for (IntervalIterator i(active.begin()); i != active.end(); i++) { @@ -1174,7 +1174,7 @@ LinearScanAllocator::validateIntervals() void LinearScanAllocator::validateAllocations() { - if (!js_JitOptions.checkGraphConsistency) + if (!js_IonOptions.checkGraphConsistency) return; for (IntervalIterator i(handled.begin()); i != handled.end(); i++) { diff --git a/js/src/jit/LiveRangeAllocator.h b/js/src/jit/LiveRangeAllocator.h index db8feecd035e..a469ebda4aeb 100644 --- a/js/src/jit/LiveRangeAllocator.h +++ b/js/src/jit/LiveRangeAllocator.h @@ -600,7 +600,7 @@ class LiveRangeAllocator : protected RegisterAllocator void validateVirtualRegisters() { #ifdef DEBUG - if (!js_JitOptions.checkGraphConsistency) + if (!js_IonOptions.checkGraphConsistency) return; for (size_t i = 1; i < graph.numVirtualRegisters(); i++) { diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 7257a52951d6..3209d31927ff 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -523,7 +523,7 @@ LIRGenerator::visitAssertFloat32(MAssertFloat32 *assertion) if (!allowFloat32Optimizations()) return true; - if (type != MIRType_Value && !js_JitOptions.eagerCompilation) { + if (type != MIRType_Value && !js_IonOptions.eagerCompilation) { JS_ASSERT_IF(checkIsFloat32, type == MIRType_Float32); JS_ASSERT_IF(!checkIsFloat32, type != MIRType_Float32); } @@ -3421,15 +3421,6 @@ LIRGenerator::visitGetDOMMember(MGetDOMMember *ins) return defineBox(lir, ins); } -bool -LIRGenerator::visitRecompileCheck(MRecompileCheck *ins) -{ - LRecompileCheck *lir = new(alloc()) LRecompileCheck(temp()); - if (!add(lir, ins)) - return false; - return assignSafepoint(lir, ins); -} - static void SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint) { @@ -3550,7 +3541,7 @@ LIRGenerator::visitBlock(MBasicBlock *block) if (!definePhis()) return false; - if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) { + if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) { if (!add(new(alloc()) LLabel())) return false; } diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index c2a86f313d4f..8302b9f16d68 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -261,7 +261,6 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitSetDOMProperty(MSetDOMProperty *ins); bool visitGetDOMProperty(MGetDOMProperty *ins); bool visitGetDOMMember(MGetDOMMember *ins); - bool visitRecompileCheck(MRecompileCheck *ins); }; } // namespace jit diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index d5e4dde8fc8b..6292fc1ab510 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -24,9 +24,6 @@ namespace jit { IonBuilder::InliningStatus IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) { - if (!optimizationInfo().inlineNative()) - return InliningStatus_NotInlined; - // Array natives. if (native == js_Array) return inlineArray(callInfo); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index c35b7ab9b1ed..1cf9adfa4365 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9204,41 +9204,6 @@ class MHaveSameClass } }; -// Increase the usecount of the provided script upon execution and test if -// the usecount surpasses the threshold. Upon hit it will recompile the -// outermost script (i.e. not the inlined script). -class MRecompileCheck : public MNullaryInstruction -{ - JSScript *script_; - uint32_t recompileThreshold_; - - MRecompileCheck(JSScript *script, uint32_t recompileThreshold) - : script_(script), - recompileThreshold_(recompileThreshold) - { - setGuard(); - } - - public: - INSTRUCTION_HEADER(RecompileCheck); - - static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_, uint32_t useCount) { - return new(alloc) MRecompileCheck(script_, useCount); - } - - JSScript *script() const { - return script_; - } - - uint32_t recompileThreshold() const { - return recompileThreshold_; - } - - AliasSet getAliasSet() const { - return AliasSet::None(); - } -}; - class MAsmJSNeg : public MUnaryInstruction { MAsmJSNeg(MDefinition *op, MIRType type) diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index bce7c0c963e0..93fe32a795c5 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -29,13 +29,11 @@ namespace jit { class MBasicBlock; class MIRGraph; class MStart; -class OptimizationInfo; class MIRGenerator { public: - MIRGenerator(CompileCompartment *compartment, TempAllocator *alloc, MIRGraph *graph, - CompileInfo *info, const OptimizationInfo *optimizationInfo); + MIRGenerator(CompileCompartment *compartment, TempAllocator *alloc, MIRGraph *graph, CompileInfo *info); TempAllocator &alloc() { return *alloc_; @@ -52,9 +50,6 @@ class MIRGenerator CompileInfo &info() { return *info_; } - const OptimizationInfo &optimizationInfo() const { - return *optimizationInfo_; - } template T * allocate(size_t count = 1) { @@ -132,7 +127,6 @@ class MIRGenerator protected: CompileInfo *info_; - const OptimizationInfo *optimizationInfo_; TempAllocator *alloc_; JSFunction *fun_; uint32_t nslots_; diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 3965afec7f6d..ac4034f6a536 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -17,11 +17,9 @@ using namespace js; using namespace js::jit; MIRGenerator::MIRGenerator(CompileCompartment *compartment, - TempAllocator *alloc, MIRGraph *graph, CompileInfo *info, - const OptimizationInfo *optimizationInfo) + TempAllocator *alloc, MIRGraph *graph, CompileInfo *info) : compartment(compartment), info_(info), - optimizationInfo_(optimizationInfo), alloc_(alloc), graph_(graph), error_(false), diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 08b622ba6395..12cee893cefa 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -211,8 +211,7 @@ namespace jit { _(RestPar) \ _(ForkJoinSlice) \ _(GuardThreadLocalObject) \ - _(CheckInterruptPar) \ - _(RecompileCheck) + _(CheckInterruptPar) // Forward declarations of MIR types. #define FORWARD_DECLARE(op) class M##op; diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index a4d1e76bb6eb..446e0f159f6c 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -311,7 +311,6 @@ class ParallelSafetyVisitor : public MInstructionVisitor UNSAFE_OP(AsmJSParameter) UNSAFE_OP(AsmJSCall) UNSAFE_OP(AsmJSCheckOverRecursed) - DROP_OP(RecompileCheck) // It looks like this could easily be made safe: UNSAFE_OP(ConvertElementsToDoubles) diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 3db801399761..a60f4ca555a1 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -2040,7 +2040,7 @@ RangeAnalysis::analyze() bool RangeAnalysis::addRangeAssertions() { - if (!js_JitOptions.checkRangeAnalysis) + if (!js_IonOptions.checkRangeAnalysis) return true; // Check the computed range for this instruction, if the option is set. Note diff --git a/js/src/jit/UnreachableCodeElimination.cpp b/js/src/jit/UnreachableCodeElimination.cpp index dec3d1caf8a3..ea37feb5d541 100644 --- a/js/src/jit/UnreachableCodeElimination.cpp +++ b/js/src/jit/UnreachableCodeElimination.cpp @@ -82,8 +82,8 @@ UnreachableCodeElimination::removeUnmarkedBlocksAndCleanup() // Pass 5: It's important for optimizations to re-run GVN (and in // turn alias analysis) after UCE if we eliminated branches. - if (rerunAliasAnalysis_ && mir_->optimizationInfo().gvnEnabled()) { - ValueNumberer gvn(mir_, graph_, mir_->optimizationInfo().gvnKind() == GVN_Optimistic); + if (rerunAliasAnalysis_ && js_IonOptions.gvn) { + ValueNumberer gvn(mir_, graph_, js_IonOptions.gvnIsOptimistic); if (!gvn.clear() || !gvn.analyze()) return false; IonSpewPass("GVN-after-UCE"); diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 879c06987db8..bb6aecb62e36 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -603,7 +603,7 @@ CodeGeneratorShared::verifyOsiPointRegs(LSafepoint *safepoint) bool CodeGeneratorShared::shouldVerifyOsiPointRegs(LSafepoint *safepoint) { - if (!js_JitOptions.checkOsiPointRegisters) + if (!js_IonOptions.checkOsiPointRegisters) return false; if (gen->info().executionMode() != SequentialExecution) diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index fdce3c69c6f8..940d346cae9d 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -76,7 +76,7 @@ LIRGeneratorShared::defineFixed(LInstructionHelper<1, X, Y> *lir, MDefinition *m if (!define(lir, mir, def)) return false; - if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) { + if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) { if (!add(new(alloc()) LNop)) return false; } @@ -163,7 +163,7 @@ LIRGeneratorShared::defineReturn(LInstruction *lir, MDefinition *mir) if (!add(lir)) return false; - if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) { + if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) { if (!add(new(alloc()) LNop)) return false; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 2ad682aded64..38996d6f9ae1 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5980,23 +5980,20 @@ JS_PUBLIC_API(void) JS_SetGlobalJitCompilerOption(JSContext *cx, JSJitCompilerOption opt, uint32_t value) { #ifdef JS_ION + jit::IonOptions defaultValues; switch (opt) { case JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER: - if (value == uint32_t(-1)) { - jit::JitOptions defaultValues; + if (value == uint32_t(-1)) value = defaultValues.baselineUsesBeforeCompile; - } - jit::js_JitOptions.baselineUsesBeforeCompile = value; + jit::js_IonOptions.baselineUsesBeforeCompile = value; break; case JSJITCOMPILER_ION_USECOUNT_TRIGGER: - if (value == uint32_t(-1)) { - jit::js_JitOptions.resetUsesBeforeCompile(); - break; - } - jit::js_JitOptions.setUsesBeforeCompile(value); + if (value == uint32_t(-1)) + value = defaultValues.usesBeforeCompile; + jit::js_IonOptions.usesBeforeCompile = value; if (value == 0) - jit::js_JitOptions.setEagerCompilation(); + jit::js_IonOptions.setEagerCompilation(); break; case JSJITCOMPILER_ION_ENABLE: if (value == 1) { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 2f8616f056f2..544f3798767f 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -590,10 +590,8 @@ class JSScript : public js::gc::BarrieredCell uint32_t sourceEnd_; uint32_t useCount; /* Number of times the script has been called - * or has had backedges taken. When running in - * ion, also increased for any inlined scripts. - * Reset if the script's JIT code is forcibly - * discarded. */ + * or has had backedges taken. Reset if the + * script's JIT code is forcibly discarded. */ #ifdef DEBUG // Unique identifier within the compartment for this script, used for diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index ce332a9f0163..61cf70e8eca8 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -746,6 +746,7 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state) ionBuilder = state.ionWorklist.popCopy(); DebugOnly executionMode = ionBuilder->info().executionMode(); + JS_ASSERT(jit::GetIonScript(ionBuilder->script(), executionMode) == ION_COMPILING_SCRIPT); #if JS_TRACE_LOGGING AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::ION_BACKGROUND_COMPILER), diff --git a/js/src/moz.build b/js/src/moz.build index de817201df82..31769c7439b5 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -257,9 +257,7 @@ if CONFIG['ENABLE_ION']: 'jit/IonCaches.cpp', 'jit/IonFrames.cpp', 'jit/IonMacroAssembler.cpp', - 'jit/IonOptimizationLevels.cpp', 'jit/IonSpewer.cpp', - 'jit/JitOptions.cpp', 'jit/JSONSpewer.cpp', 'jit/LICM.cpp', 'jit/LinearScan.cpp', diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8386af16dc35..501bc02295ff 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5440,110 +5440,103 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op) } if (const char *str = op->getStringOption("ion-gvn")) { - if (strcmp(str, "off") == 0) { - jit::js_JitOptions.disableGvn = true; - } else if (strcmp(str, "pessimistic") == 0) { - jit::js_JitOptions.forceGvnKind = true; - jit::js_JitOptions.forcedGvnKind = jit::GVN_Pessimistic; - } else if (strcmp(str, "optimistic") == 0) { - jit::js_JitOptions.forceGvnKind = true; - jit::js_JitOptions.forcedGvnKind = jit::GVN_Optimistic; - } else { + if (strcmp(str, "off") == 0) + jit::js_IonOptions.gvn = false; + else if (strcmp(str, "pessimistic") == 0) + jit::js_IonOptions.gvnIsOptimistic = false; + else if (strcmp(str, "optimistic") == 0) + jit::js_IonOptions.gvnIsOptimistic = true; + else return OptionFailure("ion-gvn", str); - } } if (const char *str = op->getStringOption("ion-licm")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableLicm = false; + jit::js_IonOptions.licm = true; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableLicm = true; + jit::js_IonOptions.licm = false; else return OptionFailure("ion-licm", str); } if (const char *str = op->getStringOption("ion-edgecase-analysis")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableEdgeCaseAnalysis = false; + jit::js_IonOptions.edgeCaseAnalysis = true; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableEdgeCaseAnalysis = true; + jit::js_IonOptions.edgeCaseAnalysis = false; else return OptionFailure("ion-edgecase-analysis", str); } if (const char *str = op->getStringOption("ion-range-analysis")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableRangeAnalysis = false; + jit::js_IonOptions.rangeAnalysis = true; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableRangeAnalysis = true; + jit::js_IonOptions.rangeAnalysis = false; else return OptionFailure("ion-range-analysis", str); } if (op->getBoolOption("ion-check-range-analysis")) - jit::js_JitOptions.checkRangeAnalysis = true; + jit::js_IonOptions.checkRangeAnalysis = true; if (op->getBoolOption("ion-check-thread-safety")) - jit::js_JitOptions.checkThreadSafety = true; + jit::js_IonOptions.checkThreadSafety = true; if (const char *str = op->getStringOption("ion-inlining")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableInlining = false; + jit::js_IonOptions.inlining = true; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableInlining = true; + jit::js_IonOptions.inlining = false; else return OptionFailure("ion-inlining", str); } if (const char *str = op->getStringOption("ion-osr")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.osr = true; + jit::js_IonOptions.osr = true; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.osr = false; + jit::js_IonOptions.osr = false; else return OptionFailure("ion-osr", str); } if (const char *str = op->getStringOption("ion-limit-script-size")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.limitScriptSize = true; + jit::js_IonOptions.limitScriptSize = true; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.limitScriptSize = false; + jit::js_IonOptions.limitScriptSize = false; else return OptionFailure("ion-limit-script-size", str); } int32_t useCount = op->getIntOption("ion-uses-before-compile"); if (useCount >= 0) - jit::js_JitOptions.setUsesBeforeCompile(useCount); + jit::js_IonOptions.usesBeforeCompile = useCount; useCount = op->getIntOption("baseline-uses-before-compile"); if (useCount >= 0) - jit::js_JitOptions.baselineUsesBeforeCompile = useCount; + jit::js_IonOptions.baselineUsesBeforeCompile = useCount; if (op->getBoolOption("baseline-eager")) - jit::js_JitOptions.baselineUsesBeforeCompile = 0; + jit::js_IonOptions.baselineUsesBeforeCompile = 0; if (const char *str = op->getStringOption("ion-regalloc")) { - if (strcmp(str, "lsra") == 0) { - jit::js_JitOptions.forceRegisterAllocator = true; - jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_LSRA; - } else if (strcmp(str, "backtracking") == 0) { - jit::js_JitOptions.forceRegisterAllocator = true; - jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_Backtracking; - } else if (strcmp(str, "stupid") == 0) { - jit::js_JitOptions.forceRegisterAllocator = true; - jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_Stupid; - } else { + if (strcmp(str, "lsra") == 0) + jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_LSRA; + else if (strcmp(str, "backtracking") == 0) + jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_Backtracking; + else if (strcmp(str, "stupid") == 0) + jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_Stupid; + else return OptionFailure("ion-regalloc", str); - } } if (op->getBoolOption("ion-eager")) - jit::js_JitOptions.setEagerCompilation(); + jit::js_IonOptions.setEagerCompilation(); if (op->getBoolOption("ion-compile-try-catch")) - jit::js_JitOptions.compileTryCatch = true; + jit::js_IonOptions.compileTryCatch = true; #ifdef JS_THREADSAFE bool parallelCompilation = false; diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index 83be1584e784..1f2e41df78fe 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -2207,8 +2207,8 @@ js::ParallelTestsShouldPass(JSContext *cx) { return jit::IsIonEnabled(cx) && jit::IsBaselineEnabled(cx) && - !jit::js_JitOptions.eagerCompilation && - jit::js_JitOptions.baselineUsesBeforeCompile != 0 && + !jit::js_IonOptions.eagerCompilation && + jit::js_IonOptions.baselineUsesBeforeCompile != 0 && cx->runtime()->gcZeal() == 0; } From fd36ed6219a873c46cbb98c3f440e9cfaa51dfbc Mon Sep 17 00:00:00 2001 From: Monica Chew Date: Thu, 12 Dec 2013 11:20:11 -0800 Subject: [PATCH 031/459] Bug 837202: Add telemetry for application reputation check (r=paolo,yoric) --- .../downloads/ApplicationReputation.cpp | 53 ++++++++++++++++++- toolkit/components/telemetry/Histograms.json | 18 +++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/toolkit/components/downloads/ApplicationReputation.cpp b/toolkit/components/downloads/ApplicationReputation.cpp index d8fc7c633f29..9e3e5f2cbe47 100644 --- a/toolkit/components/downloads/ApplicationReputation.cpp +++ b/toolkit/components/downloads/ApplicationReputation.cpp @@ -21,6 +21,7 @@ #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "mozilla/Telemetry.h" #include "nsCOMPtr.h" #include "nsDebug.h" @@ -33,6 +34,7 @@ #include "nsXPCOMStrings.h" using mozilla::Preferences; +using mozilla::Telemetry::Accumulate; // Preferences that we need to initialize the query. We may need another // preference than browser.safebrowsing.malware.enabled, or simply use @@ -61,6 +63,27 @@ public: ~PendingLookup(); private: + /** + * Telemetry states. + */ + /** + * The download appeared on the allowlist, blocklist, or no list (and thus + * could trigger a remote query). + */ + enum { + ALLOW_LIST = 0, + BLOCK_LIST = 1, + NO_LIST = 2, + } LIST_TYPES; + /** + * Status of the remote response (valid or not). + */ + enum { + SERVER_RESPONSE_VALID = 0, + SERVER_RESPONSE_FAILED = 1, + SERVER_RESPONSE_INVALID = 2, + } SERVER_RESPONSE_TYPES; + nsCOMPtr mQuery; nsCOMPtr mCallback; /** @@ -105,6 +128,8 @@ PendingLookup::~PendingLookup() { nsresult PendingLookup::OnComplete(bool shouldBlock, nsresult rv) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK, + shouldBlock); nsresult res = mCallback->OnComplete(shouldBlock, rv); return res; } @@ -119,15 +144,18 @@ PendingLookup::HandleEvent(const nsACString& tables) { nsCString allow_list; Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allow_list); if (FindInReadable(tables, allow_list)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST); return OnComplete(false, NS_OK); } nsCString block_list; Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &block_list); if (FindInReadable(tables, block_list)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST); return OnComplete(true, NS_OK); } + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST); #if 0 nsresult rv = SendRemoteQuery(); if (NS_FAILED(rv)) { @@ -268,16 +296,32 @@ PendingLookup::OnStopRequestInternal(nsIRequest *aRequest, nsISupports *aContext, nsresult aResult, bool* aShouldBlock) { + if (NS_FAILED(aResult)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_FAILED); + return aResult; + } + *aShouldBlock = false; nsresult rv; nsCOMPtr channel = do_QueryInterface(aRequest, &rv); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_FAILED); + return rv; + } uint32_t status = 0; rv = channel->GetResponseStatus(&status); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_FAILED); + return rv; + } if (status != 200) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_FAILED); return NS_ERROR_NOT_AVAILABLE; } @@ -285,11 +329,15 @@ PendingLookup::OnStopRequestInternal(nsIRequest *aRequest, safe_browsing::ClientDownloadResponse response; if (!response.ParseFromString(buf)) { NS_WARNING("Could not parse protocol buffer"); + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_INVALID); return NS_ERROR_CANNOT_CONVERT_DATA; } // There are several more verdicts, but we only respect one for now and treat // everything else as SAFE. + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_VALID); if (response.verdict() == safe_browsing::ClientDownloadResponse::DANGEROUS) { *aShouldBlock = true; } @@ -335,6 +383,7 @@ ApplicationReputationService::QueryReputation( NS_ENSURE_ARG_POINTER(aQuery); NS_ENSURE_ARG_POINTER(aCallback); + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true); nsresult rv = QueryReputationInternal(aQuery, aCallback); if (NS_FAILED(rv)) { aCallback->OnComplete(false, rv); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index a8e5707d4a06..6a2cab5804b6 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -23,6 +23,24 @@ "extended_statistics_ok": true, "description": "time spent updating accessibility (ms)" }, + "APPLICATION_REPUTATION_SHOULD_BLOCK": { + "kind": "boolean", + "description": "Application reputation verdict (shouldBlock=false is OK)" + }, + "APPLICATION_REPUTATION_LOCAL": { + "kind": "enumerated", + "n_values": 3, + "description": "Application reputation local results (0=ALLOW, 1=BLOCK, 2=NONE)" + }, + "APPLICATION_REPUTATION_SERVER": { + "kind": "enumerated", + "n_values": 3, + "description": "Application reputation remote status (0 = OK, 1 = FAIL, 2 = INVALID)" + }, + "APPLICATION_REPUTATION_COUNT": { + "kind": "flag", + "description": "Application reputation query count (both local and remote)" + }, "BACKGROUNDFILESAVER_THREAD_COUNT": { "kind": "enumerated", "n_values": 21, From 1b008d2a72c165059fbeb3aced9e8ae19e64466e Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 12 Dec 2013 20:23:29 +0100 Subject: [PATCH 032/459] Bug 904918: Odin Float32 support; p=bbouvier,dougc r=luke,sstangl Authors: - Douglas Crosher (dougc) for the ARM backend support - Benjamin Bouvier (bbouvier) for everything else --- js/src/assembler/assembler/X86Assembler.h | 6 +- js/src/jit/AsmJS.cpp | 560 +++++++++++++++++----- js/src/jit/AsmJSLink.cpp | 22 +- js/src/jit/AsmJSModule.cpp | 22 + js/src/jit/AsmJSModule.h | 18 +- js/src/jit/Lowering.cpp | 6 +- js/src/jit/MIR.h | 6 +- js/src/jit/arm/Assembler-arm.cpp | 1 + js/src/jit/arm/CodeGenerator-arm.cpp | 46 +- js/src/jit/arm/CodeGenerator-arm.h | 13 +- js/src/jit/arm/Lowering-arm.cpp | 3 + js/src/jit/arm/MacroAssembler-arm.cpp | 5 +- js/src/jit/arm/MacroAssembler-arm.h | 8 +- js/src/jit/shared/Assembler-shared.h | 13 +- js/src/jit/shared/Lowering-x86-shared.cpp | 3 + js/src/jit/x64/Assembler-x64.cpp | 2 + js/src/jit/x64/CodeGenerator-x64.cpp | 21 +- js/src/jit/x86/Assembler-x86.h | 10 + js/src/jit/x86/CodeGenerator-x86.cpp | 138 +++--- js/src/jit/x86/CodeGenerator-x86.h | 8 +- js/src/jsmath.cpp | 15 +- js/src/jsmath.h | 3 + 22 files changed, 654 insertions(+), 275 deletions(-) diff --git a/js/src/assembler/assembler/X86Assembler.h b/js/src/assembler/assembler/X86Assembler.h index bd3f2e542db5..da9f01cbea14 100644 --- a/js/src/assembler/assembler/X86Assembler.h +++ b/js/src/assembler/assembler/X86Assembler.h @@ -270,7 +270,7 @@ private: OP_GROUP2_Ev1 = 0xD1, OP_GROUP2_EvCL = 0xD3, OP_FPU6 = 0xDD, - OP_FLD32 = 0xD9, + OP_FPU6_F32 = 0xD9, OP_CALL_rel32 = 0xE8, OP_JMP_rel32 = 0xE9, PRE_SSE_F2 = 0xF2, @@ -732,7 +732,7 @@ public: void fld32_m(int offset, RegisterID base) { spew("fld %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base)); - m_formatter.oneByteOp(OP_FLD32, FPU6_OP_FLD, base, offset); + m_formatter.oneByteOp(OP_FPU6_F32, FPU6_OP_FLD, base, offset); } void fisttp_m(int offset, RegisterID base) { @@ -747,7 +747,7 @@ public: void fstp32_m(int offset, RegisterID base) { spew("fstp32 %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base)); - m_formatter.oneByteOp(OP_FLD32, FPU6_OP_FSTP, base, offset); + m_formatter.oneByteOp(OP_FPU6_F32, FPU6_OP_FSTP, base, offset); } void negl_r(RegisterID dst) diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index f7e70f9c1109..01a05500dd00 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -368,7 +368,10 @@ class Type public: enum Which { Double, - Doublish, + MaybeDouble, + Float, + MaybeFloat, + Floatish, Fixnum, Int, Signed, @@ -407,8 +410,20 @@ class Type return which_ == Double; } - bool isDoublish() const { - return isDouble() || which_ == Doublish; + bool isMaybeDouble() const { + return isDouble() || which_ == MaybeDouble; + } + + bool isFloat() const { + return which_ == Float; + } + + bool isMaybeFloat() const { + return isFloat() || which_ == MaybeFloat; + } + + bool isFloatish() const { + return isMaybeFloat() || which_ == Floatish; } bool isVoid() const { @@ -420,14 +435,18 @@ class Type } bool isVarType() const { - return isInt() || isDouble(); + return isInt() || isDouble() || isFloat(); } MIRType toMIRType() const { switch (which_) { case Double: - case Doublish: + case MaybeDouble: return MIRType_Double; + case Float: + case Floatish: + case MaybeFloat: + return MIRType_Float32; case Fixnum: case Int: case Signed: @@ -442,14 +461,17 @@ class Type const char *toChars() const { switch (which_) { - case Double: return "double"; - case Doublish: return "doublish"; - case Fixnum: return "fixnum"; - case Int: return "int"; - case Signed: return "signed"; - case Unsigned: return "unsigned"; - case Intish: return "intish"; - case Void: return "void"; + case Double: return "double"; + case MaybeDouble: return "double?"; + case Float: return "float"; + case Floatish: return "floatish"; + case MaybeFloat: return "float?"; + case Fixnum: return "fixnum"; + case Int: return "int"; + case Signed: return "signed"; + case Unsigned: return "unsigned"; + case Intish: return "intish"; + case Void: return "void"; } MOZ_ASSUME_UNREACHABLE("Invalid Type"); } @@ -465,7 +487,8 @@ class RetType enum Which { Void = Type::Void, Signed = Type::Signed, - Double = Type::Double + Double = Type::Double, + Float = Type::Float }; private: @@ -478,6 +501,7 @@ class RetType switch (coercion) { case AsmJS_ToInt32: which_ = Signed; break; case AsmJS_ToNumber: which_ = Double; break; + case AsmJS_FRound: which_ = Float; break; } } Which which() const { @@ -490,6 +514,7 @@ class RetType switch (which_) { case Void: return AsmJSModule::Return_Void; case Signed: return AsmJSModule::Return_Int32; + case Float: // will be converted to a Double case Double: return AsmJSModule::Return_Double; } MOZ_ASSUME_UNREACHABLE("Unexpected return type"); @@ -499,6 +524,7 @@ class RetType case Void: return MIRType_None; case Signed: return MIRType_Int32; case Double: return MIRType_Double; + case Float: return MIRType_Float32; } MOZ_ASSUME_UNREACHABLE("Unexpected return type"); } @@ -534,7 +560,8 @@ class VarType public: enum Which { Int = Type::Int, - Double = Type::Double + Double = Type::Double, + Float = Type::Float }; private: @@ -549,6 +576,7 @@ class VarType switch (coercion) { case AsmJS_ToInt32: which_ = Int; break; case AsmJS_ToNumber: which_ = Double; break; + case AsmJS_FRound: which_ = Float; break; } } Which which() const { @@ -558,18 +586,40 @@ class VarType return Type::Which(which_); } MIRType toMIRType() const { - return which_ == Int ? MIRType_Int32 : MIRType_Double; + switch(which_) { + case Int: return MIRType_Int32; + case Double: return MIRType_Double; + case Float: return MIRType_Float32; + } + MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float"); + return MIRType_None; } AsmJSCoercion toCoercion() const { - return which_ == Int ? AsmJS_ToInt32 : AsmJS_ToNumber; + switch(which_) { + case Int: return AsmJS_ToInt32; + case Double: return AsmJS_ToNumber; + case Float: return AsmJS_FRound; + } + MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float"); + return AsmJS_ToInt32; } static VarType FromMIRType(MIRType type) { - JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double); - return type == MIRType_Int32 ? Int : Double; + JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32); + switch(type) { + case MIRType_Int32: return Int; + case MIRType_Float32: return Float; + case MIRType_Double: return Double; + default: MOZ_ASSUME_UNREACHABLE("FromMIRType MIR type not handled"); return Int; + } } static VarType FromCheckedType(Type type) { - JS_ASSERT(type.isInt() || type.isDoublish()); - return type.isDoublish() ? Double : Int; + JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish()); + if (type.isMaybeDouble()) + return Double; + else if (type.isFloatish()) + return Float; + else + return Int; } bool operator==(VarType rhs) const { return which_ == rhs.which_; } bool operator!=(VarType rhs) const { return which_ != rhs.which_; } @@ -584,6 +634,7 @@ operator<=(Type lhs, VarType rhs) switch (rhs.which()) { case VarType::Int: return lhs.isInt(); case VarType::Double: return lhs.isDouble(); + case VarType::Float: return lhs.isFloat(); } MOZ_ASSUME_UNREACHABLE("Unexpected rhs type"); } @@ -706,6 +757,8 @@ class NumLit Value v_; public: + NumLit() {} + NumLit(Which w, Value v) : which_(w), v_(v) {} @@ -792,6 +845,29 @@ ExtractNumericLiteral(ParseNode *pn) return NumLit(NumLit::NegativeInt, Int32Value(i64)); } +static bool +ExtractFRoundableLiteral(ParseNode *pn, double *value) +{ + if (!IsNumericLiteral(pn)) + return false; + + NumLit literal = ExtractNumericLiteral(pn); + switch (literal.which()) { + case NumLit::Double: + *value = literal.toDouble(); + return true; + case NumLit::Fixnum: + case NumLit::NegativeInt: + case NumLit::BigUnsigned: + literal = NumLit(NumLit::Double, DoubleValue(literal.toInt32())); + *value = literal.toDouble(); + return true; + case NumLit::OutOfRangeInt: + break; + } + return false; +} + static inline bool IsLiteralInt(ParseNode *pn, uint32_t *u32) { @@ -848,32 +924,9 @@ TypedArrayLoadType(ArrayBufferView::ViewType viewType) case ArrayBufferView::TYPE_UINT32: return Type::Intish; case ArrayBufferView::TYPE_FLOAT32: + return Type::MaybeFloat; case ArrayBufferView::TYPE_FLOAT64: - return Type::Doublish; - default:; - } - MOZ_ASSUME_UNREACHABLE("Unexpected array type"); -} - -enum ArrayStoreEnum { - ArrayStore_Intish, - ArrayStore_Doublish -}; - -static ArrayStoreEnum -TypedArrayStoreType(ArrayBufferView::ViewType viewType) -{ - switch (viewType) { - case ArrayBufferView::TYPE_INT8: - case ArrayBufferView::TYPE_INT16: - case ArrayBufferView::TYPE_INT32: - case ArrayBufferView::TYPE_UINT8: - case ArrayBufferView::TYPE_UINT16: - case ArrayBufferView::TYPE_UINT32: - return ArrayStore_Intish; - case ArrayBufferView::TYPE_FLOAT32: - case ArrayBufferView::TYPE_FLOAT64: - return ArrayStore_Doublish; + return Type::MaybeDouble; default:; } MOZ_ASSUME_UNREACHABLE("Unexpected array type"); @@ -1305,7 +1358,8 @@ class MOZ_STACK_CLASS ModuleCompiler !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) || !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) || !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) || - !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul)) + !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) || + !addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround)) { return false; } @@ -1441,7 +1495,7 @@ class MOZ_STACK_CLASS ModuleCompiler bool addGlobalVarInitConstant(PropertyName *varName, VarType type, const Value &v, bool isConst) { uint32_t index; - if (!module_->addGlobalVarInitConstant(v, &index)) + if (!module_->addGlobalVarInitConstant(v, type.toCoercion(), &index)) return false; Global *global = moduleLifo_.new_(Global::Variable); if (!global) @@ -1808,9 +1862,16 @@ class FunctionCompiler Local(VarType t, unsigned slot) : type(t), slot(slot) {} }; + struct TypedValue + { + VarType type; + Value value; + TypedValue(VarType t, const Value &v) : type(t), value(v) {} + }; + private: typedef HashMap LocalMap; - typedef js::Vector VarInitializerVector; + typedef js::Vector VarInitializerVector; typedef HashMap LabeledBlockMap; typedef HashMap UnlabeledBlockMap; typedef js::Vector NodeStack; @@ -1924,7 +1985,7 @@ class FunctionCompiler return failName(pn, "duplicate local name '%s' not allowed", name); if (!locals_.add(p, name, Local(type, locals_.count()))) return false; - return varInitializers_.append(init); + return varInitializers_.append(TypedValue(type, init)); } bool prepareToEmitMIR(const VarTypeVector &argTypes) @@ -1950,7 +2011,8 @@ class FunctionCompiler } unsigned firstLocalSlot = argTypes.length(); for (unsigned i = 0; i < varInitializers_.length(); i++) { - MConstant *ins = MConstant::New(alloc(), varInitializers_[i]); + MConstant *ins = MConstant::NewAsmJS(alloc(), varInitializers_[i].value, + varInitializers_[i].type.toMIRType()); curBlock_->add(ins); curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins); } @@ -2010,6 +2072,16 @@ class FunctionCompiler return constant; } + MDefinition *constantFloat(float f) + { + if (!curBlock_) + return NULL; + + MConstant *constant = MConstant::NewAsmJS(alloc(), DoubleValue(double(f)), MIRType_Float32); + curBlock_->add(constant); + return constant; + } + template MDefinition *unary(MDefinition *op) { @@ -2869,10 +2941,39 @@ CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseN return m.addGlobalVarInitConstant(varName, type, literal.value(), isConst); } +static bool +CheckFloat32Coercion(ModuleCompiler &m, ParseNode *callNode, ParseNode **coercedExpr, + const char* errorMessage) +{ + JS_ASSERT(callNode->isKind(PNK_CALL)); + + ParseNode *callee = CallCallee(callNode); + if (!callee->isKind(PNK_NAME)) + return m.fail(callee, errorMessage); + + PropertyName *calleeName = callee->name(); + + const ModuleCompiler::Global *global = m.lookupGlobal(calleeName); + if (!global || global->which() != ModuleCompiler::Global::MathBuiltin || + global->mathBuiltin() != AsmJSMathBuiltin_fround) + { + return m.fail(callee, errorMessage); + } + + unsigned numArgs = CallArgListLength(callNode); + if (numArgs != 1) + return m.failf(callee, "fround passed %u arguments, expected one", numArgs); + + if (coercedExpr) + *coercedExpr = CallArgList(callNode); + return true; +} + static bool CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion, ParseNode **coercedExpr = nullptr) { + static const char *errorMessage = "in coercion expression, the expression must be of the form +x, fround(x) or x|0"; switch (coercionNode->getKind()) { case PNK_BITOR: { ParseNode *rhs = BinaryRight(coercionNode); @@ -2895,21 +2996,20 @@ CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *c *coercedExpr = UnaryKid(coercionNode); return true; } + case PNK_CALL: { + *coercion = AsmJS_FRound; + return CheckFloat32Coercion(m, coercionNode, coercedExpr, errorMessage); + } default:; } - return m.fail(coercionNode, "in coercion expression, the expression must be of the form +x or x|0"); + return m.fail(coercionNode, errorMessage); } static bool -CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode, - bool isConst) +CheckGlobalVariableImportExpr(ModuleCompiler &m, PropertyName *varName, AsmJSCoercion coercion, + ParseNode *coercedExpr, bool isConst) { - AsmJSCoercion coercion; - ParseNode *coercedExpr; - if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr)) - return false; - if (!coercedExpr->isKind(PNK_DOT)) return m.failName(coercedExpr, "invalid import expression for global '%s'", varName); @@ -2925,6 +3025,35 @@ CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNod return m.addGlobalVarImport(varName, field, coercion, isConst); } +static bool +CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode, + bool isConst) +{ + AsmJSCoercion coercion; + ParseNode *coercedExpr; + if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr)) + return false; + return CheckGlobalVariableImportExpr(m, varName, coercion, coercedExpr, isConst); +} + +static bool +CheckGlobalVariableInitFloat32(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode, + bool isConst) +{ + ParseNode *arg = NULL; + if (!CheckFloat32Coercion(m, initNode, &arg, "call must be of the form fround(x)")) + return false; + + if (IsNumericLiteral(arg)) { + double value; + if (!ExtractFRoundableLiteral(arg, &value)) + return m.fail(arg, "float global initializer needs to be a double literal"); + return m.addGlobalVarInitConstant(varName, VarType::Float, DoubleValue(value), isConst); + } + + return CheckGlobalVariableImportExpr(m, varName, AsmJSCoercion::AsmJS_FRound, arg, isConst); +} + static bool CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr) { @@ -3027,6 +3156,9 @@ CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool isConst) if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS)) return CheckGlobalVariableInitImport(m, var->name(), initNode, isConst); + if (initNode->isKind(PNK_CALL)) + return CheckGlobalVariableInitFloat32(m, var->name(), initNode, isConst); + if (initNode->isKind(PNK_NEW)) return CheckNewArrayView(m, var->name(), initNode); @@ -3058,7 +3190,7 @@ static bool ArgFail(FunctionCompiler &f, PropertyName *argName, ParseNode *stmt) { return f.failName(stmt, "expecting argument type declaration for '%s' of the " - "form 'arg = arg|0' or 'arg = +arg'", argName); + "form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'", argName); } static bool @@ -3170,6 +3302,18 @@ CheckVariable(FunctionCompiler &f, ParseNode *var) if (!initNode) return f.failName(var, "var '%s' needs explicit type declaration via an initial value", name); + if (initNode->isKind(PNK_CALL)) { + ParseNode *coercedVar = NULL; + if (!CheckFloat32Coercion(f.m(), initNode, &coercedVar, "caller in var initializer can only be fround")) + return false; + + double value; + if (!ExtractFRoundableLiteral(coercedVar, &value)) + return f.failName(coercedVar, "float initializer for '%s' needs to be a double literal", name); + + return f.addVariable(var, name, VarType::Float, DoubleValue(value)); + } + if (!IsNumericLiteral(initNode)) return f.failName(initNode, "initializer for '%s' needs to be a numeric literal", name); @@ -3416,7 +3560,7 @@ CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType } static bool -CheckArrayLoad(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type) +CheckLoadArray(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type) { ArrayBufferView::ViewType viewType; MDefinition *pointerDef; @@ -3443,15 +3587,30 @@ CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) return false; - switch (TypedArrayStoreType(viewType)) { - case ArrayStore_Intish: + switch (viewType) { + case ArrayBufferView::TYPE_INT8: + case ArrayBufferView::TYPE_INT16: + case ArrayBufferView::TYPE_INT32: + case ArrayBufferView::TYPE_UINT8: + case ArrayBufferView::TYPE_UINT16: + case ArrayBufferView::TYPE_UINT32: if (!rhsType.isIntish()) return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars()); break; - case ArrayStore_Doublish: - if (!rhsType.isDoublish()) - return f.failf(lhs, "%s is not a subtype of doublish", rhsType.toChars()); + case ArrayBufferView::TYPE_FLOAT32: + if (rhsType.isMaybeDouble()) + rhsDef = f.unary(rhsDef); + else if (!rhsType.isFloatish()) + return f.failf(lhs, "%s is not a subtype of double? or floatish", rhsType.toChars()); break; + case ArrayBufferView::TYPE_FLOAT64: + if (rhsType.isFloat()) + rhsDef = f.unary(rhsDef); + else if (!rhsType.isMaybeDouble()) + return f.failf(lhs, "%s is not a subtype of float or double?", rhsType.toChars()); + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected view type"); } f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck); @@ -3564,7 +3723,7 @@ CheckMathAbs(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition return true; } - if (argType.isDoublish()) { + if (argType.isMaybeDouble()) { if (retType != RetType::Double) return f.failf(call, "return type is double, used as %s", retType.toType().toChars()); *def = f.unary(argDef, MIRType_Double); @@ -3572,7 +3731,15 @@ CheckMathAbs(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition return true; } - return f.failf(call, "%s is not a subtype of signed or doublish", argType.toChars()); + if (argType.isMaybeFloat()) { + if (retType != RetType::Float) + return f.failf(call, "return type is float, used as %s", retType.toType().toChars()); + *def = f.unary(argDef, MIRType_Float32); + *type = Type::Float; + return true; + } + + return f.failf(call, "%s is not a subtype of signed, float? or double?", argType.toChars()); } static bool @@ -3588,7 +3755,7 @@ CheckMathSqrt(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition if (!CheckExpr(f, arg, &argDef, &argType)) return false; - if (argType.isDoublish()) { + if (argType.isMaybeDouble()) { if (retType != RetType::Double) return f.failf(call, "return type is double, used as %s", retType.toType().toChars()); *def = f.unary(argDef, MIRType_Double); @@ -3596,7 +3763,15 @@ CheckMathSqrt(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition return true; } - return f.failf(call, "%s is not a subtype of doublish", argType.toChars()); + if (argType.isMaybeFloat()) { + if (retType != RetType::Float) + return f.failf(call, "return type is float, used as %s", retType.toType().toChars()); + *def = f.unary(argDef, MIRType_Float32); + *type = Type::Float; + return true; + } + + return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars()); } typedef bool (*CheckArgType)(FunctionCompiler &f, ParseNode *argNode, Type type); @@ -3672,7 +3847,7 @@ static bool CheckIsVarType(FunctionCompiler &f, ParseNode *argNode, Type type) { if (!type.isVarType()) - return f.failf(argNode, "%s is not a subtype of int or double", type.toChars()); + return f.failf(argNode, "%s is not a subtype of int, float or double", type.toChars()); return true; } @@ -3786,6 +3961,9 @@ CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, RetTyp { PropertyName *calleeName = CallCallee(callNode)->name(); + if (retType == RetType::Float) + return f.fail(callNode, "FFI calls can't return float"); + FunctionCompiler::Call call(f, retType); if (!CheckCallArgs(f, callNode, CheckIsExternType, &call)) return false; @@ -3801,11 +3979,88 @@ CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, RetTyp return true; } +static bool CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type); + static bool -CheckIsDoublish(FunctionCompiler &f, ParseNode *argNode, Type type) +CheckFRoundArg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type, const char* error) { - if (!type.isDoublish()) - return f.failf(argNode, "%s is not a subtype of doublish", type.toChars()); + ParseNode *arg = NULL; + if (!CheckFloat32Coercion(f.m(), expr, &arg, error)) + return false; + + if (arg->isKind(PNK_CALL)) + return CheckCall(f, arg, RetType::Float, def, type); + + if (IsNumericLiteral(arg)) { + double value; + if (!ExtractFRoundableLiteral(arg, &value)) + return f.fail(arg, "call to fround with literal expects the literal to be a double"); + + *def = f.constantFloat(value); + *type = Type::Float; + return true; + } + + MDefinition *inputDef; + Type inputType; + if (!CheckExpr(f, arg, &inputDef, &inputType)) + return false; + + if (inputType.isMaybeDouble() || inputType.isSigned()) + *def = f.unary(inputDef); + else if (inputType.isUnsigned()) + *def = f.unary(inputDef); + else if (inputType.isFloatish()) + *def = inputDef; + else + return f.failf(arg, "%s is not a subtype of signed, unsigned, double? or floatish", inputType.toChars()); + + *type = Type::Float; + return true; +} + +static bool +CheckFRound(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type) +{ + MDefinition *operand; + Type operandType; + if (!CheckFRoundArg(f, callNode, &operand, &operandType, "coercion to float should use fround")) + return false; + + switch (retType.which()) { + case RetType::Double: + *def = f.unary(operand); + *type = Type::Double; + return true; + case RetType::Signed: + *def = f.unary(operand); + *type = Type::Signed; + return true; + case RetType::Float: + *def = operand; + *type = Type::Float; + return true; + case RetType::Void: + // definition and return types should be ignored by the caller + return true; + } + + MOZ_ASSUME_UNREACHABLE("return value of fround is ignored"); +} + +static bool +CheckIsMaybeDouble(FunctionCompiler &f, ParseNode *argNode, Type type) +{ + if (!type.isMaybeDouble()) + return f.failf(argNode, "%s is not a subtype of double?", type.toChars()); + return true; +} + +static bool +CheckIsMaybeFloat(FunctionCompiler &f, ParseNode *argNode, Type type) +{ + if (!type.isMaybeFloat()) + return f.failf(argNode, "%s is not a subtype of float?", type.toChars()); return true; } @@ -3814,39 +4069,46 @@ CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltin RetType retType, MDefinition **def, Type *type) { unsigned arity = 0; - AsmJSImmKind callee; + AsmJSImmKind doubleCallee, floatCallee; switch (mathBuiltin) { - case AsmJSMathBuiltin_imul: return CheckMathIMul(f, callNode, retType, def, type); - case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, retType, def, type); - case AsmJSMathBuiltin_sqrt: return CheckMathSqrt(f, callNode, retType, def, type); - case AsmJSMathBuiltin_sin: arity = 1; callee = AsmJSImm_SinD; break; - case AsmJSMathBuiltin_cos: arity = 1; callee = AsmJSImm_CosD; break; - case AsmJSMathBuiltin_tan: arity = 1; callee = AsmJSImm_TanD; break; - case AsmJSMathBuiltin_asin: arity = 1; callee = AsmJSImm_ASinD; break; - case AsmJSMathBuiltin_acos: arity = 1; callee = AsmJSImm_ACosD; break; - case AsmJSMathBuiltin_atan: arity = 1; callee = AsmJSImm_ATanD; break; - case AsmJSMathBuiltin_ceil: arity = 1; callee = AsmJSImm_CeilD; break; - case AsmJSMathBuiltin_floor: arity = 1; callee = AsmJSImm_FloorD; break; - case AsmJSMathBuiltin_exp: arity = 1; callee = AsmJSImm_ExpD; break; - case AsmJSMathBuiltin_log: arity = 1; callee = AsmJSImm_LogD; break; - case AsmJSMathBuiltin_pow: arity = 2; callee = AsmJSImm_PowD; break; - case AsmJSMathBuiltin_atan2: arity = 2; callee = AsmJSImm_ATan2D; break; + case AsmJSMathBuiltin_imul: return CheckMathIMul(f, callNode, retType, def, type); + case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, retType, def, type); + case AsmJSMathBuiltin_sqrt: return CheckMathSqrt(f, callNode, retType, def, type); + case AsmJSMathBuiltin_fround: return CheckFRound(f, callNode, retType, def, type); + case AsmJSMathBuiltin_sin: arity = 1; doubleCallee = AsmJSImm_SinD; floatCallee = AsmJSImm_SinF; break; + case AsmJSMathBuiltin_cos: arity = 1; doubleCallee = AsmJSImm_CosD; floatCallee = AsmJSImm_CosF; break; + case AsmJSMathBuiltin_tan: arity = 1; doubleCallee = AsmJSImm_TanD; floatCallee = AsmJSImm_TanF; break; + case AsmJSMathBuiltin_asin: arity = 1; doubleCallee = AsmJSImm_ASinD; floatCallee = AsmJSImm_ASinF; break; + case AsmJSMathBuiltin_acos: arity = 1; doubleCallee = AsmJSImm_ACosD; floatCallee = AsmJSImm_ACosF; break; + case AsmJSMathBuiltin_atan: arity = 1; doubleCallee = AsmJSImm_ATanD; floatCallee = AsmJSImm_ATanF; break; + case AsmJSMathBuiltin_ceil: arity = 1; doubleCallee = AsmJSImm_CeilD; floatCallee = AsmJSImm_CeilF; break; + case AsmJSMathBuiltin_floor: arity = 1; doubleCallee = AsmJSImm_FloorD; floatCallee = AsmJSImm_FloorF; break; + case AsmJSMathBuiltin_exp: arity = 1; doubleCallee = AsmJSImm_ExpD; floatCallee = AsmJSImm_ExpF; break; + case AsmJSMathBuiltin_log: arity = 1; doubleCallee = AsmJSImm_LogD; floatCallee = AsmJSImm_LogF; break; + case AsmJSMathBuiltin_pow: arity = 2; doubleCallee = AsmJSImm_PowD; floatCallee = AsmJSImm_Invalid; break; + case AsmJSMathBuiltin_atan2: arity = 2; doubleCallee = AsmJSImm_ATan2D; floatCallee = AsmJSImm_Invalid; break; } + if (retType == RetType::Float && floatCallee == AsmJSImm_Invalid) + return f.fail(callNode, "math builtin cannot be used as float"); + if (retType != RetType::Double && retType != RetType::Float) + return f.failf(callNode, "return type of math function is double or float, used as %s", retType.toType().toChars()); + FunctionCompiler::Call call(f, retType); - if (!CheckCallArgs(f, callNode, CheckIsDoublish, &call)) + if (retType == RetType::Float && !CheckCallArgs(f, callNode, CheckIsMaybeFloat, &call)) + return false; + if (retType == RetType::Double && !CheckCallArgs(f, callNode, CheckIsMaybeDouble, &call)) return false; if (call.sig().args().length() != arity) return f.failf(callNode, "call passed %u arguments, expected %u", call.sig().args().length(), arity); - if (!f.builtinCall(callee, call, MIRType_Double, def)) + if (retType == RetType::Float && !f.builtinCall(floatCallee, call, retType.toMIRType(), def)) + return false; + if (retType == RetType::Double && !f.builtinCall(doubleCallee, call, retType.toMIRType(), def)) return false; - if (retType != RetType::Double) - return f.failf(callNode, "return type is double, used as %s", retType.toType().toChars()); - - *type = Type::Double; + *type = retType.toType(); return true; } @@ -3898,14 +4160,14 @@ CheckPos(FunctionCompiler &f, ParseNode *pos, MDefinition **def, Type *type) if (!CheckExpr(f, operand, &operandDef, &operandType)) return false; - if (operandType.isSigned()) + if (operandType.isMaybeFloat() || operandType.isSigned()) *def = f.unary(operandDef); else if (operandType.isUnsigned()) *def = f.unary(operandDef); - else if (operandType.isDoublish()) + else if (operandType.isMaybeDouble()) *def = operandDef; else - return f.failf(operand, "%s is not a subtype of signed, unsigned or doublish", operandType.toChars()); + return f.failf(operand, "%s is not a subtype of signed, unsigned, float or double?", operandType.toChars()); *type = Type::Double; return true; @@ -3947,13 +4209,19 @@ CheckNeg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) return true; } - if (operandType.isDoublish()) { + if (operandType.isMaybeDouble()) { *def = f.unary(operandDef, MIRType_Double); *type = Type::Double; return true; } - return f.failf(operand, "%s is not a subtype of int or doublish", operandType.toChars()); + if (operandType.isMaybeFloat()) { + *def = f.unary(operandDef, MIRType_Float32); + *type = Type::Floatish; + return true; + } + + return f.failf(operand, "%s is not a subtype of int, float? or double?", operandType.toChars()); } static bool @@ -3967,14 +4235,14 @@ CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type * if (!CheckExpr(f, operand, &operandDef, &operandType)) return false; - if (operandType.isDoublish()) { + if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) { *def = f.unary(operandDef); *type = Type::Signed; return true; } if (!operandType.isIntish()) - return f.failf(operand, "%s is not a subtype of doublish or intish", operandType.toChars()); + return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars()); *def = operandDef; *type = Type::Signed; @@ -4071,6 +4339,8 @@ CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Typ *type = Type::Int; } else if (thenType.isDouble() && elseType.isDouble()) { *type = Type::Double; + } else if (thenType.isFloat() && elseType.isFloat()) { + *type = Type::Float; } else { return f.failf(ternary, "then/else branches of conditional must both produce int or double, " "current types are %s and %s", thenType.toChars(), elseType.toChars()); @@ -4130,14 +4400,19 @@ CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *typ return true; } - if (!lhsType.isDoublish()) - return f.failf(lhs, "%s is not a subtype of doublish", lhsType.toChars()); - if (!rhsType.isDoublish()) - return f.failf(rhs, "%s is not a subtype of doublish", rhsType.toChars()); + if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) { + *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal); + *type = Type::Double; + return true; + } - *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal); - *type = Type::Double; - return true; + if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) { + *def = f.mul(lhsDef, rhsDef, MIRType_Float32, MMul::Normal); + *type = Type::Floatish; + return true; + } + + return f.fail(star, "multiply operands must be both int, both double? or both float?"); } static bool @@ -4185,13 +4460,18 @@ CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ ? f.binary(lhsDef, rhsDef, MIRType_Int32) : f.binary(lhsDef, rhsDef, MIRType_Int32); *type = Type::Intish; - } else if (lhsType.isDoublish() && rhsType.isDoublish()) { + } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) { *def = expr->isKind(PNK_ADD) ? f.binary(lhsDef, rhsDef, MIRType_Double) : f.binary(lhsDef, rhsDef, MIRType_Double); *type = Type::Double; + } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) { + *def = expr->isKind(PNK_ADD) + ? f.binary(lhsDef, rhsDef, MIRType_Float32) + : f.binary(lhsDef, rhsDef, MIRType_Float32); + *type = Type::Floatish; } else { - return f.failf(expr, "operands to +/- must both be int or doublish, got %s and %s", + return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s", lhsType.toChars(), rhsType.toChars()); } @@ -4214,7 +4494,7 @@ CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) return false; - if (lhsType.isDoublish() && rhsType.isDoublish()) { + if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) { *def = expr->isKind(PNK_DIV) ? f.div(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false) : f.mod(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false); @@ -4222,6 +4502,15 @@ CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ return true; } + if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) { + if (expr->isKind(PNK_DIV)) + *def = f.div(lhsDef, rhsDef, MIRType_Float32, /* unsignd = */ false); + else + return f.fail(expr, "modulo cannot receive float arguments"); + *type = Type::Floatish; + return true; + } + if (lhsType.isSigned() && rhsType.isSigned()) { if (expr->isKind(PNK_DIV)) *def = f.div(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ false); @@ -4240,7 +4529,7 @@ CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ return true; } - return f.failf(expr, "arguments to / or %% must both be double, signed, or unsigned; " + return f.failf(expr, "arguments to / or %% must both be double?, float?, signed, or unsigned; " "%s and %s are given", lhsType.toChars(), rhsType.toChars()); } @@ -4274,7 +4563,13 @@ CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *t return true; } - return f.failf(comp, "arguments to a comparison must both be signed, unsigned or doubles; " + if (lhsType.isFloat() && rhsType.isFloat()) { + *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Float32); + *type = Type::Int; + return true; + } + + return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; " "%s and %s are given", lhsType.toChars(), rhsType.toChars()); } @@ -4345,6 +4640,18 @@ CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *t return true; } +static bool +CheckUncoercedCall(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) +{ + static const char* callError = "all function calls must either be ignored (via " + "f(); or comma-expression), coerced to signed " + "(via f()|0), coerced to float (via fround(f()))" + " or coerced to double (via +f())"; + + JS_ASSERT(expr->isKind(PNK_CALL)); + return CheckFRoundArg(f, expr, def, type, callError); +} + static bool CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) { @@ -4358,7 +4665,7 @@ CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) switch (expr->getKind()) { case PNK_NAME: return CheckVarRef(f, expr, def, type); - case PNK_ELEM: return CheckArrayLoad(f, expr, def, type); + case PNK_ELEM: return CheckLoadArray(f, expr, def, type); case PNK_ASSIGN: return CheckAssign(f, expr, def, type); case PNK_POS: return CheckPos(f, expr, def, type); case PNK_NOT: return CheckNot(f, expr, def, type); @@ -4367,10 +4674,7 @@ CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) case PNK_COMMA: return CheckComma(f, expr, def, type); case PNK_CONDITIONAL: return CheckConditional(f, expr, def, type); case PNK_STAR: return CheckMultiply(f, expr, def, type); - - case PNK_CALL: return f.fail(expr, "all function calls must either be ignored (via " - "f(); or comma-expression), coerced to signed " - "(via f()|0) or coerced to double (via +f())"); + case PNK_CALL: return CheckUncoercedCall(f, expr, def, type); case PNK_ADD: case PNK_SUB: return CheckAddOrSub(f, expr, def, type); @@ -4792,6 +5096,8 @@ CheckReturn(FunctionCompiler &f, ParseNode *returnStmt) retType = RetType::Signed; else if (type.isDouble()) retType = RetType::Double; + else if (type.isFloat()) + retType = RetType::Float; else if (type.isVoid()) retType = RetType::Void; else @@ -5605,7 +5911,7 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu masm.load32(src, scratch); masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase())); } else { - JS_ASSERT(iter.mirType() == MIRType_Double); + JS_ASSERT(iter.mirType() == MIRType_Double || iter.mirType() == MIRType_Float32); masm.loadDouble(src, ScratchFloatReg); masm.storeDouble(ScratchFloatReg, Address(StackPointer, iter->offsetFromArgBase())); } @@ -5629,6 +5935,9 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu case RetType::Signed: masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0)); break; + case RetType::Float: + masm.convertFloatToDouble(ReturnFloatReg, ReturnFloatReg); + // Fall through as ReturnFloatReg now contains a Double case RetType::Double: masm.canonicalizeDouble(ReturnFloatReg); masm.storeDouble(ReturnFloatReg, Address(argv, 0)); @@ -5873,6 +6182,9 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); masm.loadDouble(argv, ReturnFloatReg); break; + case RetType::Float: + MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be returned from a FFI"); + break; } // Note: the caller is IonMonkey code which means there are no non-volatile @@ -5924,6 +6236,9 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); masm.loadDouble(argv, ReturnFloatReg); break; + case RetType::Float: + MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be returned from a FFI"); + break; } masm.freeStack(reserveSize + sizeof(int32_t)); @@ -6128,6 +6443,9 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit case RetType::Double: masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert); break; + case RetType::Float: + MOZ_ASSUME_UNREACHABLE("Float shouldn't be returned from a FFI"); + break; } masm.bind(&done); diff --git a/js/src/jit/AsmJSLink.cpp b/js/src/jit/AsmJSLink.cpp index 9a3755e75231..eed158104541 100644 --- a/js/src/jit/AsmJSLink.cpp +++ b/js/src/jit/AsmJSLink.cpp @@ -70,10 +70,17 @@ ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Gl switch (global.varInitKind()) { case AsmJSModule::Global::InitConstant: { const Value &v = global.varInitConstant(); - if (v.isInt32()) + switch (global.varInitCoercion()) { + case AsmJS_ToInt32: *(int32_t *)datum = v.toInt32(); - else + break; + case AsmJS_ToNumber: *(double *)datum = v.toDouble(); + break; + case AsmJS_FRound: + *(float *)datum = static_cast(v.toDouble()); + break; + } break; } case AsmJSModule::Global::InitImport: { @@ -82,7 +89,7 @@ ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Gl if (!GetDataProperty(cx, importVal, field, &v)) return false; - switch (global.varImportCoercion()) { + switch (global.varInitCoercion()) { case AsmJS_ToInt32: if (!ToInt32(cx, v, (int32_t *)datum)) return false; @@ -91,6 +98,10 @@ ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Gl if (!ToNumber(cx, v, (double *)datum)) return false; break; + case AsmJS_FRound: + if (!RoundFloat32(cx, v, (float *)datum)) + return false; + break; } break; } @@ -157,6 +168,7 @@ ValidateMathBuiltin(JSContext *cx, AsmJSModule::Global &global, HandleValue glob case AsmJSMathBuiltin_abs: native = js_math_abs; break; case AsmJSMathBuiltin_atan2: native = math_atan2; break; case AsmJSMathBuiltin_imul: native = math_imul; break; + case AsmJSMathBuiltin_fround: native = math_fround; break; } if (!IsNativeFunction(v, native)) @@ -364,6 +376,10 @@ CallAsmJS(JSContext *cx, unsigned argc, Value *vp) if (!ToNumber(cx, v, (double*)&coercedArgs[i])) return false; break; + case AsmJS_FRound: + if (!RoundFloat32(cx, v, (float *)&coercedArgs[i])) + return false; + break; } } diff --git a/js/src/jit/AsmJSModule.cpp b/js/src/jit/AsmJSModule.cpp index 83a9b2f63635..33fb1751ad23 100644 --- a/js/src/jit/AsmJSModule.cpp +++ b/js/src/jit/AsmJSModule.cpp @@ -235,28 +235,50 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext *cx) return FuncCast(NumberMod); case AsmJSImm_SinD: return FuncCast(sin); + case AsmJSImm_SinF: + return FuncCast(sinf); case AsmJSImm_CosD: return FuncCast(cos); + case AsmJSImm_CosF: + return FuncCast(cosf); case AsmJSImm_TanD: return FuncCast(tan); + case AsmJSImm_TanF: + return FuncCast(tanf); case AsmJSImm_ASinD: return FuncCast(asin); + case AsmJSImm_ASinF: + return FuncCast(asinf); case AsmJSImm_ACosD: return FuncCast(acos); + case AsmJSImm_ACosF: + return FuncCast(acosf); case AsmJSImm_ATanD: return FuncCast(atan); + case AsmJSImm_ATanF: + return FuncCast(atanf); case AsmJSImm_CeilD: return FuncCast(ceil); + case AsmJSImm_CeilF: + return FuncCast(ceilf); case AsmJSImm_FloorD: return FuncCast(floor); + case AsmJSImm_FloorF: + return FuncCast(floorf); case AsmJSImm_ExpD: return FuncCast(exp); + case AsmJSImm_ExpF: + return FuncCast(expf); case AsmJSImm_LogD: return FuncCast(log); + case AsmJSImm_LogF: + return FuncCast(logf); case AsmJSImm_PowD: return FuncCast(ecmaPow); case AsmJSImm_ATan2D: return FuncCast(ecmaAtan2); + case AsmJSImm_Invalid: + break; } MOZ_ASSUME_UNREACHABLE("Bad AsmJSImmKind"); diff --git a/js/src/jit/AsmJSModule.h b/js/src/jit/AsmJSModule.h index 91d2b815d6bb..ea1e6e99bbad 100644 --- a/js/src/jit/AsmJSModule.h +++ b/js/src/jit/AsmJSModule.h @@ -29,7 +29,8 @@ namespace js { enum AsmJSCoercion { AsmJS_ToInt32, - AsmJS_ToNumber + AsmJS_ToNumber, + AsmJS_FRound }; // The asm.js spec recognizes this set of builtin Math functions. @@ -39,7 +40,8 @@ enum AsmJSMathBuiltin AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan, AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp, AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt, - AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul + AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul, + AsmJSMathBuiltin_fround }; // Static-link data is used to patch a module either after it has been @@ -105,9 +107,9 @@ class AsmJSModule struct { uint32_t index_; VarInitKind initKind_; + AsmJSCoercion coercion_; union { Value constant_; // will only contain int32/double - AsmJSCoercion coercion_; } init; } var; uint32_t ffiIndex_; @@ -151,10 +153,9 @@ class AsmJSModule JS_ASSERT(pod.u.var.initKind_ == InitConstant); return pod.u.var.init.constant_; } - AsmJSCoercion varImportCoercion() const { + AsmJSCoercion varInitCoercion() const { JS_ASSERT(pod.which_ == Variable); - JS_ASSERT(pod.u.var.initKind_ == InitImport); - return pod.u.var.init.coercion_; + return pod.u.var.coercion_; } PropertyName *varImportField() const { JS_ASSERT(pod.which_ == Variable); @@ -455,13 +456,14 @@ class AsmJSModule return charsBegin_ + pod.charsLength_; } - bool addGlobalVarInitConstant(const Value &v, uint32_t *globalIndex) { + bool addGlobalVarInitConstant(const Value &v, AsmJSCoercion coercion, uint32_t *globalIndex) { JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0); if (pod.numGlobalVars_ == UINT32_MAX) return false; Global g(Global::Variable, nullptr); g.pod.u.var.initKind_ = Global::InitConstant; g.pod.u.var.init.constant_ = v; + g.pod.u.var.coercion_ = coercion; g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++; return globals_.append(g); } @@ -469,7 +471,7 @@ class AsmJSModule JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0); Global g(Global::Variable, name); g.pod.u.var.initKind_ = Global::InitImport; - g.pod.u.var.init.coercion_ = coercion; + g.pod.u.var.coercion_ = coercion; g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++; return globals_.append(g); } diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 3209d31927ff..92ceb8941332 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3300,7 +3300,7 @@ LIRGenerator::visitAsmJSParameter(MAsmJSParameter *ins) if (abi.argInRegister()) return defineFixed(new(alloc()) LAsmJSParameter, ins, LAllocation(abi.reg())); - JS_ASSERT(ins->type() == MIRType_Int32 || ins->type() == MIRType_Double); + JS_ASSERT(IsNumberType(ins->type())); LAllocation::Kind argKind = ins->type() == MIRType_Int32 ? LAllocation::INT_ARGUMENT : LAllocation::DOUBLE_ARGUMENT; @@ -3312,7 +3312,7 @@ LIRGenerator::visitAsmJSReturn(MAsmJSReturn *ins) { MDefinition *rval = ins->getOperand(0); LAsmJSReturn *lir = new(alloc()) LAsmJSReturn; - if (rval->type() == MIRType_Double) + if (IsFloatingPointType(rval->type())) lir->setOperand(0, useFixed(rval, ReturnFloatReg)); else if (rval->type() == MIRType_Int32) lir->setOperand(0, useFixed(rval, ReturnReg)); @@ -3330,7 +3330,7 @@ LIRGenerator::visitAsmJSVoidReturn(MAsmJSVoidReturn *ins) bool LIRGenerator::visitAsmJSPassStackArg(MAsmJSPassStackArg *ins) { - if (ins->arg()->type() == MIRType_Double) { + if (IsFloatingPointType(ins->arg()->type())) { JS_ASSERT(!ins->arg()->isEmittedAtUses()); return add(new(alloc()) LAsmJSPassStackArg(useRegisterAtStart(ins->arg())), ins); } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 1cf9adfa4365..23713460447c 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9241,7 +9241,9 @@ class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false) { setMovable(); - if (vt == ArrayBufferView::TYPE_FLOAT32 || vt == ArrayBufferView::TYPE_FLOAT64) + if (vt == ArrayBufferView::TYPE_FLOAT32) + setResultType(MIRType_Float32); + else if (vt == ArrayBufferView::TYPE_FLOAT64) setResultType(MIRType_Double); else setResultType(MIRType_Int32); @@ -9291,7 +9293,7 @@ class MAsmJSLoadGlobalVar : public MNullaryInstruction MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset, bool isConstant) : globalDataOffset_(globalDataOffset), isConstant_(isConstant) { - JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double); + JS_ASSERT(IsNumberType(type)); setResultType(type); setMovable(); } diff --git a/js/src/jit/arm/Assembler-arm.cpp b/js/src/jit/arm/Assembler-arm.cpp index a44225a13097..a149027d1159 100644 --- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -45,6 +45,7 @@ ABIArgGenerator::next(MIRType type) current_ = ABIArg(Register::FromCode(intRegIndex_)); intRegIndex_++; break; + case MIRType_Float32: case MIRType_Double: if (floatRegIndex_ == NumFloatArgRegs) { static const int align = sizeof(double) - 1; diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index 305791e39562..ae1984e96efc 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1969,12 +1969,10 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) JS_ASSERT(ptrImm >= 0); if (isFloat) { VFPRegister vd(ToFloatRegister(ins->output())); - if (size == 32) { + if (size == 32) masm.ma_vldr(Operand(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always); - masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always); - } else { + else masm.ma_vldr(Operand(HeapReg, ptrImm), vd, Assembler::Always); - } } else { masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm), ToRegister(ins->output()), Offset, Assembler::Always); @@ -1987,12 +1985,10 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) if (mir->skipBoundsCheck()) { if (isFloat) { VFPRegister vd(ToFloatRegister(ins->output())); - if (size == 32) { + if (size == 32) masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always); - masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always); - } else { + else masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Always); - } } else { masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, ToRegister(ins->output()), Offset, Assembler::Always); @@ -2003,12 +1999,12 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) BufferOffset bo = masm.ma_BoundsCheck(ptrReg); if (isFloat) { FloatRegister dst = ToFloatRegister(ins->output()); - masm.ma_vmov(NANReg, dst, Assembler::AboveOrEqual); VFPRegister vd(dst); if (size == 32) { + masm.convertDoubleToFloat(NANReg, dst, Assembler::AboveOrEqual); masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below); - masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Below); } else { + masm.ma_vmov(NANReg, dst, Assembler::AboveOrEqual); masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Below); } } else { @@ -2044,12 +2040,10 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) JS_ASSERT(ptrImm >= 0); if (isFloat) { VFPRegister vd(ToFloatRegister(ins->value())); - if (size == 32) { - masm.as_vcvt(VFPRegister(ScratchFloatReg).singleOverlay(), vd, false, Assembler::Always); - masm.ma_vstr(VFPRegister(ScratchFloatReg).singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always); - } else { + if (size == 32) + masm.ma_vstr(vd.singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always); + else masm.ma_vstr(vd, Operand(HeapReg, ptrImm), Assembler::Always); - } } else { masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm), ToRegister(ins->value()), Offset, Assembler::Always); @@ -2064,7 +2058,7 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) if (isFloat) { VFPRegister vd(ToFloatRegister(ins->value())); if (size == 32) - masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Always); + masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always); else masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Always); } else { @@ -2078,7 +2072,7 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) if (isFloat) { VFPRegister vd(ToFloatRegister(ins->value())); if (size == 32) - masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Below); + masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below); else masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Below); } else { @@ -2229,10 +2223,14 @@ CodeGeneratorARM::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins) { const MAsmJSLoadGlobalVar *mir = ins->mir(); unsigned addr = mir->globalDataOffset(); - if (mir->type() == MIRType_Int32) + if (mir->type() == MIRType_Int32) { masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output())); - else + } else if (mir->type() == MIRType_Float32) { + VFPRegister vd(ToFloatRegister(ins->output())); + masm.ma_vldr(Operand(GlobalReg, addr), vd.singleOverlay()); + } else { masm.ma_vldr(Operand(GlobalReg, addr), ToFloatRegister(ins->output())); + } return true; } @@ -2242,12 +2240,16 @@ CodeGeneratorARM::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins) const MAsmJSStoreGlobalVar *mir = ins->mir(); MIRType type = mir->value()->type(); - JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double); + JS_ASSERT(IsNumberType(type)); unsigned addr = mir->globalDataOffset(); - if (mir->value()->type() == MIRType_Int32) + if (mir->value()->type() == MIRType_Int32) { masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value())); - else + } else if (mir->value()->type() == MIRType_Float32) { + VFPRegister vd(ToFloatRegister(ins->value())); + masm.ma_vstr(vd.singleOverlay(), Operand(GlobalReg, addr)); + } else { masm.ma_vstr(ToFloatRegister(ins->value()), Operand(GlobalReg, addr)); + } return true; } diff --git a/js/src/jit/arm/CodeGenerator-arm.h b/js/src/jit/arm/CodeGenerator-arm.h index e9f431010169..a916315a485b 100644 --- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -176,10 +176,19 @@ class CodeGeneratorARM : public CodeGeneratorShared bool generateInvalidateEpilogue(); protected: void postAsmJSCall(LAsmJSCall *lir) { -#if !defined(JS_CPU_ARM_HARDFP) +#ifndef JS_CPU_ARM_HARDFP if (lir->mir()->callee().which() == MAsmJSCall::Callee::Builtin) { - if (lir->mir()->type() == MIRType_Double) + switch (lir->mir()->type()) { + case MIRType_Double: masm.ma_vxfer(r0, r1, d0); + break; + case MIRType_Float32: + masm.as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(), + Assembler::CoreToFloat); + break; + default: + break; + } } #endif } diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index fd206d8736cc..8f4ca0c78d19 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -412,6 +412,9 @@ LIRGeneratorARM::visitAsmJSNeg(MAsmJSNeg *ins) if (ins->type() == MIRType_Int32) return define(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins); + if(ins->type() == MIRType_Float32) + return define(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins); + JS_ASSERT(ins->type() == MIRType_Double); return define(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins); } diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 28af4f12de64..fe039c5ec771 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -74,9 +74,10 @@ MacroAssemblerARM::convertUInt32ToFloat32(const Register &src, const FloatRegist as_vcvt(VFPRegister(dest).singleOverlay(), dest.uintOverlay()); } -void MacroAssemblerARM::convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest) +void MacroAssemblerARM::convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest, + Condition c) { - as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src)); + as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src), false, c); } // there are two options for implementing emitTruncateDouble. diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index d6db34bccc3d..9fb423cb43b1 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -50,7 +50,8 @@ class MacroAssemblerARM : public Assembler void convertInt32ToDouble(const Address &src, FloatRegister dest); void convertUInt32ToFloat32(const Register &src, const FloatRegister &dest); void convertUInt32ToDouble(const Register &src, const FloatRegister &dest); - void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest); + void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest, + Condition c = Always); void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail); void convertDoubleToInt32(const FloatRegister &src, const Register &dest, Label *fail, bool negativeZeroCheck = true); @@ -1494,11 +1495,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM return as_cmp(bounded, Imm8(0)); } - void storeFloat(VFPRegister src, Register base, Register index, Condition cond) { - as_vcvt(VFPRegister(ScratchFloatReg).singleOverlay(), src, false, cond); - ma_vstr(VFPRegister(ScratchFloatReg).singleOverlay(), base, index, 0, cond); - - } void moveFloat(FloatRegister src, FloatRegister dest) { as_vmov(VFPRegister(src).singleOverlay(), VFPRegister(dest).singleOverlay()); } diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index b358b6d2667a..62123dee1cd0 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -691,17 +691,28 @@ enum AsmJSImmKind #endif AsmJSImm_ModD, AsmJSImm_SinD, + AsmJSImm_SinF, AsmJSImm_CosD, + AsmJSImm_CosF, AsmJSImm_TanD, + AsmJSImm_TanF, AsmJSImm_ASinD, + AsmJSImm_ASinF, AsmJSImm_ACosD, + AsmJSImm_ACosF, AsmJSImm_ATanD, + AsmJSImm_ATanF, AsmJSImm_CeilD, + AsmJSImm_CeilF, AsmJSImm_FloorD, + AsmJSImm_FloorF, AsmJSImm_ExpD, + AsmJSImm_ExpF, AsmJSImm_LogD, + AsmJSImm_LogF, AsmJSImm_PowD, - AsmJSImm_ATan2D + AsmJSImm_ATan2D, + AsmJSImm_Invalid }; // Pointer to be embedded as an immediate in asm.js code. diff --git a/js/src/jit/shared/Lowering-x86-shared.cpp b/js/src/jit/shared/Lowering-x86-shared.cpp index 4f30e4045cb9..8895dba562bc 100644 --- a/js/src/jit/shared/Lowering-x86-shared.cpp +++ b/js/src/jit/shared/Lowering-x86-shared.cpp @@ -227,6 +227,9 @@ LIRGeneratorX86Shared::visitAsmJSNeg(MAsmJSNeg *ins) if (ins->type() == MIRType_Int32) return defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins, 0); + if (ins->type() == MIRType_Float32) + return defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins, 0); + JS_ASSERT(ins->type() == MIRType_Double); return defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins, 0); } diff --git a/js/src/jit/x64/Assembler-x64.cpp b/js/src/jit/x64/Assembler-x64.cpp index eb80b1cd0fd2..992a2e497b9a 100644 --- a/js/src/jit/x64/Assembler-x64.cpp +++ b/js/src/jit/x64/Assembler-x64.cpp @@ -39,6 +39,7 @@ ABIArgGenerator::next(MIRType type) case MIRType_Pointer: current_ = ABIArg(IntArgRegs[regIndex_++]); break; + case MIRType_Float32: case MIRType_Double: current_ = ABIArg(FloatArgRegs[regIndex_++]); break; @@ -58,6 +59,7 @@ ABIArgGenerator::next(MIRType type) current_ = ABIArg(IntArgRegs[intRegIndex_++]); break; case MIRType_Double: + case MIRType_Float32: if (floatRegIndex_ == NumFloatArgRegs) { current_ = ABIArg(stackOffset_); stackOffset_ += sizeof(uint64_t); diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 7e8deea7a876..3ed262503409 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -409,15 +409,6 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) srcAddr = Operand(HeapReg, ToRegister(ptr), TimesOne); } - if (vt == ArrayBufferView::TYPE_FLOAT32) { - FloatRegister dest = ToFloatRegister(ins->output()); - uint32_t before = masm.size(); - masm.loadFloat(srcAddr, dest); - uint32_t after = masm.size(); - masm.cvtss2sd(dest, dest); - return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output()))); - } - uint32_t before = masm.size(); switch (vt) { case ArrayBufferView::TYPE_INT8: masm.movsbl(srcAddr, ToRegister(ins->output())); break; @@ -426,6 +417,7 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) case ArrayBufferView::TYPE_UINT16: masm.movzwl(srcAddr, ToRegister(ins->output())); break; case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32: masm.movl(srcAddr, ToRegister(ins->output())); break; + case ArrayBufferView::TYPE_FLOAT32: masm.loadFloat(srcAddr, ToFloatRegister(ins->output())); break; case ArrayBufferView::TYPE_FLOAT64: masm.loadDouble(srcAddr, ToFloatRegister(ins->output())); break; default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); } @@ -453,14 +445,6 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) dstAddr = Operand(HeapReg, ToRegister(ins->ptr()), TimesOne); } - if (vt == ArrayBufferView::TYPE_FLOAT32) { - masm.convertDoubleToFloat(ToFloatRegister(ins->value()), ScratchFloatReg); - uint32_t before = masm.size(); - masm.storeFloat(ScratchFloatReg, dstAddr); - uint32_t after = masm.size(); - return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after)); - } - uint32_t before = masm.size(); if (ins->value()->isConstant()) { switch (vt) { @@ -480,6 +464,7 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) case ArrayBufferView::TYPE_UINT16: masm.movw(ToRegister(ins->value()), dstAddr); break; case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32: masm.movl(ToRegister(ins->value()), dstAddr); break; + case ArrayBufferView::TYPE_FLOAT32: masm.storeFloat(ToFloatRegister(ins->value()), dstAddr); break; case ArrayBufferView::TYPE_FLOAT64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break; default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); } @@ -508,7 +493,7 @@ CodeGeneratorX64::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins) MAsmJSStoreGlobalVar *mir = ins->mir(); MIRType type = mir->value()->type(); - JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double); + JS_ASSERT(IsNumberType(type)); CodeOffsetLabel label; if (type == MIRType_Int32) diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 302f76bcd3b7..684ce02afa79 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -278,6 +278,16 @@ class Assembler : public AssemblerX86Shared } } + void fstp32(const Operand &src) { + switch (src.kind()) { + case Operand::MEM_REG_DISP: + masm.fstp32_m(src.disp(), src.base()); + break; + default: + MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); + } + } + void cmpl(const Register src, ImmWord ptr) { masm.cmpl_ir(ptr.value, src.code()); } diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index e7b7b755f56a..534518ff2eaa 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -410,16 +410,21 @@ CodeGeneratorX86::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir) class jit::OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase { AnyRegister dest_; + bool isFloat32Load_; public: - OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest) : dest_(dest) {} + OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load) + : dest_(dest), isFloat32Load_(isFloat32Load) + {} + const AnyRegister &dest() const { return dest_; } + bool isFloat32Load() const { return isFloat32Load_; } bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this); } }; template void -CodeGeneratorX86::loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, - const LDefinition *out) +CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, + const LDefinition *out) { switch (vt) { case ArrayBufferView::TYPE_INT8: masm.movsblWithPatch(srcAddr, ToRegister(out)); break; @@ -429,6 +434,7 @@ CodeGeneratorX86::loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, co case ArrayBufferView::TYPE_UINT16: masm.movzwlWithPatch(srcAddr, ToRegister(out)); break; case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(srcAddr, ToRegister(out)); break; + case ArrayBufferView::TYPE_FLOAT32: masm.movssWithPatch(srcAddr, ToFloatRegister(out)); break; case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break; default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); } @@ -436,19 +442,11 @@ CodeGeneratorX86::loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, co template bool -CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, - const LDefinition *out) +CodeGeneratorX86::loadAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, + const LDefinition *out) { - if (vt == ArrayBufferView::TYPE_FLOAT32) { - FloatRegister dest = ToFloatRegister(out); - uint32_t before = masm.size(); - masm.movssWithPatch(srcAddr, dest); - uint32_t after = masm.size(); - masm.cvtss2sd(dest, dest); - return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest))); - } uint32_t before = masm.size(); - loadNonFloat32ViewTypeElement(vt, srcAddr, out); + loadViewTypeElement(vt, srcAddr, out); uint32_t after = masm.size(); return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out))); } @@ -458,13 +456,15 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic { const MLoadTypedArrayElementStatic *mir = ins->mir(); ArrayBufferView::ViewType vt = mir->viewType(); + JS_ASSERT_IF(vt == ArrayBufferView::TYPE_FLOAT32, mir->type() == MIRType_Float32); Register ptr = ToRegister(ins->ptr()); const LDefinition *out = ins->output(); OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr; + bool isFloat32Load = (vt == ArrayBufferView::TYPE_FLOAT32); if (!mir->fallible()) { - ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out)); + ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load); if (!addOutOfLineCode(ool)) return false; } @@ -476,18 +476,11 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic return false; Address srcAddr(ptr, (int32_t) mir->base()); - if (vt == ArrayBufferView::TYPE_FLOAT32) { - JS_ASSERT(mir->type() == MIRType_Float32); - FloatRegister dest = ToFloatRegister(out); - masm.movssWithPatch(srcAddr, dest); - masm.canonicalizeFloat(dest); - if (ool) - masm.bind(ool->rejoin()); - return true; - } - loadNonFloat32ViewTypeElement(vt, srcAddr, out); + loadViewTypeElement(vt, srcAddr, out); if (vt == ArrayBufferView::TYPE_FLOAT64) masm.canonicalizeDouble(ToFloatRegister(out)); + if (vt == ArrayBufferView::TYPE_FLOAT32) + masm.canonicalizeFloat(ToFloatRegister(out)); if (ool) masm.bind(ool->rejoin()); return true; @@ -507,33 +500,25 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) // immediate in the instruction. This displacement will fixed up when the // base address is known during dynamic linking (AsmJSModule::initHeap). PatchedAbsoluteAddress srcAddr((void *) ptr->toConstant()->toInt32()); - return loadViewTypeElement(vt, srcAddr, out); + return loadAndNoteViewTypeElement(vt, srcAddr, out); } Register ptrReg = ToRegister(ptr); Address srcAddr(ptrReg, 0); if (mir->skipBoundsCheck()) - return loadViewTypeElement(vt, srcAddr, out); + return loadAndNoteViewTypeElement(vt, srcAddr, out); - OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out)); + bool isFloat32Load = vt == ArrayBufferView::TYPE_FLOAT32; + OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load); if (!addOutOfLineCode(ool)) return false; CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0)); masm.j(Assembler::AboveOrEqual, ool->entry()); - if (vt == ArrayBufferView::TYPE_FLOAT32) { - FloatRegister dest = ToFloatRegister(out); - uint32_t before = masm.size(); - masm.movssWithPatch(srcAddr, dest); - uint32_t after = masm.size(); - masm.cvtss2sd(dest, dest); - masm.bind(ool->rejoin()); - return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest), cmp.offset())); - } uint32_t before = masm.size(); - loadNonFloat32ViewTypeElement(vt, srcAddr, out); + loadViewTypeElement(vt, srcAddr, out); uint32_t after = masm.size(); masm.bind(ool->rejoin()); return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset())); @@ -543,7 +528,10 @@ bool CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool) { if (ool->dest().isFloat()) { - masm.loadConstantDouble(GenericNaN(), ool->dest().fpu()); + if (ool->isFloat32Load()) + masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu()); + else + masm.loadConstantDouble(GenericNaN(), ool->dest().fpu()); } else { Register destReg = ool->dest().gpr(); masm.mov(ImmWord(0), destReg); @@ -554,8 +542,8 @@ CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArra template void -CodeGeneratorX86::storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, - const T &dstAddr) +CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, + const T &dstAddr) { switch (vt) { case ArrayBufferView::TYPE_INT8: @@ -565,6 +553,7 @@ CodeGeneratorX86::storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, c case ArrayBufferView::TYPE_UINT16: masm.movwWithPatch(ToRegister(value), dstAddr); break; case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(ToRegister(value), dstAddr); break; + case ArrayBufferView::TYPE_FLOAT32: masm.movssWithPatch(ToFloatRegister(value), dstAddr); break; case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break; default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); } @@ -572,18 +561,11 @@ CodeGeneratorX86::storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, c template bool -CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, - const T &dstAddr) +CodeGeneratorX86::storeAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, + const T &dstAddr) { - if (vt == ArrayBufferView::TYPE_FLOAT32) { - uint32_t before = masm.size(); - masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg); - masm.movssWithPatch(ScratchFloatReg, dstAddr); - uint32_t after = masm.size(); - return gen->noteHeapAccess(AsmJSHeapAccess(before, after)); - } uint32_t before = masm.size(); - storeNonFloat32ViewTypeElement(vt, value, dstAddr); + storeViewTypeElement(vt, value, dstAddr); uint32_t after = masm.size(); return gen->noteHeapAccess(AsmJSHeapAccess(before, after)); } @@ -602,13 +584,7 @@ CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStati masm.j(Assembler::AboveOrEqual, &rejoin); Address dstAddr(ptr, (int32_t) mir->base()); - if (vt == ArrayBufferView::TYPE_FLOAT32) { - JS_ASSERT(mir->value()->type() == MIRType_Float32); - masm.movssWithPatch(ToFloatRegister(value), dstAddr); - masm.bind(&rejoin); - return true; - } - storeNonFloat32ViewTypeElement(vt, value, dstAddr); + storeViewTypeElement(vt, value, dstAddr); masm.bind(&rejoin); return true; } @@ -627,29 +603,21 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) // immediate in the instruction. This displacement will fixed up when the // base address is known during dynamic linking (AsmJSModule::initHeap). PatchedAbsoluteAddress dstAddr((void *) ptr->toConstant()->toInt32()); - return storeViewTypeElement(vt, value, dstAddr); + return storeAndNoteViewTypeElement(vt, value, dstAddr); } Register ptrReg = ToRegister(ptr); Address dstAddr(ptrReg, 0); if (mir->skipBoundsCheck()) - return storeViewTypeElement(vt, value, dstAddr); + return storeAndNoteViewTypeElement(vt, value, dstAddr); CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0)); Label rejoin; masm.j(Assembler::AboveOrEqual, &rejoin); - if (vt == ArrayBufferView::TYPE_FLOAT32) { - masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg); - uint32_t before = masm.size(); - masm.movssWithPatch(ScratchFloatReg, dstAddr); - uint32_t after = masm.size(); - masm.bind(&rejoin); - return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset())); - } uint32_t before = masm.size(); - storeNonFloat32ViewTypeElement(vt, value, dstAddr); + storeViewTypeElement(vt, value, dstAddr); uint32_t after = masm.size(); masm.bind(&rejoin); return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset())); @@ -659,10 +627,14 @@ bool CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins) { MAsmJSLoadGlobalVar *mir = ins->mir(); + MIRType type = mir->type(); + JS_ASSERT(IsNumberType(type)); CodeOffsetLabel label; - if (mir->type() == MIRType_Int32) + if (type == MIRType_Int32) label = masm.movlWithPatch(PatchedAbsoluteAddress(), ToRegister(ins->output())); + else if (type == MIRType_Float32) + label = masm.movssWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output())); else label = masm.movsdWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output())); @@ -675,11 +647,13 @@ CodeGeneratorX86::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins) MAsmJSStoreGlobalVar *mir = ins->mir(); MIRType type = mir->value()->type(); - JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double); + JS_ASSERT(IsNumberType(type)); CodeOffsetLabel label; if (type == MIRType_Int32) label = masm.movlWithPatch(ToRegister(ins->value()), PatchedAbsoluteAddress()); + else if (type == MIRType_Float32) + label = masm.movssWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress()); else label = masm.movsdWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress()); @@ -713,13 +687,18 @@ void CodeGeneratorX86::postAsmJSCall(LAsmJSCall *lir) { MAsmJSCall *mir = lir->mir(); - if (mir->type() != MIRType_Double || mir->callee().which() != MAsmJSCall::Callee::Builtin) + if (!IsFloatingPointType(mir->type()) || mir->callee().which() != MAsmJSCall::Callee::Builtin) return; - masm.reserveStack(sizeof(double)); - masm.fstp(Operand(esp, 0)); - masm.loadDouble(Operand(esp, 0), ReturnFloatReg); - masm.freeStack(sizeof(double)); + if (mir->type() == MIRType_Float32) { + Operand op(esp, -sizeof(float)); + masm.fstp32(op); + masm.loadFloat(op, ReturnFloatReg); + } else { + Operand op(esp, -sizeof(double)); + masm.fstp(op); + masm.loadDouble(op, ReturnFloatReg); + } } void @@ -1013,7 +992,12 @@ CodeGeneratorX86::visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool) masm.setupUnalignedABICall(1, output); masm.cvtss2sd(input, input); masm.passABIArg(input); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32)); + + if (gen->compilingAsmJS()) + masm.callWithABI(AsmJSImm_ToInt32); + else + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32)); + masm.storeCallResult(output); masm.pop(input); diff --git a/js/src/jit/x86/CodeGenerator-x86.h b/js/src/jit/x86/CodeGenerator-x86.h index 6b6a7ac1410a..daf82715b922 100644 --- a/js/src/jit/x86/CodeGenerator-x86.h +++ b/js/src/jit/x86/CodeGenerator-x86.h @@ -30,16 +30,16 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared ValueOperand ToTempValue(LInstruction *ins, size_t pos); template - bool loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, + bool loadAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, const LDefinition *out); template - void loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, + void loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, const LDefinition *out); template - bool storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, + bool storeAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, const T &dstAddr); template - void storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, + void storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, const T &dstAddr); void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType, const Register &elements, const LAllocation *index); diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 718cd2acd15f..10169094b64b 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -460,6 +460,16 @@ js::math_imul(JSContext *cx, unsigned argc, Value *vp) return true; } +// Implements Math.fround (20.2.2.16) up to step 3 +bool +js::RoundFloat32(JSContext *cx, Handle v, float *out) +{ + double d; + bool success = ToNumber(cx, v, &d); + *out = static_cast(d); + return success; +} + bool js::math_fround(JSContext *cx, unsigned argc, Value *vp) { @@ -470,11 +480,10 @@ js::math_fround(JSContext *cx, unsigned argc, Value *vp) return true; } - double x; - if (!ToNumber(cx, args[0], &x)) + float f; + if (!RoundFloat32(cx, args[0], &f)) return false; - float f = x; args.rval().setDouble(static_cast(f)); return true; } diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 717f46019b5c..28fb924f0031 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -84,6 +84,9 @@ namespace js { extern bool math_imul(JSContext *cx, unsigned argc, js::Value *vp); +extern bool +RoundFloat32(JSContext *cx, Handle v, float *out); + extern bool math_fround(JSContext *cx, unsigned argc, js::Value *vp); From 0199c03803f0576674775919d4f19f281318e7a7 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 12 Dec 2013 20:23:35 +0100 Subject: [PATCH 033/459] Bug 904918: Odin Float32 support: tests; r=luke --- js/src/jit-test/lib/asm.js | 4 +- js/src/jit-test/tests/asm.js/testFloat32.js | 301 ++++++++++++++++++ .../jit-test/tests/asm.js/testHeapAccess.js | 1 + js/src/jit-test/tests/asm.js/testZOOB.js | 17 +- 4 files changed, 316 insertions(+), 7 deletions(-) create mode 100644 js/src/jit-test/tests/asm.js/testFloat32.js diff --git a/js/src/jit-test/lib/asm.js b/js/src/jit-test/lib/asm.js index d5f22ae9aaa7..8d98b7d3cdcc 100644 --- a/js/src/jit-test/lib/asm.js +++ b/js/src/jit-test/lib/asm.js @@ -57,7 +57,7 @@ function assertAsmDirectiveFail(str) eval(str); } catch (e) { if ((''+e).indexOf(ASM_DIRECTIVE_FAIL_STRING) == -1) - throw new Error("Didn't catch the expected directive failure error; instead caught: " + e); + throw new Error("Didn't catch the expected directive failure error; instead caught: " + e + "\nStack: " + new Error().stack); caught = true; } if (!caught) @@ -85,7 +85,7 @@ function assertAsmTypeFail() Function.apply(null, arguments); } catch (e) { if ((''+e).indexOf(ASM_TYPE_FAIL_STRING) == -1) - throw new Error("Didn't catch the expected type failure error; instead caught: " + e); + throw new Error("Didn't catch the expected type failure error; instead caught: " + e + "\nStack: " + new Error().stack); caught = true; } if (!caught) diff --git a/js/src/jit-test/tests/asm.js/testFloat32.js b/js/src/jit-test/tests/asm.js/testFloat32.js new file mode 100644 index 000000000000..4b78b4c9e5c0 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testFloat32.js @@ -0,0 +1,301 @@ +load(libdir + "asm.js"); +const TO_FLOAT32 = "var toF = glob.Math.fround;"; +const HEAP32 = "var f32 = new glob.Float32Array(heap);"; +var heap = new ArrayBuffer(4096); + +// Module linking +assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() {} return f"), null); +assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() {} return f"), {fround: Math.fround}); +assertAsmLinkFail(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() {} return f"), {Math: {fround: Math.imul}}); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() {} return f"), {Math:{fround: Math.fround}})(), undefined); + +// Argument coercions +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = unknown(x); } return f"); +assertAsmTypeFail('glob', USE_ASM + "function f(i) { i = toF(i); } return f"); +assertAsmTypeFail('glob', USE_ASM + "var cos = glob.Math.cos; function f(x) { x = cos(x); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x, x); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF('hi'); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(loat); } return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(i) { i = toF(i); } return f"), this)(), undefined); + +// Local variables declarations +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = unknown(); } return f"); +assertAsmTypeFail('glob', USE_ASM + "var cos = glob.Math.cos; function f() { var i = cos(); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(x, x); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF('hi'); } return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5); } return f"), this)(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); } return f"), this)(), undefined); + +// Return values +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(4, 4); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF({}); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(x); } return f"); + +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(42); } return f"), this)(), 42); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(0.); } return f"), this)(), 0); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(-0.); } return f"), this)(), -0); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var inf = glob.Infinity; function f() { return toF(inf); } return f"), this)(), Infinity); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(13.37); } return f"), this)(), Math.fround(13.37)); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return +toF(4.); } return f"), this)(), 4); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return +~~toF(4.5); } return f"), this)(), 4); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(4.5) | 0; } return f"), this)(), 4); + +// Assign values +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); i = 5; return toF(i); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); i = 6.; return toF(i); } return f"); + +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); return toF(i); } return f"), this)(), 5); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); i = toF(42); return toF(i); } return f"), this)(), 42); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); i = toF(6.); return toF(i); } return f"), this)(), 6); + +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { var i = toF(5.); f32[0] = toF(6.); i = f32[0]; return toF(i); } return f"); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { var i = toF(5.); f32[0] = toF(6.); i = toF(f32[0]); return toF(i); } return f"), this, null, heap)(), 6); + +// Special array assignments (the other ones are tested in testHeapAccess) +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { var i = 5.; f32[0] = i; return toF(f32[0]); } return f"), this, null, heap)(), 5); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "var f64 = new glob.Float64Array(heap); function f() { var i = toF(5.); f64[0] = i; return +f64[0]; } return f"), this, null, heap)(), 5); + +var HEAP64 = "var f64 = new glob.Float64Array(heap);" +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 1.5; return +f32[0]; } return f"), this, null, heap)(), 1.5); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + HEAP64 + "function f() { f64[0] = 1.5; return toF(f64[0]); } return f"), this, null, heap)(), 1.5); + +// Coercions +// -> from Float32 +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); var n = 0; n = i | 0; return n | 0; } return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); var n = 0.; n = +x; return +n; } return f"), this)(16.64), Math.fround(16.64)); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); var n = 0; n = ~~x; return n | 0; } return f"), this)(16.64), 16); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); var n = 0; n = ~~x >>> 0; return n | 0; } return f"), this)(16.64), 16); + +// -> from float? +function makeCoercion(coercionFunc) { + return USE_ASM + HEAP32 + TO_FLOAT32 + "function f(x) { x = toF(x); f32[0] = x; return " + coercionFunc('f32[0]') + " } return f"; +} +assertAsmTypeFail('glob', 'ffi', 'heap', makeCoercion(x => x + '|0')); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', makeCoercion(x => '+' + x)), this, null, heap)(16.64), Math.fround(16.64)); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', makeCoercion(x => 'toF(' + x + ')')), this, null, heap)(16.64), Math.fround(16.64)); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', makeCoercion(x => '~~+' + x + '|0')), this, null, heap)(16.64), 16); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', makeCoercion(x => '~~toF(' + x + ')|0')), this, null, heap)(16.64), 16); + +// -> to Float32 +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = x|0; return toF(~~x); } return f"), this)(23), 23); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = x|0; return toF(x >> 0); } return f"), this)(23), 23); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = +x; return toF(x); } return f"), this)(13.37), Math.fround(13.37)); + +UINT32_MAX = Math.pow(2, 32)-1; +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = x|0; return toF(x >>> 0); } return f"), this)(-1), Math.fround(UINT32_MAX)); + +// Global variables imports +assertAsmTypeFail('glob', USE_ASM + "var x = toF(); function f() {} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = some(3); function f() {} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = toF(); function f() {} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = toF(3, 4); function f() {} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = toF({x: 3}); function f() {} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = toF(true); function f() {} return f"); +assertAsmTypeFail('glob', USE_ASM + "var x = toF(3);" + TO_FLOAT32 + "function f() {} return f"); + +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var x = toF(3.5); function f() {} return f"), this)(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var x = toF(3); function f() {} return f"), this)(), undefined); +assertEq(asmLink(asmCompile('glob', 'ffi', USE_ASM + TO_FLOAT32 + "var x = toF(ffi.x); function f() {} return f"), this, {x:3})(), undefined); +assertEq(asmLink(asmCompile('glob', 'ffi', USE_ASM + TO_FLOAT32 + "var x = toF(ffi.x); function f() {} return f"), this, {x:3.5})(), undefined); + +// Global variables uses +values = [2.01, 13.37, -3.141592653] +specials = [NaN, Infinity] + +for (v of values) { + assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var x = toF(" + v + "); function f() {return toF(x);} return f"), this)(), Math.fround(v)); + assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var x = toF(0.); function f() {x = toF(" + v + "); return toF(x);} return f"), this)(), Math.fround(v)); + assertEq(asmLink(asmCompile('glob', 'ffi', USE_ASM + TO_FLOAT32 + "var x = toF(ffi.x); function f() {return toF(x);} return f"), this, {x:v})(), Math.fround(v)); +} + +for (v of specials) { + assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var special = glob." + v + "; var g=toF(0.); function f() {g=toF(special); return toF(g);} return f"), this)(), Math.fround(v)); +} + +// Math builtins +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var imul = glob.Math.imul; function f() {var x = toF(1.5), y = toF(2.4); return imul(x, y) | 0;} return f"); + +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var abs = glob.Math.abs; function f() {var x = toF(1.5); return +abs(x);} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var abs = glob.Math.abs; function f() {var x = toF(1.5); return abs(x) | 0;} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var abs = glob.Math.abs; function f() {var x = toF(1.5); return toF(abs(x))} return f"), this)(), Math.fround(1.5)); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var abs = glob.Math.abs; function f() {var x = toF(-1.5); return toF(abs(x))} return f"), this)(), Math.fround(1.5)); + +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f() {var x = toF(1.5); return +sqrt(x);} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f() {var x = toF(1.5); return sqrt(x) | 0;} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f() {var x = toF(2.25); return toF(sqrt(x))} return f"), this)(), Math.fround(1.5)); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f() {var x = toF(-1.); return toF(sqrt(x))} return f"), this)(), NaN); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; var inf = glob.Infinity; function f() {var x = toF(0.); x = toF(inf); return toF(sqrt(x))} return f"), this)(), Infinity); + +// float?s +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + HEAP32 + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f(x) { x = toF(x); f32[0] = x; return toF(sqrt(f32[0])) } return f"), this, null, heap)(64), Math.fround(8)); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + HEAP32 + TO_FLOAT32 + "var cos = glob.Math.cos; function f(x) { x = toF(x); f32[0] = x; return toF(cos(f32[0])) } return f"), this, null, heap)(3.141592653), -1); + +// All Math functions with arity 1 behave like cos and sin +var cosModule = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = toF(x); return toF(g(x))} return f"), this); +for (v of [0, 3.141592653, Math.Infinity, NaN]) + assertEq(cosModule(v), Math.fround(Math.cos(Math.fround(v)))); + +var sinModule = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.sin; function f(x) {x = toF(x); return toF(g(x))} return f"), this); +for (v of [0, 3.141592653, Math.Infinity, NaN]) + assertEq(sinModule(v), Math.fround(Math.sin(Math.fround(v)))); + +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = toF(x); return +(g(x))} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = +x; return toF(g(x))} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = x|0; return toF(g(x))} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = toF(x); return g(x) | 0} return f"); + +// Math functions with arity of two are not specialized for floats, so we shouldn't feed them with floats arguments or +// return type +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return toF(g(x, 2.))} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return +g(x, 2.)} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return toF(g(+x, 2.))} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return +g(+x, 2.)} return f"), this)(3), 9); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return toF(+g(+x, 2.))} return f"), this)(3), 9); + +// Other function calls +// -> Signature comparisons +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x); return toF(x);} function f() {var x=toF(4.); var y=toF(0.); var z = 0.; y=toF(g(x)); z = +g(x); return toF(z); } return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var sqrt=glob.Math.sqrt; function g(x){x=toF(x); return toF(sqrt(x));} function f() {var x=toF(4.); var y=toF(0.); var z = 0.; y = toF(g(x)); z = +toF(g(x)); return toF(z); } return f"), this)(), 2); + +// -> FFI +var FFI = "var ffi = env.ffi;"; +assertAsmTypeFail('glob', 'env', USE_ASM + TO_FLOAT32 + FFI + "function f() {var x = toF(3.14); return +ffi(x);} return f"); +assertAsmTypeFail('glob', 'env', USE_ASM + TO_FLOAT32 + FFI + "function f() {var x = toF(3.14); return toF(ffi(+x));} return f"); + +var env = {ffi: function(x) { return x+1; }}; // use registers +assertEq(asmLink(asmCompile('glob', 'env', USE_ASM + TO_FLOAT32 + FFI + "function f(x) {x = toF(x); return toF(+ffi(+x));} return f"), this, env)(5), Math.fround(6)); +env = {ffi: function(a,b,c,d,e,f,g,h,i) { return a+b+c+d+e+f+g+h+i; }}; // use stack arguments (> 8 arguments on linux x64) +assertEq(asmLink(asmCompile('glob', 'env', USE_ASM + TO_FLOAT32 + FFI + "function f(x) {x = toF(x); return toF(+ffi(+x, 1., 2., 3., 4., 5., -5., -4., 1.));} return f"), this, env)(5), Math.fround(12)); + +// -> Internal calls +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x);return toF(+x + 1.);} function f(x) {x = +x; return toF(g(x));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x);return toF(+x + 1.);} function f(x) {x = x|0; return toF(g(x));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x);return toF(+x + 1.);} function f(x) {x = x|0; return toF(g(x));} return f"); + +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x);return toF(+x + 1.);} function f(x) {x = toF(x); return toF(g(x));} return f"), this, env)(5), Math.fround(6)); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function g(x,y){x=toF(x);y=toF(y);return toF(+x + +y);} function f(x) {x = toF(x); return toF(g(x, toF(1.)));} return f"), this, env)(5), Math.fround(6)); + +// --> internal calls with unused return values +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = +x; g = (g + ~~x)|0; return g|0;} function f(x) { x = +x; toF(s(x)); return g|0} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = +x; g = (g + ~~x)|0; return toF(g|0);} function f(x) { x = +x; toF(s(x)); return g|0} return f"), this)(3), 7); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = toF(x); g = (g + ~~x)|0; return toF(g|0);} function f(x) { x = toF(x); return (toF(s(x)), g)|0} return f"), this)(3), 7); + +// --> coerced calls +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = toF(x); g = (g + ~~x)|0; return +(g|0);} function f(x) { x = toF(x); return toF(+s(x))} return f"), this)(3), 7); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = toF(x); g = (g + ~~x)|0; return g|0;} function f(x) { x = toF(x); return toF(s(x)|0)} return f"), this)(3), 7); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = toF(x); g = (g + ~~x)|0; return toF(g|0);} function f(x) { x = toF(x); return toF(toF(s(x)))} return f"), this)(3), 7); + +// --> test pressure on registers in internal calls when there are |numArgs| arguments +for (numArgs of [5, 9, 17, 33, 65, 129]) { + let code = (function(n) { + let args = "", coercions = "", sum = "", call="x"; + for (let i = 0; i < n; i++) { + let name = 'a' + i; + args += name + ((i == n-1)?'':','); + coercions += name + '=toF(' + name + ');'; + sum += ((i>0)?'+':'') + ' +' + name; + call += (i==0)?'':',toF(' + i + '.)' + } + return "function g(" + args + "){" + coercions + "return toF(" + sum + ");}" + +"function f(x) { x = toF(x); return toF(g(" + call + "))}"; + })(numArgs); + assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + code + "return f"), this, env)(5), Math.fround(5 + numArgs * (numArgs-1) / 2)); +} + +// -> Pointer calls +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);} function f(x, n) {x=+x;n=n|0;return toF(t[n&1](x));} var t=[a,b]; return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);} function f(x, n) {x=x|0;n=n|0;return toF(t[n&1](x));} var t=[a,b]; return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);} function f(x, n) {x=toF(x);n=n|0;return t[n&1](x)|0;} var t=[a,b]; return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);} function f(x, n) {x=toF(x);n=n|0;return +t[n&1](x);} var t=[a,b]; return f"); + +code = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);}" + + "function f(x, n) {x=toF(x);n=n|0;return toF(t[n&1](x));} var t=[a,b]; return f"), this); +assertEq(code(0, 0), .5); +assertEq(code(0, 1), -.5); +assertEq(code(13.37, 0), Math.fround(13.37 + .5)); +assertEq(code(13.37, 1), Math.fround(13.37 - .5)); + +// Arithmetic operations +// -> mul +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3 * toF(4.));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3. * toF(4.)); } return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return +(toF(3.) * toF(4.));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3) * toF(4) * toF(5));} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.) * toF(4.)); } return f"), this)(), 12); + +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3 * f32[0]);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3. * f32[0]);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return +(toF(3.) * f32[0]);} return f"); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(toF(3.) * f32[0]);} return f"), this, null, heap)(), 12); + +var mul = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x=toF(x); return toF(x * toF(4.));} return f"), this); +assertEq(mul(Infinity), Infinity); +assertEq(mul(NaN), NaN); +assertEq(mul(0), 0); +assertEq(mul(1), 4); +assertEq(mul(0.33), Math.fround(0.33 * 4)); + +// -> add +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3 + toF(4.));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3.5 + toF(4.));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return +(toF(3.5) + toF(4.));} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) + toF(4.)); } return f"), this)(), 7.5); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) + toF(4.) + toF(4.5));} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(toF(3.5) + toF(4.)) + toF(4.5)); } return f"), this)(), 12); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(toF(3) + f32[0]);} return f"), this, null, heap)(), 7); + +// --> no additions with float? or floatish +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3 + f32[0]);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3. + f32[0]);} return f"); + +// -> sub +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3 - toF(4.));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3.5 - toF(4.));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return +(toF(3.5) - toF(4.));} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) - toF(4.)); } return f"), this)(), -.5); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) - toF(4.) - toF(4.5));} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(toF(3.5) - toF(4.)) - toF(4.5)); } return f"), this)(), -5); + +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) + toF(4.) - toF(4.5));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) - toF(4.) + toF(4.5));} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(toF(3.5) + toF(4.)) - toF(4.5)); } return f"), this)(), 3); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(toF(3.5) - toF(4.)) + toF(4.5)); } return f"), this)(), 4); + +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3 - f32[0]);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3. - f32[0]);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return +(toF(3) - f32[0]);} return f"); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(toF(3) - f32[0]);} return f"), this, null, heap)(), -1); + +// -> div +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3 / toF(4.));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3.5 / toF(4.));} return f"); +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return +(toF(3.5) / toF(4.));} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(12.) / toF(4.)); } return f"), this)(), 3); + +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3 / f32[0]);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3. / f32[0]);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return +(toF(3) / f32[0]);} return f"); +assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 2.; return toF(toF(4) / f32[0]);} return f"), this, null, heap)(), 2); + +// -> mod +assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) % toF(4.));} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { return toF(f32[0] % toF(4.));} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { return toF(toF(3.5) % f32[0]);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { return toF(f32[1] % f32[0]);} return f"); + +// Comparisons +for (op of ['==', '!=', '<', '>', '<=', '>=']) { + let code = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); if( x " + op + " toF(3.) ) return 1; else return 0; return -1; } return f"), this); + let ternary = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); return ((x " + op + " toF(3.)) ? 1 : 0)|0 } return f"), this); + for (v of [-5, 0, 2.5, 3, 13.37, NaN, Infinity]) { + let expected = eval("("+ v + " " + op + " 3)|0"); + assertEq(code(v) | 0, expected); + assertEq(ternary(v) | 0, expected); + } + + assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { if( f32[0] " + op + " toF(3.) ) return 1; else return 0; return -1; } return f"); + assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { if( (toF(1.) + toF(2.)) " + op + " toF(3.) ) return 1; else return 0; return -1; } return f"); +} diff --git a/js/src/jit-test/tests/asm.js/testHeapAccess.js b/js/src/jit-test/tests/asm.js/testHeapAccess.js index 699b6d4a9b57..662f7bba06af 100644 --- a/js/src/jit-test/tests/asm.js/testHeapAccess.js +++ b/js/src/jit-test/tests/asm.js/testHeapAccess.js @@ -154,6 +154,7 @@ assertEq(new Float64Array(BUF_64KB)[1], 1.3); new Float32Array(BUF_64KB)[1] = 1.0; assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[(4&0xffff)>>2] } return f'), this, null, BUF_64KB)(), 1.0); +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'var toFloat32 = glob.Math.fround; function f() { return toFloat32(f32[(4&0xffff)>>2]) } return f'), this, null, BUF_64KB)(), 1.0); new Float64Array(BUF_64KB)[1] = 1.3; assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8&0xffff)>>3] } return f'), this, null, BUF_64KB)(), 1.3); diff --git a/js/src/jit-test/tests/asm.js/testZOOB.js b/js/src/jit-test/tests/asm.js/testZOOB.js index d68b5f5308c2..d8c38d394ce5 100644 --- a/js/src/jit-test/tests/asm.js/testZOOB.js +++ b/js/src/jit-test/tests/asm.js/testZOOB.js @@ -80,12 +80,12 @@ function testInt(ctor, shift, scale, disp) { } } -function testFloat(ctor, shift, scale, disp) { +function testFloat(ctor, shift, scale, disp, coercion) { var ab = new ArrayBuffer(4096); var arr = new ctor(ab); for (var i = 0; i < arr.length; i++) arr[i] = i; - var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i) {i=i|0; return +arr[((i<<' + scale + ')+' + disp + ')>>' + shift + '] } return f'), this, null, ab); + var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); var toF = glob.Math.fround; function f(i) {i=i|0; return ' + coercion + '(arr[((i<<' + scale + ')+' + disp + ')>>' + shift + ']) } return f'), this, null, ab); for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097]) assertEq(f(i), +arr[((i<>shift]); @@ -94,7 +94,7 @@ function testFloat(ctor, shift, scale, disp) { assertEq(f(i+j), +arr[(((i+j)<>shift]); } - var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i,j) {i=i|0;j=+j; arr[((i<<' + scale + ')+' + disp + ')>>' + shift + '] = j } return f'), this, null, ab); + var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); var toF = glob.Math.fround; function f(i,j) {i=i|0;j=+j; arr[((i<<' + scale + ')+' + disp + ')>>' + shift + '] = j } return f'), this, null, ab); for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097]) { var index = ((i<>shift; var v = +arr[index]; @@ -114,6 +114,13 @@ function testFloat(ctor, shift, scale, disp) { } } +function testFloat32(ctor, shift, scale, disp) { + testFloat(ctor, shift, scale, disp, "toF"); +} +function testFloat64(ctor, shift, scale, disp) { + testFloat(ctor, shift, scale, disp, "+"); +} + function test(tester, ctor, shift) { for (scale of [0,1,2,3]) { for (disp of [0,1,8,Math.pow(2,31)-1,Math.pow(2,31),Math.pow(2,32)-1]) @@ -127,5 +134,5 @@ test(testInt, Int16Array, 1); test(testInt, Uint16Array, 1); test(testInt, Int32Array, 2); test(testInt, Uint32Array, 2); -test(testFloat, Float32Array, 2); -test(testFloat, Float64Array, 3); +test(testFloat32, Float32Array, 2); +test(testFloat64, Float64Array, 3); From 978e927a0eb251f6ecc514ce55e867e6b31413c2 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Thu, 12 Dec 2013 16:05:31 +0100 Subject: [PATCH 034/459] Bug 949474 - Reflect the fact that the nominal range for the sampleRate argument of the AudioContext.createBuffer changed in the spec. r=ehsan --- content/media/webaudio/AudioContext.cpp | 4 ++-- .../media/webaudio/test/test_AudioBuffer.html | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index f269163652f7..850727f1ef6c 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -174,8 +174,8 @@ AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels, uint32_t aLength, float aSampleRate, ErrorResult& aRv) { - if (aSampleRate < 8000 || aSampleRate > 96000 || !aLength) { - aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + if (aSampleRate < 8000 || aSampleRate > 192000 || !aLength || !aNumberOfChannels) { + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return nullptr; } diff --git a/content/media/webaudio/test/test_AudioBuffer.html b/content/media/webaudio/test/test_AudioBuffer.html index 75eeb7ff00e5..47716853bf6c 100644 --- a/content/media/webaudio/test/test_AudioBuffer.html +++ b/content/media/webaudio/test/test_AudioBuffer.html @@ -81,15 +81,21 @@ addLoadEvent(function() { expectException(function() { context.createBuffer(2, 2048, 7999); - }, DOMException.NOT_SUPPORTED_ERR); + }, DOMException.INDEX_SIZE_ERR); expectException(function() { - context.createBuffer(2, 2048, 96001); - }, DOMException.NOT_SUPPORTED_ERR); + context.createBuffer(2, 2048, 192001); + }, DOMException.INDEX_SIZE_ERR); context.createBuffer(2, 2048, 8000); // no exception - context.createBuffer(2, 2048, 96000); // no exception + context.createBuffer(2, 2048, 192000); // no exception + context.createBuffer(32, 2048, 48000); // no exception + // Null length expectException(function() { context.createBuffer(2, 0, 48000); - }, DOMException.NOT_SUPPORTED_ERR); + }, DOMException.INDEX_SIZE_ERR); + // Null number of channels + expectException(function() { + context.createBuffer(0, 2048, 48000); + }, DOMException.INDEX_SIZE_ERR); SimpleTest.finish(); }); From 64bc3f2d7ed6b0b8b3a32f584d9011a359b479be Mon Sep 17 00:00:00 2001 From: Nicholas Hurley Date: Thu, 12 Dec 2013 11:26:50 -0800 Subject: [PATCH 035/459] Bug 948757 - Don't flood servers with bunches of speculative connections. r=mcmanus --- netwerk/base/public/nsISpeculativeConnect.idl | 9 +-------- netwerk/base/src/Seer.cpp | 9 +-------- netwerk/protocol/http/nsHttpConnectionMgr.cpp | 6 +----- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/netwerk/base/public/nsISpeculativeConnect.idl b/netwerk/base/public/nsISpeculativeConnect.idl index f4bcd3cf4039..7d436519b7e7 100644 --- a/netwerk/base/public/nsISpeculativeConnect.idl +++ b/netwerk/base/public/nsISpeculativeConnect.idl @@ -35,7 +35,7 @@ interface nsISpeculativeConnect : nsISupports * inline) to determine whether or not to actually make a speculative * connection. */ -[builtinclass, uuid(2b6d6fb6-ab28-4f4c-af84-bfdbb7866d72)] +[builtinclass, uuid(f5f70897-2edf-4c00-a1c3-87d51b92cec5)] interface nsISpeculativeConnectionOverrider : nsISupports { /** @@ -50,11 +50,4 @@ interface nsISpeculativeConnectionOverrider : nsISupports * connect via SPDY or not. */ [infallible] readonly attribute boolean ignorePossibleSpdyConnections; - - /** - * Used to determine if we will ignore the existence of any currently idle - * connections when we decide whether or not to make a speculative - * connection. - */ - [infallible] readonly attribute boolean ignoreIdle; }; diff --git a/netwerk/base/src/Seer.cpp b/netwerk/base/src/Seer.cpp index f024345f1831..85f0fbcdee85 100644 --- a/netwerk/base/src/Seer.cpp +++ b/netwerk/base/src/Seer.cpp @@ -305,13 +305,6 @@ Seer::Observe(nsISupports *subject, const char *topic, // Seer::nsISpeculativeConnectionOverrider -NS_IMETHODIMP -Seer::GetIgnoreIdle(bool *ignoreIdle) -{ - *ignoreIdle = true; - return NS_OK; -} - NS_IMETHODIMP Seer::GetIgnorePossibleSpdyConnections(bool *ignorePossibleSpdyConnections) { @@ -903,7 +896,7 @@ Seer::PredictForLink(nsIURI *targetURI, nsIURI *sourceURI, } } - mSpeculativeService->SpeculativeConnect(targetURI, this); + mSpeculativeService->SpeculativeConnect(targetURI, nullptr); if (verifier) { verifier->OnPredictPreconnect(targetURI); } diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 05cd4c8ee39e..529702bf92d8 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -334,7 +334,6 @@ public: // intentional! bool mOverridesOK; uint32_t mParallelSpeculativeConnectLimit; - bool mIgnoreIdle; bool mIgnorePossibleSpdyConnections; // As above, added manually so we can use nsRefPtr without inheriting from @@ -381,7 +380,6 @@ nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci, args->mOverridesOK = true; overrider->GetParallelSpeculativeConnectLimit( &args->mParallelSpeculativeConnectLimit); - overrider->GetIgnoreIdle(&args->mIgnoreIdle); overrider->GetIgnorePossibleSpdyConnections( &args->mIgnorePossibleSpdyConnections); } @@ -2590,16 +2588,14 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param) uint32_t parallelSpeculativeConnectLimit = gHttpHandler->ParallelSpeculativeConnectLimit(); bool ignorePossibleSpdyConnections = false; - bool ignoreIdle = false; if (args->mOverridesOK) { parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit; ignorePossibleSpdyConnections = args->mIgnorePossibleSpdyConnections; - ignoreIdle = args->mIgnoreIdle; } if (mNumHalfOpenConns < parallelSpeculativeConnectLimit && - (ignoreIdle || !ent->mIdleConns.Length()) && + ent->mIdleConns.Length() < parallelSpeculativeConnectLimit && !RestrictConnections(ent, ignorePossibleSpdyConnections) && !AtActiveConnectionLimit(ent, args->mTrans->Caps())) { CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true); From 79731ad42a6017c988b52954dd390c87fdc22854 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 12 Dec 2013 19:27:55 +0000 Subject: [PATCH 036/459] Bug 920905 - DOMFile.name is used to show the filename in the b2g filepicker, r=fabrice --- b2g/components/FilePicker.js | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/b2g/components/FilePicker.js b/b2g/components/FilePicker.js index e121a29433e7..1d04d961311a 100644 --- a/b2g/components/FilePicker.js +++ b/b2g/components/FilePicker.js @@ -32,6 +32,7 @@ const AUDIO_FILTERS = ['audio/basic', 'audio/L24', 'audio/mp4', 'audio/webm']; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import("resource://gre/modules/FileUtils.jsm"); XPCOMUtils.defineLazyServiceGetter(this, 'cpmm', '@mozilla.org/childprocessmessagemanager;1', @@ -179,18 +180,39 @@ FilePicker.prototype = { return; } - var name = 'blob'; - if (data.result.blob.type) { - let mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); - let mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, ''); - if (mimeInfo) { - name += '.' + mimeInfo.primaryExtension; + // The name to be shown can be part of the message, or can be taken from + // the DOMFile (if the blob is a DOMFile). + let name = data.result.name; + if (!name && + (data.result.blob instanceof this.mParent.File) && + data.result.blob.name) { + name = data.result.blob.name; + } + + // Let's try to remove the full path and take just the filename. + if (name) { + let file = new FileUtils.File(data.result.blob.name); + if (file && file.leafName) { + name = file.leafName; + } + } + + // the fallback is a filename composed by 'blob' + extension. + if (!name) { + name = 'blob'; + if (data.result.blob.type) { + let mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, ''); + if (mimeInfo) { + name += '.' + mimeInfo.primaryExtension; + } } } let file = new this.mParent.File(data.result.blob, { name: name, type: data.result.blob.type }); + if (file) { this.fireSuccess(file); } else { From 919668270c84380d7ab446e379ca2b9198a93667 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 12 Dec 2013 19:30:10 +0000 Subject: [PATCH 037/459] Bug 887836 - URLQuery object, r=khuey --- dom/base/URLSearchParams.cpp | 236 ++++++++++++++++++++++++ dom/base/URLSearchParams.h | 76 ++++++++ dom/base/moz.build | 2 + dom/base/test/mochitest.ini | 1 + dom/base/test/test_urlSearchParams.html | 141 ++++++++++++++ dom/bindings/Bindings.conf | 5 + dom/webidl/URLSearchParams.webidl | 26 +++ dom/webidl/URLUtils.webidl | 2 +- dom/webidl/moz.build | 1 + 9 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 dom/base/URLSearchParams.cpp create mode 100644 dom/base/URLSearchParams.h create mode 100644 dom/base/test/test_urlSearchParams.html create mode 100644 dom/webidl/URLSearchParams.webidl diff --git a/dom/base/URLSearchParams.cpp b/dom/base/URLSearchParams.cpp new file mode 100644 index 000000000000..af2f09371222 --- /dev/null +++ b/dom/base/URLSearchParams.cpp @@ -0,0 +1,236 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "URLSearchParams.h" +#include "mozilla/dom/URLSearchParamsBinding.h" + +namespace mozilla { +namespace dom { + +URLSearchParams::URLSearchParams() +{ +} + +URLSearchParams::~URLSearchParams() +{ + DeleteAll(); +} + +JSObject* +URLSearchParams::WrapObject(JSContext* aCx, JS::Handle aScope) +{ + return URLSearchParamsBinding::Wrap(aCx, aScope, this); +} + +/* static */ already_AddRefed +URLSearchParams::Constructor(const GlobalObject& aGlobal, + const nsAString& aInit, + ErrorResult& aRv) +{ + nsRefPtr sp = new URLSearchParams(); + sp->ParseInput(aInit); + return sp.forget(); +} + +/* static */ already_AddRefed +URLSearchParams::Constructor(const GlobalObject& aGlobal, + URLSearchParams& aInit, + ErrorResult& aRv) +{ + nsRefPtr sp = new URLSearchParams(); + aInit.mSearchParams.EnumerateRead(CopyEnumerator, sp); + return sp.forget(); +} + +void +URLSearchParams::ParseInput(const nsAString& aInput) +{ + nsAString::const_iterator start, end; + aInput.BeginReading(start); + aInput.EndReading(end); + nsAString::const_iterator iter(start); + + while (start != end) { + nsAutoString string; + + if (FindCharInReadable('&', iter, end)) { + string.Assign(Substring(start, iter)); + start = ++iter; + } else { + string.Assign(Substring(start, end)); + start = end; + } + + if (string.IsEmpty()) { + continue; + } + + nsAString::const_iterator eqStart, eqEnd; + string.BeginReading(eqStart); + string.EndReading(eqEnd); + nsAString::const_iterator eqIter(eqStart); + + nsAutoString name; + nsAutoString value; + + if (FindCharInReadable('=', eqIter, eqEnd)) { + name.Assign(Substring(eqStart, eqIter)); + + ++eqIter; + value.Assign(Substring(eqIter, eqEnd)); + } else { + name.Assign(string); + } + + nsAutoString decodedName; + DecodeString(name, decodedName); + + nsAutoString decodedValue; + DecodeString(value, decodedValue); + + Append(decodedName, decodedValue); + } +} + +void +URLSearchParams::DecodeString(const nsAString& aInput, nsAString& aOutput) +{ + nsAString::const_iterator start, end; + aInput.BeginReading(start); + aInput.EndReading(end); + + while (start != end) { + // replace '+' with U+0020 + if (*start == '+') { + aOutput.Append(' '); + ++start; + continue; + } + + // Percent decode algorithm + if (*start == '%') { + nsAString::const_iterator first(start); + ++first; + + nsAString::const_iterator second(first); + ++second; + +#define ASCII_HEX_DIGIT( x ) \ + ((x >= 0x41 && x <= 0x46) || \ + (x >= 0x61 && x <= 0x66) || \ + (x >= 0x30 && x <= 0x39)) + +#define HEX_DIGIT( x ) \ + (*x >= 0x30 && *x <= 0x39 \ + ? *x - 0x30 \ + : (*x >= 0x41 && *x <= 0x46 \ + ? *x - 0x37 \ + : *x - 0x57)) + + if (first != end && second != end && + ASCII_HEX_DIGIT(*first) && ASCII_HEX_DIGIT(*second)) { + aOutput.Append(HEX_DIGIT(first) * 16 + HEX_DIGIT(second)); + start = ++second; + continue; + + } else { + aOutput.Append('%'); + ++start; + continue; + } + } + + aOutput.Append(*start); + ++start; + } +} + +/* static */ PLDHashOperator +URLSearchParams::CopyEnumerator(const nsAString& aName, + nsTArray* aArray, + void *userData) +{ + URLSearchParams* aSearchParams = static_cast(userData); + + nsTArray* newArray = new nsTArray(); + newArray->AppendElements(*aArray); + + aSearchParams->mSearchParams.Put(aName, newArray); + return PL_DHASH_NEXT; +} + +void +URLSearchParams::Get(const nsAString& aName, nsString& aRetval) +{ + nsTArray* array; + if (!mSearchParams.Get(aName, &array)) { + aRetval.Truncate(); + return; + } + + aRetval.Assign(array->ElementAt(0)); +} + +void +URLSearchParams::GetAll(const nsAString& aName, nsTArray& aRetval) +{ + nsTArray* array; + if (!mSearchParams.Get(aName, &array)) { + return; + } + + aRetval.AppendElements(*array); +} + +void +URLSearchParams::Set(const nsAString& aName, const nsAString& aValue) +{ + nsTArray* array; + if (!mSearchParams.Get(aName, &array)) { + array = new nsTArray(); + array->AppendElement(aValue); + mSearchParams.Put(aName, array); + } else { + array->ElementAt(0) = aValue; + } +} + +void +URLSearchParams::Append(const nsAString& aName, const nsAString& aValue) +{ + nsTArray* array; + if (!mSearchParams.Get(aName, &array)) { + array = new nsTArray(); + mSearchParams.Put(aName, array); + } + + array->AppendElement(aValue); +} + +bool +URLSearchParams::Has(const nsAString& aName) +{ + return mSearchParams.Get(aName, nullptr); +} + +void +URLSearchParams::Delete(const nsAString& aName) +{ + nsTArray* array; + if (!mSearchParams.Get(aName, &array)) { + return; + } + + mSearchParams.Remove(aName); +} + +void +URLSearchParams::DeleteAll() +{ + mSearchParams.Clear(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/base/URLSearchParams.h b/dom/base/URLSearchParams.h new file mode 100644 index 000000000000..90c3d579a94f --- /dev/null +++ b/dom/base/URLSearchParams.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_URLSearchParams_h +#define mozilla_dom_URLSearchParams_h + +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/ErrorResult.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" + +namespace mozilla { +namespace dom { + +class URLSearchParams MOZ_FINAL +{ +public: + NS_INLINE_DECL_REFCOUNTING(URL) + + URLSearchParams(); + ~URLSearchParams(); + + // WebIDL methods + nsISupports* GetParentObject() const + { + return nullptr; + } + + JSObject* + WrapObject(JSContext* aCx, JS::Handle aScope); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aInit, + ErrorResult& aRv); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, URLSearchParams& aInit, + ErrorResult& aRv); + + void Get(const nsAString& aName, nsString& aRetval); + + void GetAll(const nsAString& aName, nsTArray& aRetval); + + void Set(const nsAString& aName, const nsAString& aValue); + + void Append(const nsAString& aName, const nsAString& aValue); + + bool Has(const nsAString& aName); + + void Delete(const nsAString& aName); + + uint32_t Size() const + { + return mSearchParams.Count(); + } + +private: + void ParseInput(const nsAString& aInput); + + void DeleteAll(); + + void DecodeString(const nsAString& aInput, nsAString& aOutput); + + static PLDHashOperator + CopyEnumerator(const nsAString& aName, nsTArray* aArray, + void *userData); + + nsClassHashtable> mSearchParams; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_URLSearchParams_h */ diff --git a/dom/base/moz.build b/dom/base/moz.build index dc8c53494e92..b6c0ee9cd98a 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -62,6 +62,7 @@ EXPORTS.mozilla.dom += [ 'ScriptSettings.h', 'StructuredCloneTags.h', 'URL.h', + 'URLSearchParams.h', ] UNIFIED_SOURCES += [ @@ -97,6 +98,7 @@ UNIFIED_SOURCES += [ 'nsWrapperCache.cpp', 'ScriptSettings.cpp', 'URL.cpp', + 'URLSearchParams.cpp', 'WindowNamedPropertiesHandler.cpp', ] diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 6b6403f395aa..51ded34a81c5 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -44,3 +44,4 @@ support-files = [test_openDialogChromeOnly.html] [test_messagemanager_targetchain.html] [test_url_empty_port.html] +[test_urlSearchParams.html] diff --git a/dom/base/test/test_urlSearchParams.html b/dom/base/test/test_urlSearchParams.html new file mode 100644 index 000000000000..a6a4c18f563e --- /dev/null +++ b/dom/base/test/test_urlSearchParams.html @@ -0,0 +1,141 @@ + + + + + + + Test for Bug 887836 + + + + +Mozilla Bug 887836 +

+ +
+
+ + + diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 807edf96001a..8603e0f2f360 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1290,6 +1290,11 @@ DOMInterfaces = { 'nativeOwnership': 'owned', }], +'URLSearchParams' : { + 'wrapperCache' : False, + 'nativeOwnership': 'refcounted', +}, + 'VTTCue': { 'nativeType': 'mozilla::dom::TextTrackCue' }, diff --git a/dom/webidl/URLSearchParams.webidl b/dom/webidl/URLSearchParams.webidl new file mode 100644 index 000000000000..7d4d99a309e4 --- /dev/null +++ b/dom/webidl/URLSearchParams.webidl @@ -0,0 +1,26 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * http://url.spec.whatwg.org/#urlsearchparams + * + * To the extent possible under law, the editors have waived all copyright + * and related or neighboring rights to this work. In addition, as of 17 + * February 2013, the editors have made this specification available under + * the Open Web Foundation Agreement Version 1.0, which is available at + * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0. + */ + +[Constructor(optional DOMString init = ""), + Constructor(URLSearchParams init)] +interface URLSearchParams { + DOMString? get(DOMString name); + sequence getAll(DOMString name); + void set(DOMString name, DOMString value); + void append(DOMString name, DOMString value); + boolean has(DOMString name); + void delete(DOMString name); + readonly attribute unsigned long size; +}; diff --git a/dom/webidl/URLUtils.webidl b/dom/webidl/URLUtils.webidl index 338a61eabd02..ddd690ccef05 100644 --- a/dom/webidl/URLUtils.webidl +++ b/dom/webidl/URLUtils.webidl @@ -27,7 +27,7 @@ interface URLUtils { attribute DOMString port; attribute DOMString pathname; attribute DOMString search; - // attribute URLQuery? query; + // attribute URLSearchParams? searchParams; attribute DOMString hash; }; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index abcd6a2cbe59..108b38a1b3ec 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -407,6 +407,7 @@ WEBIDL_FILES = [ 'UIEvent.webidl', 'UndoManager.webidl', 'URL.webidl', + 'URLSearchParams.webidl', 'URLUtils.webidl', 'URLUtilsReadOnly.webidl', 'ValidityState.webidl', From 41741d7eb0ad7be74cfb1338ba502947d9bd7f7c Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 12 Dec 2013 19:30:20 +0000 Subject: [PATCH 038/459] Bug 887836 - patch 2 - URLQuery in workers, r=khuey --- dom/workers/RegisterBindings.cpp | 2 + dom/workers/test/mochitest.ini | 2 + dom/workers/test/test_urlSearchParams.html | 43 +++++++ dom/workers/test/urlSearchparams_worker.js | 133 +++++++++++++++++++++ 4 files changed, 180 insertions(+) create mode 100644 dom/workers/test/test_urlSearchParams.html create mode 100644 dom/workers/test/urlSearchparams_worker.js diff --git a/dom/workers/RegisterBindings.cpp b/dom/workers/RegisterBindings.cpp index 68ec92ee546c..a40dc6b100ae 100644 --- a/dom/workers/RegisterBindings.cpp +++ b/dom/workers/RegisterBindings.cpp @@ -26,6 +26,7 @@ #include "mozilla/dom/XMLHttpRequestBinding.h" #include "mozilla/dom/XMLHttpRequestUploadBinding.h" #include "mozilla/dom/URLBinding.h" +#include "mozilla/dom/URLSearchParamsBinding.h" #include "mozilla/dom/WorkerBinding.h" #include "mozilla/dom/WorkerLocationBinding.h" #include "mozilla/dom/WorkerNavigatorBinding.h" @@ -70,6 +71,7 @@ WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle aGlobal) !XMLHttpRequestBinding_workers::GetConstructorObject(aCx, aGlobal) || !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, aGlobal) || !URLBinding_workers::GetConstructorObject(aCx, aGlobal) || + !URLSearchParamsBinding::GetConstructorObject(aCx, global) || !WorkerBinding::GetConstructorObject(aCx, aGlobal) || !WorkerLocationBinding_workers::GetConstructorObject(aCx, aGlobal) || !WorkerNavigatorBinding_workers::GetConstructorObject(aCx, aGlobal)) { diff --git a/dom/workers/test/mochitest.ini b/dom/workers/test/mochitest.ini index 422f786c2386..493592dd8b92 100644 --- a/dom/workers/test/mochitest.ini +++ b/dom/workers/test/mochitest.ini @@ -57,6 +57,7 @@ support-files = xhr_worker.js url_exceptions_worker.js jsversion_worker.js + urlSearchParams_worker.js [test_404.html] [test_atob.html] @@ -114,4 +115,5 @@ support-files = [test_xhr_system.html] [test_xhr_system.js] [test_url_exceptions.html] +[test_urlSearchParams.html] [test_jsversion.html] diff --git a/dom/workers/test/test_urlSearchParams.html b/dom/workers/test/test_urlSearchParams.html new file mode 100644 index 000000000000..5635b4640d98 --- /dev/null +++ b/dom/workers/test/test_urlSearchParams.html @@ -0,0 +1,43 @@ + + + + + Test for URLSearchParams object in workers + + + + +

+ +

+
+
+ + diff --git a/dom/workers/test/urlSearchparams_worker.js b/dom/workers/test/urlSearchparams_worker.js new file mode 100644 index 000000000000..bec0ea83fb37 --- /dev/null +++ b/dom/workers/test/urlSearchparams_worker.js @@ -0,0 +1,133 @@ +function ok(a, msg) { + dump("OK: " + !!a + " => " + a + " " + msg + "\n"); + postMessage({type: 'status', status: !!a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n"); + postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); +} + +onmessage = function() { + status = false; + try { + if ((URLSearchParams instanceof Object)) { + status = true; + } + } catch(e) { + } + ok(status, "URLSearchParams in workers \\o/"); + + function testSimpleURLSearchParams() { + var u = new URLSearchParams(); + ok(u, "URLSearchParams created"); + is(u.has('foo'), false, 'URLSearchParams.has(foo)'); + is(u.get('foo'), '', 'URLSearchParams.get(foo)'); + is(u.getAll('foo').length, 0, 'URLSearchParams.getAll(foo)'); + is(u.size, 0, 'URLSearchParams.size()'); + + u.append('foo', 'bar'); + is(u.has('foo'), true, 'URLSearchParams.has(foo)'); + is(u.get('foo'), 'bar', 'URLSearchParams.get(foo)'); + is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)'); + is(u.size, 1, 'URLSearchParams.size()'); + + u.set('foo', 'bar2'); + is(u.get('foo'), 'bar2', 'URLSearchParams.get(foo)'); + is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)'); + is(u.size, 1, 'URLSearchParams.size()'); + + u.delete('foo'); + is(u.size, 0, 'URLSearchParams.size()'); + + runTest(); + } + + function testCopyURLSearchParams() { + var u = new URLSearchParams(); + ok(u, "URLSearchParams created"); + u.append('foo', 'bar'); + is(u.size, 1, "u.size()"); + + var uu = new URLSearchParams(u); + is(uu.size, 1, "uu.size()"); + is(uu.get('foo'), 'bar', 'uu.get()'); + + u.append('foo', 'bar2'); + is(u.getAll('foo').length, 2, "u.getAll()"); + is(uu.getAll('foo').length, 1, "uu.getAll()"); + + runTest(); + } + + function testParserURLSearchParams() { + var checks = [ + { input: '', data: {} }, + { input: 'a', data: { 'a' : [''] } }, + { input: 'a=b', data: { 'a' : ['b'] } }, + { input: 'a=', data: { 'a' : [''] } }, + { input: '=b', data: { '' : ['b'] } }, + { input: '&', data: {} }, + { input: '&a', data: { 'a' : [''] } }, + { input: 'a&', data: { 'a' : [''] } }, + { input: 'a&a', data: { 'a' : ['', ''] } }, + { input: 'a&b&c', data: { 'a' : [''], 'b' : [''], 'c' : [''] } }, + { input: 'a=b&c=d', data: { 'a' : ['b'], 'c' : ['d'] } }, + { input: 'a=b&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } }, + { input: '&&&a=b&&&&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } }, + { input: 'a=a&a=b&a=c', data: { 'a' : ['a', 'b', 'c'] } }, + { input: 'a==a', data: { 'a' : ['=a'] } }, + { input: 'a=a+b+c+d', data: { 'a' : ['a b c d'] } }, + { input: '%=a', data: { '%' : ['a'] } }, + { input: '%a=a', data: { '%a' : ['a'] } }, + { input: '%a_=a', data: { '%a_' : ['a'] } }, + { input: '%61=a', data: { 'a' : ['a'] } }, + { input: '%=a', data: { '%' : ['a'] } }, + { input: '%a=a', data: { '%a' : ['a'] } }, + { input: '%a_=a', data: { '%a_' : ['a'] } }, + { input: '%61=a', data: { 'a' : ['a'] } }, + { input: '%61+%4d%4D=', data: { 'a MM' : [''] } }, + ]; + + for (var i = 0; i < checks.length; ++i) { + var u = new URLSearchParams(checks[i].input); + + var count = 0; + for (var key in checks[i].data) { + ++count; + ok(u.has(key), "key " + key + " found"); + + var all = u.getAll(key); + is(all.length, checks[i].data[key].length, "same number of elements"); + + for (var k = 0; k < all.length; ++k) { + is(all[k], checks[i].data[key][k], "value matches"); + } + } + + is(u.size, count, "size matches"); + } + + runTest(); + } + + var tests = [ + testSimpleURLSearchParams, + testCopyURLSearchParams, + testParserURLSearchParams + ]; + + function runTest() { + if (!tests.length) { + postMessage({type: 'finish' }); + return; + } + + var test = tests.shift(); + test(); + } + + runTest(); +} + + From aeb5063aca149161e54fe57fe5dded8f655898ef Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 12 Dec 2013 19:30:27 +0000 Subject: [PATCH 039/459] Bug 887836 - URLSearchParams and URL, r=ehsan, r=bz, r=smaug --HG-- rename : dom/workers/test/urlSearchparams_worker.js => dom/workers/test/urlSearchParams_worker.js --- content/base/src/Link.cpp | 94 ++++++++- content/base/src/Link.h | 20 +- .../html/content/src/HTMLAnchorElement.cpp | 22 +- content/html/content/src/HTMLAnchorElement.h | 30 +-- content/html/content/src/HTMLAreaElement.cpp | 22 +- content/html/content/src/HTMLAreaElement.h | 34 ++- content/html/content/src/HTMLLinkElement.cpp | 2 + content/svg/content/src/SVGAElement.cpp | 26 ++- content/svg/content/src/SVGAElement.h | 1 + dom/base/URL.cpp | 89 ++++++++ dom/base/URL.h | 19 +- dom/base/URLSearchParams.cpp | 193 ++++++++++++++++-- dom/base/URLSearchParams.h | 64 +++++- dom/base/test/test_urlSearchParams.html | 102 ++++++++- dom/bindings/Bindings.conf | 6 - .../mochitest/general/test_interfaces.html | 1 + dom/webidl/URLUtils.webidl | 2 +- dom/workers/RegisterBindings.cpp | 2 +- dom/workers/URL.cpp | 91 ++++++++- dom/workers/URL.h | 24 ++- ...ms_worker.js => urlSearchParams_worker.js} | 65 +++++- .../components/places/tests/cpp/mock_Link.h | 129 ++++++++++++ 22 files changed, 935 insertions(+), 103 deletions(-) rename dom/workers/test/{urlSearchparams_worker.js => urlSearchParams_worker.js} (64%) diff --git a/content/base/src/Link.cpp b/content/base/src/Link.cpp index ef63ddefcf68..d5389a7490b2 100644 --- a/content/base/src/Link.cpp +++ b/content/base/src/Link.cpp @@ -214,7 +214,16 @@ Link::SetPathname(const nsAString &aPathname) } void -Link::SetSearch(const nsAString &aSearch) +Link::SetSearch(const nsAString& aSearch) +{ + SetSearchInternal(aSearch); + if (mSearchParams) { + mSearchParams->Invalidate(); + } +} + +void +Link::SetSearchInternal(const nsAString& aSearch) { nsCOMPtr uri(GetURIToMutate()); nsCOMPtr url(do_QueryInterface(uri)); @@ -478,6 +487,9 @@ Link::ResetLinkState(bool aNotify, bool aHasHref) // If we've cached the URI, reset always invalidates it. mCachedURI = nullptr; + if (mSearchParams) { + mSearchParams->Invalidate(); + } // Update our state back to the default. mLinkState = defaultState; @@ -563,5 +575,85 @@ Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const return n; } +URLSearchParams* +Link::GetSearchParams() +{ + CreateSearchParamsIfNeeded(); + return mSearchParams; +} + +void +Link::SetSearchParams(URLSearchParams* aSearchParams) +{ + if (!aSearchParams) { + return; + } + + if (!aSearchParams->HasURLAssociated()) { + MOZ_ASSERT(aSearchParams->IsValid()); + + mSearchParams = aSearchParams; + mSearchParams->SetObserver(this); + } else { + CreateSearchParamsIfNeeded(); + mSearchParams->CopyFromURLSearchParams(*aSearchParams); + } + + nsAutoString search; + mSearchParams->Serialize(search); + SetSearchInternal(search); +} + +void +Link::URLSearchParamsUpdated() +{ + MOZ_ASSERT(mSearchParams && mSearchParams->IsValid()); + + nsString search; + mSearchParams->Serialize(search); + SetSearchInternal(search); +} + +void +Link::URLSearchParamsNeedsUpdates() +{ + MOZ_ASSERT(mSearchParams); + + nsAutoCString search; + nsCOMPtr uri(GetURI()); + nsCOMPtr url(do_QueryInterface(uri)); + if (url) { + nsresult rv = url->GetQuery(search); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get the query from a nsIURL."); + } + } + + mSearchParams->ParseInput(search); +} + +void +Link::CreateSearchParamsIfNeeded() +{ + if (!mSearchParams) { + mSearchParams = new URLSearchParams(); + mSearchParams->SetObserver(this); + mSearchParams->Invalidate(); + } +} + +void +Link::Unlink() +{ + mSearchParams = nullptr; +} + +void +Link::Traverse(nsCycleCollectionTraversalCallback &cb) +{ + Link* tmp = this; + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams); +} + } // namespace dom } // namespace mozilla diff --git a/content/base/src/Link.h b/content/base/src/Link.h index 163bf746f7cb..a297bd693976 100644 --- a/content/base/src/Link.h +++ b/content/base/src/Link.h @@ -13,6 +13,7 @@ #include "mozilla/IHistory.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/dom/URLSearchParams.h" #include "nsEventStates.h" #include "nsIContent.h" @@ -25,7 +26,7 @@ class Element; { 0xb25edee6, 0xdd35, 0x4f8b, \ { 0xab, 0x90, 0x66, 0xd0, 0xbd, 0x3c, 0x22, 0xd5 } } -class Link : public nsISupports +class Link : public URLSearchParamsObserver { public: NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_LINK_IMPLEMENTATION_IID) @@ -61,6 +62,7 @@ public: void SetHostname(const nsAString &aHostname); void SetPathname(const nsAString &aPathname); void SetSearch(const nsAString &aSearch); + void SetSearchParams(mozilla::dom::URLSearchParams* aSearchParams); void SetPort(const nsAString &aPort); void SetHash(const nsAString &aHash); void GetOrigin(nsAString &aOrigin); @@ -71,6 +73,7 @@ public: void GetHostname(nsAString &_hostname); void GetPathname(nsAString &_pathname); void GetSearch(nsAString &_search); + URLSearchParams* GetSearchParams(); void GetPort(nsAString &_port); void GetHash(nsAString &_hash); @@ -109,6 +112,10 @@ public: bool ElementHasHref() const; + // URLSearchParamsObserver + void URLSearchParamsUpdated() MOZ_OVERRIDE; + void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE; + protected: virtual ~Link(); @@ -127,6 +134,10 @@ protected: nsIURI* GetCachedURI() const { return mCachedURI; } bool HasCachedURI() const { return !!mCachedURI; } + // CC methods + void Unlink(); + void Traverse(nsCycleCollectionTraversalCallback &cb); + private: /** * Unregisters from History so this node no longer gets notifications about @@ -137,6 +148,10 @@ private: already_AddRefed GetURIToMutate(); void SetHrefAttribute(nsIURI *aURI); + void CreateSearchParamsIfNeeded(); + + void SetSearchInternal(const nsAString& aSearch); + mutable nsCOMPtr mCachedURI; Element * const mElement; @@ -150,6 +165,9 @@ private: bool mNeedsRegistration; bool mRegistered; + +protected: + nsRefPtr mSearchParams; }; NS_DEFINE_STATIC_IID_ACCESSOR(Link, MOZILLA_DOM_LINK_IMPLEMENTATION_IID) diff --git a/content/html/content/src/HTMLAnchorElement.cpp b/content/html/content/src/HTMLAnchorElement.cpp index b70ad2905f44..9d599c9303a2 100644 --- a/content/html/content/src/HTMLAnchorElement.cpp +++ b/content/html/content/src/HTMLAnchorElement.cpp @@ -41,8 +41,26 @@ HTMLAnchorElement::~HTMLAnchorElement() { } -NS_IMPL_ISUPPORTS_INHERITED2(HTMLAnchorElement, nsGenericHTMLElement, - nsIDOMHTMLAnchorElement, Link) +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement) + NS_INTERFACE_TABLE_INHERITED2(HTMLAnchorElement, + nsIDOMHTMLAnchorElement, + Link) +NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement) + +NS_IMPL_ADDREF_INHERITED(HTMLAnchorElement, Element) +NS_IMPL_RELEASE_INHERITED(HTMLAnchorElement, Element) + +NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAnchorElement) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLAnchorElement, + nsGenericHTMLElement) + tmp->Link::Traverse(cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLAnchorElement, + nsGenericHTMLElement) + tmp->Link::Unlink(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement) diff --git a/content/html/content/src/HTMLAnchorElement.h b/content/html/content/src/HTMLAnchorElement.h index 39ee17bc47cd..751b541537a6 100644 --- a/content/html/content/src/HTMLAnchorElement.h +++ b/content/html/content/src/HTMLAnchorElement.h @@ -33,6 +33,10 @@ public: // nsISupports NS_DECL_ISUPPORTS_INHERITED + // CC + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLAnchorElement, + nsGenericHTMLElement) + virtual int32_t TabIndexDefault() MOZ_OVERRIDE; virtual bool Draggable() const MOZ_OVERRIDE; @@ -135,30 +139,14 @@ public: rv = SetText(aValue); } - void GetOrigin(nsAString& aOrigin) - { - Link::GetOrigin(aOrigin); - } + // Link::GetOrigin is OK for us - void GetUsername(nsAString& aUsername) - { - Link::GetUsername(aUsername); - } + // Link::GetUsername is OK for us + // Link::SetUsername is OK for us - void SetUsername(const nsAString& aUsername) - { - Link::SetUsername(aUsername); - } + // Link::Getpassword is OK for us + // Link::Setpassword is OK for us - void GetPassword(nsAString& aPassword) - { - Link::GetPassword(aPassword); - } - - void SetPassword(const nsAString& aPassword) - { - Link::SetPassword(aPassword); - } // The XPCOM URI decomposition attributes are fine for us void GetCoords(nsString& aValue) { diff --git a/content/html/content/src/HTMLAreaElement.cpp b/content/html/content/src/HTMLAreaElement.cpp index d7ebcbd088c4..3cc9b7331d37 100644 --- a/content/html/content/src/HTMLAreaElement.cpp +++ b/content/html/content/src/HTMLAreaElement.cpp @@ -25,8 +25,26 @@ HTMLAreaElement::~HTMLAreaElement() { } -NS_IMPL_ISUPPORTS_INHERITED2(HTMLAreaElement, nsGenericHTMLElement, - nsIDOMHTMLAreaElement, Link) +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLAreaElement) + NS_INTERFACE_TABLE_INHERITED2(HTMLAreaElement, + nsIDOMHTMLAreaElement, + Link) +NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement) + +NS_IMPL_ADDREF_INHERITED(HTMLAreaElement, Element) +NS_IMPL_RELEASE_INHERITED(HTMLAreaElement, Element) + +NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAreaElement) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLAreaElement, + nsGenericHTMLElement) + tmp->Link::Traverse(cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLAreaElement, + nsGenericHTMLElement) + tmp->Link::Unlink(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_ELEMENT_CLONE(HTMLAreaElement) diff --git a/content/html/content/src/HTMLAreaElement.h b/content/html/content/src/HTMLAreaElement.h index dd84662a6861..4523c7e21367 100644 --- a/content/html/content/src/HTMLAreaElement.h +++ b/content/html/content/src/HTMLAreaElement.h @@ -30,6 +30,10 @@ public: // nsISupports NS_DECL_ISUPPORTS_INHERITED + // CC + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLAreaElement, + nsGenericHTMLElement) + // DOM memory reporter participant NS_DECL_SIZEOF_EXCLUDING_THIS @@ -108,33 +112,16 @@ public: SetHTMLAttr(nsGkAtoms::ping, aPing, aError); } - void GetOrigin(nsAString &aOrigin) - { - Link::GetOrigin(aOrigin); - } + // The Link::GetOrigin is OK for us // The XPCOM GetProtocol is OK for us // The XPCOM SetProtocol is OK for us - void GetUsername(nsAString& aUsername) - { - Link::GetUsername(aUsername); - } + // The Link::GetUsername is OK for us + // The Link::SetUsername is OK for us - void SetUsername(const nsAString& aUsername) - { - Link::SetUsername(aUsername); - } - - void GetPassword(nsAString& aPassword) - { - Link::GetPassword(aPassword); - } - - void SetPassword(const nsAString& aPassword) - { - Link::SetPassword(aPassword); - } + // The Link::GetPassword is OK for us + // The Link::SetPassword is OK for us // The XPCOM GetHost is OK for us // The XPCOM SetHost is OK for us @@ -154,6 +141,9 @@ public: // The XPCOM GetHash is OK for us // The XPCOM SetHash is OK for us + // The Link::GetSearchParams is OK for us + // The Link::SetSearchParams is OK for us + bool NoHref() const { return GetBoolAttr(nsGkAtoms::nohref); diff --git a/content/html/content/src/HTMLLinkElement.cpp b/content/html/content/src/HTMLLinkElement.cpp index 2ed55d397320..87f69e93001f 100644 --- a/content/html/content/src/HTMLLinkElement.cpp +++ b/content/html/content/src/HTMLLinkElement.cpp @@ -44,11 +44,13 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLLinkElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLLinkElement, nsGenericHTMLElement) tmp->nsStyleLinkElement::Traverse(cb); + tmp->Link::Traverse(cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLLinkElement, nsGenericHTMLElement) tmp->nsStyleLinkElement::Unlink(); + tmp->Link::Unlink(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_ADDREF_INHERITED(HTMLLinkElement, Element) diff --git a/content/svg/content/src/SVGAElement.cpp b/content/svg/content/src/SVGAElement.cpp index 49d870980141..1dc94402d7e1 100644 --- a/content/svg/content/src/SVGAElement.cpp +++ b/content/svg/content/src/SVGAElement.cpp @@ -34,12 +34,28 @@ nsSVGElement::StringInfo SVGAElement::sStringInfo[2] = //---------------------------------------------------------------------- // nsISupports methods -NS_IMPL_ISUPPORTS_INHERITED4(SVGAElement, SVGAElementBase, - nsIDOMNode, - nsIDOMElement, - nsIDOMSVGElement, - Link) +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGAElement) + NS_INTERFACE_TABLE_INHERITED4(SVGAElement, + nsIDOMNode, + nsIDOMElement, + nsIDOMSVGElement, + Link) +NS_INTERFACE_TABLE_TAIL_INHERITING(SVGAElementBase) +NS_IMPL_CYCLE_COLLECTION_CLASS(SVGAElement) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGAElement, + SVGAElementBase) + tmp->Link::Traverse(cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGAElement, + SVGAElementBase) + tmp->Link::Unlink(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase) +NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase) //---------------------------------------------------------------------- // Implementation diff --git a/content/svg/content/src/SVGAElement.h b/content/svg/content/src/SVGAElement.h index 29728f0c21f5..23351b57e211 100644 --- a/content/svg/content/src/SVGAElement.h +++ b/content/svg/content/src/SVGAElement.h @@ -30,6 +30,7 @@ protected: public: NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAElement, SVGAElementBase) // nsINode interface methods virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor) MOZ_OVERRIDE; diff --git a/dom/base/URL.cpp b/dom/base/URL.cpp index acf7bdfd7a87..9dd1d6041844 100644 --- a/dom/base/URL.cpp +++ b/dom/base/URL.cpp @@ -20,6 +20,15 @@ namespace mozilla { namespace dom { +NS_IMPL_CYCLE_COLLECTION_1(URL, mSearchParams) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(URL) +NS_IMPL_CYCLE_COLLECTING_RELEASE(URL) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + URL::URL(nsIURI* aURI) : mURI(aURI) { @@ -212,6 +221,10 @@ URL::SetHref(const nsAString& aHref, ErrorResult& aRv) } aRv = mURI->SetSpec(href); + + if (mSearchParams) { + mSearchParams->Invalidate(); + } } void @@ -288,6 +301,33 @@ URL::SetHost(const nsAString& aHost) mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost)); } +void +URL::URLSearchParamsUpdated() +{ + MOZ_ASSERT(mSearchParams && mSearchParams->IsValid()); + + nsAutoString search; + mSearchParams->Serialize(search); + SetSearchInternal(search); +} + +void +URL::URLSearchParamsNeedsUpdates() +{ + MOZ_ASSERT(mSearchParams); + + nsAutoCString search; + nsCOMPtr url(do_QueryInterface(mURI)); + if (url) { + nsresult rv = url->GetQuery(search); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get the query from a nsIURL."); + } + } + + mSearchParams->ParseInput(search); +} + void URL::GetHostname(nsString& aHostname) const { @@ -384,6 +424,16 @@ URL::GetSearch(nsString& aSearch) const void URL::SetSearch(const nsAString& aSearch) +{ + SetSearchInternal(aSearch); + + if (mSearchParams) { + mSearchParams->Invalidate(); + } +} + +void +URL::SetSearchInternal(const nsAString& aSearch) { nsCOMPtr url(do_QueryInterface(mURI)); if (!url) { @@ -394,6 +444,35 @@ URL::SetSearch(const nsAString& aSearch) url->SetQuery(NS_ConvertUTF16toUTF8(aSearch)); } +URLSearchParams* +URL::GetSearchParams() +{ + CreateSearchParamsIfNeeded(); + return mSearchParams; +} + +void +URL::SetSearchParams(URLSearchParams* aSearchParams) +{ + if (!aSearchParams) { + return; + } + + if (!aSearchParams->HasURLAssociated()) { + MOZ_ASSERT(aSearchParams->IsValid()); + + mSearchParams = aSearchParams; + mSearchParams->SetObserver(this); + } else { + CreateSearchParamsIfNeeded(); + mSearchParams->CopyFromURLSearchParams(*aSearchParams); + } + + nsAutoString search; + mSearchParams->Serialize(search); + SetSearchInternal(search); +} + void URL::GetHash(nsString& aHash) const { @@ -422,5 +501,15 @@ bool IsChromeURI(nsIURI* aURI) return false; } +void +URL::CreateSearchParamsIfNeeded() +{ + if (!mSearchParams) { + mSearchParams = new URLSearchParams(); + mSearchParams->SetObserver(this); + mSearchParams->Invalidate(); + } +} + } } diff --git a/dom/base/URL.h b/dom/base/URL.h index 762f4e292e67..76a715e64919 100644 --- a/dom/base/URL.h +++ b/dom/base/URL.h @@ -6,6 +6,7 @@ #define URL_h___ #include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/URLSearchParams.h" #include "nsCycleCollectionParticipant.h" #include "nsAutoPtr.h" #include "nsString.h" @@ -29,10 +30,11 @@ namespace workers { class URLProxy; } -class URL MOZ_FINAL +class URL MOZ_FINAL : public URLSearchParamsObserver { public: - NS_INLINE_DECL_REFCOUNTING(URL) + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(URL) URL(nsIURI* aURI); @@ -108,16 +110,28 @@ public: void SetSearch(const nsAString& aArg); + URLSearchParams* GetSearchParams(); + + void SetSearchParams(URLSearchParams* aSearchParams); + void GetHash(nsString& aRetval) const; void SetHash(const nsAString& aArg); + // URLSearchParamsObserver + void URLSearchParamsUpdated() MOZ_OVERRIDE; + void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE; + private: nsIURI* GetURI() const { return mURI; } + void CreateSearchParamsIfNeeded(); + + void SetSearchInternal(const nsAString& aSearch); + static void CreateObjectURLInternal(const GlobalObject& aGlobal, nsISupports* aObject, const nsACString& aScheme, @@ -126,6 +140,7 @@ private: ErrorResult& aError); nsCOMPtr mURI; + nsRefPtr mSearchParams; friend class mozilla::dom::workers::URLProxy; }; diff --git a/dom/base/URLSearchParams.cpp b/dom/base/URLSearchParams.cpp index af2f09371222..b182250cb67e 100644 --- a/dom/base/URLSearchParams.cpp +++ b/dom/base/URLSearchParams.cpp @@ -9,8 +9,19 @@ namespace mozilla { namespace dom { +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(URLSearchParams, mObserver) +NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams) +NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + URLSearchParams::URLSearchParams() + : mValid(false) { + SetIsDOMBinding(); } URLSearchParams::~URLSearchParams() @@ -30,7 +41,7 @@ URLSearchParams::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { nsRefPtr sp = new URLSearchParams(); - sp->ParseInput(aInit); + sp->ParseInput(NS_ConvertUTF16toUTF8(aInit)); return sp.forget(); } @@ -41,19 +52,23 @@ URLSearchParams::Constructor(const GlobalObject& aGlobal, { nsRefPtr sp = new URLSearchParams(); aInit.mSearchParams.EnumerateRead(CopyEnumerator, sp); + sp->mValid = true; return sp.forget(); } void -URLSearchParams::ParseInput(const nsAString& aInput) +URLSearchParams::ParseInput(const nsACString& aInput) { - nsAString::const_iterator start, end; + // Remove all the existing data before parsing a new input. + DeleteAll(); + + nsACString::const_iterator start, end; aInput.BeginReading(start); aInput.EndReading(end); - nsAString::const_iterator iter(start); + nsACString::const_iterator iter(start); while (start != end) { - nsAutoString string; + nsAutoCString string; if (FindCharInReadable('&', iter, end)) { string.Assign(Substring(start, iter)); @@ -67,13 +82,13 @@ URLSearchParams::ParseInput(const nsAString& aInput) continue; } - nsAString::const_iterator eqStart, eqEnd; + nsACString::const_iterator eqStart, eqEnd; string.BeginReading(eqStart); string.EndReading(eqEnd); - nsAString::const_iterator eqIter(eqStart); + nsACString::const_iterator eqIter(eqStart); - nsAutoString name; - nsAutoString value; + nsAutoCString name; + nsAutoCString value; if (FindCharInReadable('=', eqIter, eqEnd)) { name.Assign(Substring(eqStart, eqIter)); @@ -84,20 +99,23 @@ URLSearchParams::ParseInput(const nsAString& aInput) name.Assign(string); } - nsAutoString decodedName; + nsAutoCString decodedName; DecodeString(name, decodedName); - nsAutoString decodedValue; + nsAutoCString decodedValue; DecodeString(value, decodedValue); - Append(decodedName, decodedValue); + AppendInternal(NS_ConvertUTF8toUTF16(decodedName), + NS_ConvertUTF8toUTF16(decodedValue)); } + + mValid = true; } void -URLSearchParams::DecodeString(const nsAString& aInput, nsAString& aOutput) +URLSearchParams::DecodeString(const nsACString& aInput, nsACString& aOutput) { - nsAString::const_iterator start, end; + nsACString::const_iterator start, end; aInput.BeginReading(start); aInput.EndReading(end); @@ -111,10 +129,10 @@ URLSearchParams::DecodeString(const nsAString& aInput, nsAString& aOutput) // Percent decode algorithm if (*start == '%') { - nsAString::const_iterator first(start); + nsACString::const_iterator first(start); ++first; - nsAString::const_iterator second(first); + nsACString::const_iterator second(first); ++second; #define ASCII_HEX_DIGIT( x ) \ @@ -147,6 +165,18 @@ URLSearchParams::DecodeString(const nsAString& aInput, nsAString& aOutput) } } +void +URLSearchParams::CopyFromURLSearchParams(URLSearchParams& aSearchParams) +{ + // The other SearchParams must be valid before copying its data. + aSearchParams.Validate(); + + // Remove all the existing data before parsing a new input. + DeleteAll(); + aSearchParams.mSearchParams.EnumerateRead(CopyEnumerator, this); + mValid = true; +} + /* static */ PLDHashOperator URLSearchParams::CopyEnumerator(const nsAString& aName, nsTArray* aArray, @@ -161,9 +191,28 @@ URLSearchParams::CopyEnumerator(const nsAString& aName, return PL_DHASH_NEXT; } +void +URLSearchParams::SetObserver(URLSearchParamsObserver* aObserver) +{ + MOZ_ASSERT(!mObserver); + mObserver = aObserver; +} + +void +URLSearchParams::Validate() +{ + MOZ_ASSERT(mValid || mObserver); + if (!mValid) { + mObserver->URLSearchParamsNeedsUpdates(); + MOZ_ASSERT(mValid); + } +} + void URLSearchParams::Get(const nsAString& aName, nsString& aRetval) { + Validate(); + nsTArray* array; if (!mSearchParams.Get(aName, &array)) { aRetval.Truncate(); @@ -176,6 +225,8 @@ URLSearchParams::Get(const nsAString& aName, nsString& aRetval) void URLSearchParams::GetAll(const nsAString& aName, nsTArray& aRetval) { + Validate(); + nsTArray* array; if (!mSearchParams.Get(aName, &array)) { return; @@ -187,6 +238,10 @@ URLSearchParams::GetAll(const nsAString& aName, nsTArray& aRetval) void URLSearchParams::Set(const nsAString& aName, const nsAString& aValue) { + // Before setting any new value we have to be sure to have all the previous + // values in place. + Validate(); + nsTArray* array; if (!mSearchParams.Get(aName, &array)) { array = new nsTArray(); @@ -195,10 +250,23 @@ URLSearchParams::Set(const nsAString& aName, const nsAString& aValue) } else { array->ElementAt(0) = aValue; } + + NotifyObserver(); } void URLSearchParams::Append(const nsAString& aName, const nsAString& aValue) +{ + // Before setting any new value we have to be sure to have all the previous + // values in place. + Validate(); + + AppendInternal(aName, aValue); + NotifyObserver(); +} + +void +URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue) { nsTArray* array; if (!mSearchParams.Get(aName, &array)) { @@ -212,18 +280,32 @@ URLSearchParams::Append(const nsAString& aName, const nsAString& aValue) bool URLSearchParams::Has(const nsAString& aName) { + Validate(); return mSearchParams.Get(aName, nullptr); } void URLSearchParams::Delete(const nsAString& aName) { + // Before deleting any value we have to be sure to have all the previous + // values in place. + Validate(); + nsTArray* array; if (!mSearchParams.Get(aName, &array)) { return; } mSearchParams.Remove(aName); + + NotifyObserver(); +} + +uint32_t +URLSearchParams::Size() +{ + Validate(); + return mSearchParams.Count(); } void @@ -232,5 +314,84 @@ URLSearchParams::DeleteAll() mSearchParams.Clear(); } +class MOZ_STACK_CLASS SerializeData +{ +public: + SerializeData() + : mFirst(true) + {} + + nsAutoString mValue; + bool mFirst; + + void Serialize(const nsCString& aInput) + { + const unsigned char* p = (const unsigned char*) aInput.get(); + + while (p && *p) { + // ' ' to '+' + if (*p == 0x20) { + mValue.Append(0x2B); + // Percent Encode algorithm + } else if (*p == 0x2A || *p == 0x2D || *p == 0x2E || + (*p >= 0x30 && *p <= 0x39) || + (*p >= 0x41 && *p <= 0x5A) || *p == 0x5F || + (*p >= 0x61 && *p <= 0x7A)) { + mValue.Append(*p); + } else { + mValue.AppendPrintf("%%%X", *p); + } + + ++p; + } + } +}; + +void +URLSearchParams::Serialize(nsAString& aValue) +{ + MOZ_ASSERT(mValid); + + SerializeData data; + mSearchParams.EnumerateRead(SerializeEnumerator, &data); + aValue.Assign(data.mValue); +} + +/* static */ PLDHashOperator +URLSearchParams::SerializeEnumerator(const nsAString& aName, + nsTArray* aArray, + void *userData) +{ + SerializeData* data = static_cast(userData); + + for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) { + if (data->mFirst) { + data->mFirst = false; + } else { + data->mValue.Append(NS_LITERAL_STRING("&")); + } + + data->Serialize(NS_ConvertUTF16toUTF8(aName)); + data->mValue.Append(NS_LITERAL_STRING("=")); + data->Serialize(NS_ConvertUTF16toUTF8(aArray->ElementAt(i))); + } + + return PL_DHASH_NEXT; +} + +void +URLSearchParams::NotifyObserver() +{ + if (mObserver) { + mObserver->URLSearchParamsUpdated(); + } +} + +void +URLSearchParams::Invalidate() +{ + mValid = false; +} + } // namespace dom } // namespace mozilla diff --git a/dom/base/URLSearchParams.h b/dom/base/URLSearchParams.h index 90c3d579a94f..c876085fa237 100644 --- a/dom/base/URLSearchParams.h +++ b/dom/base/URLSearchParams.h @@ -8,28 +8,47 @@ #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/ErrorResult.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" +#include "nsISupports.h" namespace mozilla { namespace dom { -class URLSearchParams MOZ_FINAL +class URLSearchParamsObserver : public nsISupports { public: - NS_INLINE_DECL_REFCOUNTING(URL) + virtual ~URLSearchParamsObserver() {} + + virtual void URLSearchParamsUpdated() = 0; + virtual void URLSearchParamsNeedsUpdates() = 0; +}; + +class URLSearchParams MOZ_FINAL : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URLSearchParams) URLSearchParams(); ~URLSearchParams(); + bool HasURLAssociated() const + { + return !!mObserver; + } + // WebIDL methods nsISupports* GetParentObject() const { return nullptr; } - JSObject* - WrapObject(JSContext* aCx, JS::Handle aScope); + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; static already_AddRefed Constructor(const GlobalObject& aGlobal, const nsAString& aInit, @@ -39,6 +58,21 @@ public: Constructor(const GlobalObject& aGlobal, URLSearchParams& aInit, ErrorResult& aRv); + void ParseInput(const nsACString& aInput); + + void CopyFromURLSearchParams(URLSearchParams& aSearchParams); + + void SetObserver(URLSearchParamsObserver* aObserver); + + void Invalidate(); + + bool IsValid() const + { + return mValid; + } + + void Serialize(nsAString& aValue); + void Get(const nsAString& aName, nsString& aRetval); void GetAll(const nsAString& aName, nsTArray& aRetval); @@ -51,23 +85,33 @@ public: void Delete(const nsAString& aName); - uint32_t Size() const - { - return mSearchParams.Count(); - } + uint32_t Size(); private: - void ParseInput(const nsAString& aInput); + void AppendInternal(const nsAString& aName, const nsAString& aValue); void DeleteAll(); - void DecodeString(const nsAString& aInput, nsAString& aOutput); + void DecodeString(const nsACString& aInput, nsACString& aOutput); + + void NotifyObserver(); static PLDHashOperator CopyEnumerator(const nsAString& aName, nsTArray* aArray, void *userData); + static PLDHashOperator + SerializeEnumerator(const nsAString& aName, nsTArray* aArray, + void *userData); + + void + Validate(); + nsClassHashtable> mSearchParams; + + nsRefPtr mObserver; + + bool mValid; }; } // namespace dom diff --git a/dom/base/test/test_urlSearchParams.html b/dom/base/test/test_urlSearchParams.html index a6a4c18f563e..272d01c6521a 100644 --- a/dom/base/test/test_urlSearchParams.html +++ b/dom/base/test/test_urlSearchParams.html @@ -19,7 +19,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
 
- + diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 8603e0f2f360..333d277fd9c3 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1287,14 +1287,8 @@ DOMInterfaces = { { 'workers': True, 'wrapperCache': False, - 'nativeOwnership': 'owned', }], -'URLSearchParams' : { - 'wrapperCache' : False, - 'nativeOwnership': 'refcounted', -}, - 'VTTCue': { 'nativeType': 'mozilla::dom::TextTrackCue' }, diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index a55b12916683..f91b427f4114 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -614,6 +614,7 @@ var interfaceNamesInGlobalScope = "UIEvent", "UndoManager", "URL", + "URLSearchParams", {name: "UserDataHandler", xbl: true}, "UserProximityEvent", {name: "USSDReceivedEvent", b2g: true, pref: "dom.mobileconnection.enabled"}, diff --git a/dom/webidl/URLUtils.webidl b/dom/webidl/URLUtils.webidl index ddd690ccef05..033a6fca9291 100644 --- a/dom/webidl/URLUtils.webidl +++ b/dom/webidl/URLUtils.webidl @@ -27,7 +27,7 @@ interface URLUtils { attribute DOMString port; attribute DOMString pathname; attribute DOMString search; - // attribute URLSearchParams? searchParams; + attribute URLSearchParams? searchParams; attribute DOMString hash; }; diff --git a/dom/workers/RegisterBindings.cpp b/dom/workers/RegisterBindings.cpp index a40dc6b100ae..aa6e7c5ea320 100644 --- a/dom/workers/RegisterBindings.cpp +++ b/dom/workers/RegisterBindings.cpp @@ -71,7 +71,7 @@ WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle aGlobal) !XMLHttpRequestBinding_workers::GetConstructorObject(aCx, aGlobal) || !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, aGlobal) || !URLBinding_workers::GetConstructorObject(aCx, aGlobal) || - !URLSearchParamsBinding::GetConstructorObject(aCx, global) || + !URLSearchParamsBinding::GetConstructorObject(aCx, aGlobal) || !WorkerBinding::GetConstructorObject(aCx, aGlobal) || !WorkerLocationBinding_workers::GetConstructorObject(aCx, aGlobal) || !WorkerNavigatorBinding_workers::GetConstructorObject(aCx, aGlobal)) { diff --git a/dom/workers/URL.cpp b/dom/workers/URL.cpp index e2f0cb21040f..d9b69ff67978 100644 --- a/dom/workers/URL.cpp +++ b/dom/workers/URL.cpp @@ -19,6 +19,7 @@ #include "mozilla/dom/URL.h" #include "mozilla/dom/URLBinding.h" +#include "mozilla/dom/URLSearchParams.h" #include "nsIIOService.h" #include "nsNetCID.h" @@ -536,6 +537,17 @@ private: mozilla::ErrorResult& mRv; }; +NS_IMPL_CYCLE_COLLECTION_1(URL, mSearchParams) + +// The reason for using worker::URL is to have different refcnt logging than +// for main thread URL. +NS_IMPL_CYCLE_COLLECTING_ADDREF(workers::URL) +NS_IMPL_CYCLE_COLLECTING_RELEASE(workers::URL) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + // static URL* URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, @@ -606,10 +618,9 @@ URL::~URL() } JSObject* -URL::WrapObject(JSContext* aCx, JS::Handle aScope, - bool* aTookOwnership) +URL::WrapObject(JSContext* aCx, JS::Handle aScope) { - return URLBinding_workers::Wrap(aCx, aScope, this, aTookOwnership); + return URLBinding_workers::Wrap(aCx, aScope, this); } void @@ -634,6 +645,10 @@ URL::SetHref(const nsAString& aHref, ErrorResult& aRv) if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) { JS_ReportPendingException(mWorkerPrivate->GetJSContext()); } + + if (mSearchParams) { + mSearchParams->Invalidate(); + } } void @@ -837,6 +852,16 @@ URL::GetSearch(nsString& aSearch) const void URL::SetSearch(const nsAString& aSearch) +{ + SetSearchInternal(aSearch); + + if (mSearchParams) { + mSearchParams->Invalidate(); + } +} + +void +URL::SetSearchInternal(const nsAString& aSearch) { ErrorResult rv; nsRefPtr runnable = @@ -848,6 +873,36 @@ URL::SetSearch(const nsAString& aSearch) } } +mozilla::dom::URLSearchParams* +URL::GetSearchParams() +{ + CreateSearchParamsIfNeeded(); + return mSearchParams; +} + +void +URL::SetSearchParams(URLSearchParams* aSearchParams) +{ + if (!aSearchParams) { + return; + } + + if (!aSearchParams->HasURLAssociated()) { + MOZ_ASSERT(aSearchParams->IsValid()); + + mSearchParams = aSearchParams; + mSearchParams->SetObserver(this); + } else { + CreateSearchParamsIfNeeded(); + mSearchParams->CopyFromURLSearchParams(*aSearchParams); + } + + + nsString search; + mSearchParams->Serialize(search); + SetSearchInternal(search); +} + void URL::GetHash(nsString& aHash) const { @@ -924,4 +979,34 @@ URL::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl) } } +void +URL::URLSearchParamsUpdated() +{ + MOZ_ASSERT(mSearchParams && mSearchParams->IsValid()); + + nsString search; + mSearchParams->Serialize(search); + SetSearchInternal(search); +} + +void +URL::URLSearchParamsNeedsUpdates() +{ + MOZ_ASSERT(mSearchParams); + + nsString search; + GetSearch(search); + mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1))); +} + +void +URL::CreateSearchParamsIfNeeded() +{ + if (!mSearchParams) { + mSearchParams = new URLSearchParams(); + mSearchParams->SetObserver(this); + mSearchParams->Invalidate(); + } +} + END_WORKERS_NAMESPACE diff --git a/dom/workers/URL.h b/dom/workers/URL.h index 57af9f20c512..922c6c837e13 100644 --- a/dom/workers/URL.h +++ b/dom/workers/URL.h @@ -11,7 +11,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/BindingDeclarations.h" -#include "mozilla/dom/NonRefcountedDOMObject.h" +#include "mozilla/dom/URLSearchParams.h" namespace mozilla { namespace dom { @@ -23,9 +23,13 @@ BEGIN_WORKERS_NAMESPACE class URLProxy; -class URL MOZ_FINAL : public NonRefcountedDOMObject +class URL MOZ_FINAL : public mozilla::dom::URLSearchParamsObserver { + typedef mozilla::dom::URLSearchParams URLSearchParams; + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(URL) URL(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy); ~URL(); @@ -38,8 +42,7 @@ public: } JSObject* - WrapObject(JSContext* aCx, JS::Handle aScope, - bool* aTookOwnership); + WrapObject(JSContext* aCx, JS::Handle aScope); // Methods for WebIDL @@ -101,18 +104,31 @@ public: void SetSearch(const nsAString& aSearch); + URLSearchParams* GetSearchParams(); + + void SetSearchParams(URLSearchParams* aSearchParams); + void GetHash(nsString& aHost) const; void SetHash(const nsAString& aHash); + // IURLSearchParamsObserver + void URLSearchParamsUpdated() MOZ_OVERRIDE; + void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE; + private: URLProxy* GetURLProxy() const { return mURLProxy; } + void CreateSearchParamsIfNeeded(); + + void SetSearchInternal(const nsAString& aSearch); + WorkerPrivate* mWorkerPrivate; nsRefPtr mURLProxy; + nsRefPtr mSearchParams; }; END_WORKERS_NAMESPACE diff --git a/dom/workers/test/urlSearchparams_worker.js b/dom/workers/test/urlSearchParams_worker.js similarity index 64% rename from dom/workers/test/urlSearchparams_worker.js rename to dom/workers/test/urlSearchParams_worker.js index bec0ea83fb37..73f1d61ecac0 100644 --- a/dom/workers/test/urlSearchparams_worker.js +++ b/dom/workers/test/urlSearchParams_worker.js @@ -8,6 +8,11 @@ function is(a, b, msg) { postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); } +function isnot(a, b, msg) { + dump("ISNOT: " + (a!==b) + " => " + a + " | " + b + " " + msg + "\n"); + postMessage({type: 'status', status: a !== b, msg: a + " !== " + b + ": " + msg }); +} + onmessage = function() { status = false; try { @@ -111,10 +116,66 @@ onmessage = function() { runTest(); } + function testURL() { + var url = new URL('http://www.example.net?a=b&c=d'); + ok(url.searchParams, "URL searchParams exists!"); + ok(url.searchParams.has('a'), "URL.searchParams.has('a')"); + is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')"); + ok(url.searchParams.has('c'), "URL.searchParams.has('c')"); + is(url.searchParams.get('c'), 'd', "URL.searchParams.get('c')"); + + url.searchParams.set('e', 'f'); + ok(url.href.indexOf('e=f') != 1, 'URL right'); + + var u = new URLSearchParams(); + u.append('foo', 'bar'); + url.searchParams = u; + is(url.searchParams, u, "URL.searchParams is the same object"); + is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')"); + is(url.href, 'http://www.example.net/?foo=bar', 'URL right'); + + url.searchParams = null; + is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')"); + is(url.href, 'http://www.example.net/?foo=bar', 'URL right'); + + var url2 = new URL('http://www.example.net?e=f'); + url.searchParams = url2.searchParams; + isnot(url.searchParams, url2.searchParams, "URL.searchParams is not the same object"); + is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')"); + + url.href = "http://www.example.net?bar=foo"; + is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')"); + + runTest(); + } + + function testEncoding() { + var encoding = [ [ '1', '1' ], + [ 'a b', 'a+b' ], + [ '<>', '%3C%3E' ], + [ '\u0541', '%D5%81'] ]; + + for (var i = 0; i < encoding.length; ++i) { + var a = new URLSearchParams(); + a.set('a', encoding[i][0]); + + var url = new URL('http://www.example.net'); + url.searchParams = a; + is(url.href, 'http://www.example.net/?a=' + encoding[i][1]); + + var url2 = new URL(url.href); + is(url2.searchParams.get('a'), encoding[i][0], 'a is still there'); + } + + runTest(); + } + var tests = [ testSimpleURLSearchParams, testCopyURLSearchParams, - testParserURLSearchParams + testParserURLSearchParams, + testURL, + testEncoding ]; function runTest() { @@ -129,5 +190,3 @@ onmessage = function() { runTest(); } - - diff --git a/toolkit/components/places/tests/cpp/mock_Link.h b/toolkit/components/places/tests/cpp/mock_Link.h index 277fcfde1403..4a9713dc7a4d 100644 --- a/toolkit/components/places/tests/cpp/mock_Link.h +++ b/toolkit/components/places/tests/cpp/mock_Link.h @@ -13,6 +13,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/dom/Link.h" +#include "mozilla/dom/URLSearchParams.h" class mock_Link : public mozilla::dom::Link { @@ -115,6 +116,134 @@ Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const return 0; } +void +Link::URLSearchParamsUpdated() +{ + NS_NOTREACHED("Unexpected call to Link::URLSearchParamsUpdated"); +} + +void +Link::URLSearchParamsNeedsUpdates() +{ + NS_NOTREACHED("Unexpected call to Link::URLSearchParamsNeedsUpdates"); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(URLSearchParams) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URLSearchParams) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(URLSearchParams) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(URLSearchParams) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams) +NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + + +URLSearchParams::URLSearchParams() +{ +} + +URLSearchParams::~URLSearchParams() +{ +} + +JSObject* +URLSearchParams::WrapObject(JSContext* aCx, JS::Handle aScope) +{ + return nullptr; +} + +void +URLSearchParams::ParseInput(const nsACString& aInput) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::ParseInput"); +} + +void +URLSearchParams::CopyFromURLSearchParams(URLSearchParams& aSearchParams) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::CopyFromURLSearchParams"); +} + +void +URLSearchParams::SetObserver(URLSearchParamsObserver* aObserver) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::SetObserver"); +} + +void +URLSearchParams::Serialize(nsAString& aValue) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::Serialize"); +} + +void +URLSearchParams::Get(const nsAString& aName, nsString& aRetval) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::Get"); +} + +void +URLSearchParams::GetAll(const nsAString& aName, nsTArray& aRetval) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::GetAll"); +} + +void +URLSearchParams::Set(const nsAString& aName, const nsAString& aValue) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::Set"); +} + +void +URLSearchParams::Append(const nsAString& aName, const nsAString& aValue) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::Append"); +} + +void +URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::AppendInternal"); +} + +bool +URLSearchParams::Has(const nsAString& aName) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::Has"); + return false; +} + +void +URLSearchParams::Delete(const nsAString& aName) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::Delete"); +} + +void +URLSearchParams::DeleteAll() +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::DeleteAll"); +} + +void +URLSearchParams::NotifyObserver() +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObserver"); +} + +void +URLSearchParams::Invalidate() +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::Invalidate"); +} + + } // namespace dom } // namespace mozilla From cf5141986662c05a358bc188b7dd36ddbfb23fb0 Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Mon, 25 Nov 2013 14:28:06 -0500 Subject: [PATCH 040/459] bug 943023 - unify some of toolkit/components/url-classifier/ r=ehsan --- toolkit/components/url-classifier/moz.build | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/toolkit/components/url-classifier/moz.build b/toolkit/components/url-classifier/moz.build index cc8fd93105f4..d728ecda24c0 100644 --- a/toolkit/components/url-classifier/moz.build +++ b/toolkit/components/url-classifier/moz.build @@ -17,20 +17,28 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'url-classifier' -SOURCES += [ +UNIFIED_SOURCES += [ 'ChunkSet.cpp', 'Classifier.cpp', - 'HashStore.cpp', 'LookupCache.cpp', 'nsCheckSummedOutputStream.cpp', 'nsUrlClassifierDBService.cpp', - 'nsUrlClassifierPrefixSet.cpp', 'nsUrlClassifierProxies.cpp', - 'nsUrlClassifierStreamUpdater.cpp', 'nsUrlClassifierUtils.cpp', 'ProtocolParser.cpp', ] +# define conflicting LOG() macros +SOURCES += [ + 'nsUrlClassifierPrefixSet.cpp', + 'nsUrlClassifierStreamUpdater.cpp', +] + +# contains variables that conflict with LookupCache.cpp +SOURCES += [ + 'HashStore.cpp', +] + EXTRA_COMPONENTS += [ 'nsURLClassifier.manifest', 'nsUrlClassifierHashCompleter.js', From 0c6e1524c992c8fdbdc02cdeec131c007e3e6761 Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Thu, 5 Dec 2013 16:28:21 -0500 Subject: [PATCH 041/459] bug 946946 - add Document.docShell r=bz --- dom/bindings/Bindings.conf | 1 + dom/webidl/Document.webidl | 3 +++ 2 files changed, 4 insertions(+) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 333d277fd9c3..cb89c6176ea1 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1894,6 +1894,7 @@ addExternalIface('nsIInputStreamCallback', nativeType='nsIInputStreamCallback', headerFile='nsIAsyncInputStream.h') addExternalIface('nsIStreamListener', nativeType='nsIStreamListener', notflattened=True) addExternalIface('nsISupports', nativeType='nsISupports') +addExternalIface('nsIDocShell', nativeType='nsIDocShell', notflattened=True) addExternalIface('nsIEditor', nativeType='nsIEditor', notflattened=True) addExternalIface('nsIVariant', nativeType='nsIVariant', notflattened=True) addExternalIface('OutputStream', nativeType='nsIOutputStream', diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index d2949ba5852e..ebc541ae3043 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -19,6 +19,7 @@ interface StyleSheetList; interface WindowProxy; interface nsISupports; interface URI; +interface nsIDocShell; enum VisibilityState { "hidden", "visible" }; @@ -340,6 +341,8 @@ partial interface Document { void obsoleteSheet(URI sheetURI); [ChromeOnly, Throws] void obsoleteSheet(DOMString sheetURI); + + [ChromeOnly] readonly attribute nsIDocShell? docShell; }; // Extension to give chrome JS the ability to determine when a document was From ae517971ad06501cbe0b04f3e826b20103d03664 Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Wed, 20 Nov 2013 14:18:25 -0500 Subject: [PATCH 042/459] bug 947022 - type nsIPresShell::mForwardingContainer and nsPresContext::mContainer r=bz --- content/base/src/nsContentUtils.cpp | 3 +- content/base/src/nsDocument.cpp | 3 +- content/events/src/nsDOMUIEvent.cpp | 2 +- content/events/src/nsEventStateManager.cpp | 16 ++--- content/html/content/src/HTMLBodyElement.cpp | 68 +++++++++---------- content/smil/nsDOMTimeEvent.cpp | 2 +- content/xul/document/src/XULDocument.cpp | 7 +- dom/base/nsDOMWindowUtils.cpp | 3 +- dom/plugins/base/nsPluginInstanceOwner.cpp | 9 +-- .../src/nsComposerDocumentCommands.cpp | 12 +--- layout/base/nsCSSFrameConstructor.cpp | 31 ++++----- layout/base/nsDocumentViewer.cpp | 19 +++--- layout/base/nsIPresShell.h | 6 +- layout/base/nsPresContext.cpp | 55 ++++++++------- layout/base/nsPresContext.h | 21 +++--- layout/base/nsPresShell.cpp | 53 ++++++--------- layout/generic/nsGfxScrollFrame.cpp | 2 +- layout/mathml/nsMathMLmactionFrame.cpp | 21 +++--- layout/printing/nsPrintEngine.cpp | 3 +- layout/svg/nsSVGOuterSVGFrame.cpp | 2 +- layout/xul/nsMenuPopupFrame.cpp | 8 +-- layout/xul/nsResizerFrame.cpp | 3 +- layout/xul/nsTitleBarFrame.cpp | 5 +- layout/xul/nsXULPopupManager.cpp | 3 +- .../typeaheadfind/nsTypeAheadFind.cpp | 14 ++-- widget/xpwidgets/nsBaseWidget.cpp | 3 +- 26 files changed, 166 insertions(+), 208 deletions(-) diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index b9690cac6bba..2fc03a3528a3 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -6581,8 +6581,7 @@ nsContentUtils::GetSelectionInTextControl(Selection* aSelection, nsIEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) { - nsCOMPtr container = aPresContext->GetContainer(); - nsCOMPtr docShell(do_QueryInterface(container)); + nsCOMPtr docShell(aPresContext->GetDocShell()); bool isEditable; if (!docShell || NS_FAILED(docShell->GetEditable(&isEditable)) || !isEditable) diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index bf6c2119e09b..210e0613ad2d 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -6183,7 +6183,8 @@ nsDocument::DoNotifyPossibleTitleChange() nsCOMPtr shell = GetShell(); if (shell) { - nsCOMPtr container = shell->GetPresContext()->GetContainer(); + nsCOMPtr container = + shell->GetPresContext()->GetContainerWeak(); if (container) { nsCOMPtr docShellWin = do_QueryInterface(container); if (docShellWin) { diff --git a/content/events/src/nsDOMUIEvent.cpp b/content/events/src/nsDOMUIEvent.cpp index 59882d559b9f..5c5e895b0772 100644 --- a/content/events/src/nsDOMUIEvent.cpp +++ b/content/events/src/nsDOMUIEvent.cpp @@ -63,7 +63,7 @@ nsDOMUIEvent::nsDOMUIEvent(mozilla::dom::EventTarget* aOwner, mView = nullptr; if (mPresContext) { - nsCOMPtr container = mPresContext->GetContainer(); + nsISupports* container = mPresContext->GetContainerWeak(); if (container) { nsCOMPtr window = do_GetInterface(container); diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index 67edf1722fe3..0466d203e1db 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -1455,7 +1455,7 @@ nsEventStateManager::GetAccessKeyLabelPrefix(nsAString& aPrefix) nsAutoString separator, modifierText; nsContentUtils::GetModifierSeparatorText(separator); - nsCOMPtr container = mPresContext->GetContainer(); + nsCOMPtr container = mPresContext->GetContainerWeak(); int32_t modifierMask = GetAccessModifierMaskFor(container); if (modifierMask & NS_MODIFIER_CONTROL) { @@ -1489,7 +1489,7 @@ nsEventStateManager::HandleAccessKey(nsPresContext* aPresContext, ProcessingAccessKeyState aAccessKeyState, int32_t aModifierMask) { - nsCOMPtr pcContainer = aPresContext->GetContainer(); + nsCOMPtr pcContainer = aPresContext->GetContainerWeak(); // Alt or other accesskey modifier is down, we may need to do an accesskey if (mAccessKeys.Count() > 0 && @@ -2204,7 +2204,7 @@ nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext, { *aTargetNode = nullptr; - nsCOMPtr container = aPresContext->GetContainer(); + nsCOMPtr container = aPresContext->GetContainerWeak(); nsCOMPtr window = do_GetInterface(container); if (!window) return; @@ -2423,10 +2423,7 @@ nsEventStateManager::GetMarkupDocumentViewer(nsIMarkupDocumentViewer** aMv) nsPresContext *presContext = presShell->GetPresContext(); if(!presContext) return NS_ERROR_FAILURE; - nsCOMPtr pcContainer = presContext->GetContainer(); - if(!pcContainer) return NS_ERROR_FAILURE; - - nsCOMPtr docshell(do_QueryInterface(pcContainer)); + nsCOMPtr docshell(presContext->GetDocShell()); if(!docshell) return NS_ERROR_FAILURE; nsCOMPtr cv; @@ -2487,7 +2484,7 @@ nsEventStateManager::ChangeFullZoom(int32_t change) void nsEventStateManager::DoScrollHistory(int32_t direction) { - nsCOMPtr pcContainer(mPresContext->GetContainer()); + nsCOMPtr pcContainer(mPresContext->GetContainerWeak()); if (pcContainer) { nsCOMPtr webNav(do_QueryInterface(pcContainer)); if (webNav) { @@ -3787,8 +3784,7 @@ nsEventStateManager::UpdateCursor(nsPresContext* aPresContext, if (Preferences::GetBool("ui.use_activity_cursor", false)) { // Check whether or not to show the busy cursor - nsCOMPtr pcContainer = aPresContext->GetContainer(); - nsCOMPtr docShell(do_QueryInterface(pcContainer)); + nsCOMPtr docShell(aPresContext->GetDocShell()); if (!docShell) return; uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE; docShell->GetBusyFlags(&busyFlags); diff --git a/content/html/content/src/HTMLBodyElement.cpp b/content/html/content/src/HTMLBodyElement.cpp index 9f17f945eb01..389cdd3d39d7 100644 --- a/content/html/content/src/HTMLBodyElement.cpp +++ b/content/html/content/src/HTMLBodyElement.cpp @@ -130,44 +130,41 @@ BodyRule::MapRuleInfoInto(nsRuleData* aData) // if marginwidth or marginheight is set in the and not set in the // reflect them as margin in the if (bodyMarginWidth == -1 || bodyMarginHeight == -1) { - nsCOMPtr container = aData->mPresContext->GetContainer(); - if (container) { - nsCOMPtr docShell(do_QueryInterface(container)); - if (docShell) { - nscoord frameMarginWidth=-1; // default value - nscoord frameMarginHeight=-1; // default value - docShell->GetMarginWidth(&frameMarginWidth); // -1 indicates not set - docShell->GetMarginHeight(&frameMarginHeight); - if ((frameMarginWidth >= 0) && (bodyMarginWidth == -1)) { // set in & not in - if (eCompatibility_NavQuirks == mode) { - if ((bodyMarginHeight == -1) && (0 > frameMarginHeight)) // nav quirk - frameMarginHeight = 0; - } + nsCOMPtr docShell(aData->mPresContext->GetDocShell()); + if (docShell) { + nscoord frameMarginWidth=-1; // default value + nscoord frameMarginHeight=-1; // default value + docShell->GetMarginWidth(&frameMarginWidth); // -1 indicates not set + docShell->GetMarginHeight(&frameMarginHeight); + if ((frameMarginWidth >= 0) && (bodyMarginWidth == -1)) { // set in & not in + if (eCompatibility_NavQuirks == mode) { + if ((bodyMarginHeight == -1) && (0 > frameMarginHeight)) // nav quirk + frameMarginHeight = 0; } - if ((frameMarginHeight >= 0) && (bodyMarginHeight == -1)) { // set in & not in - if (eCompatibility_NavQuirks == mode) { - if ((bodyMarginWidth == -1) && (0 > frameMarginWidth)) // nav quirk - frameMarginWidth = 0; - } + } + if ((frameMarginHeight >= 0) && (bodyMarginHeight == -1)) { // set in & not in + if (eCompatibility_NavQuirks == mode) { + if ((bodyMarginWidth == -1) && (0 > frameMarginWidth)) // nav quirk + frameMarginWidth = 0; } + } - if ((bodyMarginWidth == -1) && (frameMarginWidth >= 0)) { - nsCSSValue* marginLeft = aData->ValueForMarginLeftValue(); - if (marginLeft->GetUnit() == eCSSUnit_Null) - marginLeft->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel); - nsCSSValue* marginRight = aData->ValueForMarginRightValue(); - if (marginRight->GetUnit() == eCSSUnit_Null) - marginRight->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel); - } + if ((bodyMarginWidth == -1) && (frameMarginWidth >= 0)) { + nsCSSValue* marginLeft = aData->ValueForMarginLeftValue(); + if (marginLeft->GetUnit() == eCSSUnit_Null) + marginLeft->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel); + nsCSSValue* marginRight = aData->ValueForMarginRightValue(); + if (marginRight->GetUnit() == eCSSUnit_Null) + marginRight->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel); + } - if ((bodyMarginHeight == -1) && (frameMarginHeight >= 0)) { - nsCSSValue* marginTop = aData->ValueForMarginTop(); - if (marginTop->GetUnit() == eCSSUnit_Null) - marginTop->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel); - nsCSSValue* marginBottom = aData->ValueForMarginBottom(); - if (marginBottom->GetUnit() == eCSSUnit_Null) - marginBottom->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel); - } + if ((bodyMarginHeight == -1) && (frameMarginHeight >= 0)) { + nsCSSValue* marginTop = aData->ValueForMarginTop(); + if (marginTop->GetUnit() == eCSSUnit_Null) + marginTop->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel); + nsCSSValue* marginBottom = aData->ValueForMarginBottom(); + if (marginBottom->GetUnit() == eCSSUnit_Null) + marginBottom->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel); } } } @@ -462,8 +459,7 @@ HTMLBodyElement::GetAssociatedEditor() return nullptr; } - nsCOMPtr container = presContext->GetContainer(); - nsCOMPtr docShell = do_QueryInterface(container); + nsCOMPtr docShell = presContext->GetDocShell(); if (!docShell) { return nullptr; } diff --git a/content/smil/nsDOMTimeEvent.cpp b/content/smil/nsDOMTimeEvent.cpp index a6469d482577..6994343a5d56 100644 --- a/content/smil/nsDOMTimeEvent.cpp +++ b/content/smil/nsDOMTimeEvent.cpp @@ -33,7 +33,7 @@ nsDOMTimeEvent::nsDOMTimeEvent(mozilla::dom::EventTarget* aOwner, mEvent->mFlags.mCancelable = false; if (mPresContext) { - nsCOMPtr container = mPresContext->GetContainer(); + nsISupports* container = mPresContext->GetContainerWeak(); if (container) { nsCOMPtr window = do_GetInterface(container); if (window) { diff --git a/content/xul/document/src/XULDocument.cpp b/content/xul/document/src/XULDocument.cpp index baad170ec961..be5f8fc7ec5e 100644 --- a/content/xul/document/src/XULDocument.cpp +++ b/content/xul/document/src/XULDocument.cpp @@ -2042,12 +2042,7 @@ XULDocument::StartLayout(void) if (! cx) return NS_ERROR_UNEXPECTED; - nsCOMPtr container = cx->GetContainer(); - NS_ASSERTION(container != nullptr, "pres context has no container"); - if (! container) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr docShell(do_QueryInterface(container)); + nsCOMPtr docShell = cx->GetDocShell(); NS_ASSERTION(docShell != nullptr, "container is not a docshell"); if (! docShell) return NS_ERROR_UNEXPECTED; diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 7edc40bf2dbd..698c9de79ba3 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -325,8 +325,7 @@ MaybeReflowForInflationScreenWidthChange(nsPresContext *aPresContext) changed = changed || (fontInflationWasEnabled != presShell->FontSizeInflationEnabled()); if (changed) { - nsCOMPtr container = aPresContext->GetContainer(); - nsCOMPtr docShell = do_QueryInterface(container); + nsCOMPtr docShell = aPresContext->GetDocShell(); if (docShell) { nsCOMPtr cv; docShell->GetContentViewer(getter_AddRefs(cv)); diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index f20c42c60fd6..cb5974fe7358 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -498,7 +498,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL, } // the container of the pres context will give us the link handler - nsCOMPtr container = presContext->GetContainer(); + nsCOMPtr container = presContext->GetContainerWeak(); NS_ENSURE_TRUE(container,NS_ERROR_FAILURE); nsCOMPtr lh = do_QueryInterface(container); NS_ENSURE_TRUE(lh, NS_ERROR_FAILURE); @@ -554,12 +554,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const PRUnichar *aStatusMsg) if (!mObjectFrame) { return rv; } - nsCOMPtr cont = mObjectFrame->PresContext()->GetContainer(); - if (!cont) { - return NS_OK; - } - - nsCOMPtr docShellItem(do_QueryInterface(cont, &rv)); + nsCOMPtr docShellItem = mObjectFrame->PresContext()->GetDocShell(); if (NS_FAILED(rv) || !docShellItem) { return rv; } diff --git a/editor/composer/src/nsComposerDocumentCommands.cpp b/editor/composer/src/nsComposerDocumentCommands.cpp index b74179b58a46..f45826372ea9 100644 --- a/editor/composer/src/nsComposerDocumentCommands.cpp +++ b/editor/composer/src/nsComposerDocumentCommands.cpp @@ -105,11 +105,7 @@ nsSetDocumentOptionsCommand::DoCommandParams(const char *aCommandName, rv = aParams->GetBooleanValue("plugins", &allowPlugins); if (NS_SUCCEEDED(rv)) { - nsCOMPtr container = presContext->GetContainer(); - NS_ENSURE_TRUE(container, NS_ERROR_FAILURE); - - nsCOMPtr docShell(do_QueryInterface(container, &rv)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr docShell(presContext->GetDocShell()); NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); rv = docShell->SetAllowPlugins(allowPlugins); @@ -158,11 +154,7 @@ nsSetDocumentOptionsCommand::GetCommandStateParams(const char *aCommandName, rv = aParams->GetBooleanValue("plugins", &allowPlugins); if (NS_SUCCEEDED(rv)) { - nsCOMPtr container = presContext->GetContainer(); - NS_ENSURE_TRUE(container, NS_ERROR_FAILURE); - - nsCOMPtr docShell(do_QueryInterface(container, &rv)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr docShell(presContext->GetDocShell()); NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); allowPlugins = docShell->PluginsAllowedInCurrentDoc(); diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index b6d3332d145f..c4a154168510 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -49,7 +49,7 @@ #include "nsContentUtils.h" #include "nsIScriptError.h" #ifdef XP_MACOSX -#include "nsIDocShellTreeItem.h" +#include "nsIDocShell.h" #endif #include "ChildIterator.h" #include "nsError.h" @@ -4094,22 +4094,19 @@ const nsCSSFrameConstructor::FrameConstructionData* nsCSSFrameConstructor::FindXULMenubarData(Element* aElement, nsStyleContext* aStyleContext) { - nsCOMPtr container = - aStyleContext->PresContext()->GetContainer(); - if (container) { - nsCOMPtr treeItem(do_QueryInterface(container)); - if (treeItem) { - int32_t type; - treeItem->GetItemType(&type); - if (nsIDocShellTreeItem::typeChrome == type) { - nsCOMPtr parent; - treeItem->GetParent(getter_AddRefs(parent)); - if (!parent) { - // This is the root. Suppress the menubar, since on Mac - // window menus are not attached to the window. - static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA(); - return &sSuppressData; - } + nsCOMPtr treeItem = + aStyleContext->PresContext()->GetDocShell(); + if (treeItem) { + int32_t type; + treeItem->GetItemType(&type); + if (nsIDocShellTreeItem::typeChrome == type) { + nsCOMPtr parent; + treeItem->GetParent(getter_AddRefs(parent)); + if (!parent) { + // This is the root. Suppress the menubar, since on Mac + // window menus are not attached to the window. + static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA(); + return &sSuppressData; } } } diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 268366040fce..0d39f8cdd258 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -617,7 +617,7 @@ nsDocumentViewer::SetContainer(nsIDocShell* aContainer) { mContainer = static_cast(aContainer)->asWeakPtr(); if (mPresContext) { - mPresContext->SetContainer(aContainer); + mPresContext->SetContainer(mContainer); } // We're loading a new document into the window where this document @@ -887,7 +887,7 @@ nsDocumentViewer::InitInternal(nsIWidget* aParentWidget, requestor->GetInterface(NS_GET_IID(nsILinkHandler), getter_AddRefs(linkHandler)); - mPresContext->SetContainer(requestor); + mPresContext->SetContainer(mContainer); mPresContext->SetLinkHandler(linkHandler); } @@ -1309,13 +1309,13 @@ AttachContainerRecurse(nsIDocShell* aShell) nsRefPtr pc; viewer->GetPresContext(getter_AddRefs(pc)); if (pc) { - pc->SetContainer(aShell); + pc->SetContainer(static_cast(aShell)); pc->SetLinkHandler(nsCOMPtr(do_QueryInterface(aShell))); } nsCOMPtr presShell; viewer->GetPresShell(getter_AddRefs(presShell)); if (presShell) { - presShell->SetForwardingContainer(nullptr); + presShell->SetForwardingContainer(WeakPtr()); } } @@ -1343,7 +1343,7 @@ nsDocumentViewer::Open(nsISupports *aState, nsISHEntry *aSHEntry) mHidden = false; if (mPresShell) - mPresShell->SetForwardingContainer(nullptr); + mPresShell->SetForwardingContainer(WeakPtr()); // Rehook the child presentations. The child shells are still in // session history, so get them from there. @@ -1469,7 +1469,8 @@ DetachContainerRecurse(nsIDocShell *aShell) nsCOMPtr presShell; viewer->GetPresShell(getter_AddRefs(presShell)); if (presShell) { - presShell->SetForwardingContainer(nsWeakPtr(do_GetWeakReference(aShell))); + auto weakShell = static_cast(aShell)->asWeakPtr(); + presShell->SetForwardingContainer(weakShell); } } @@ -1590,9 +1591,7 @@ nsDocumentViewer::Destroy() mPresContext->SetContainer(nullptr); } if (mPresShell) { - nsWeakPtr container = - do_GetWeakReference(static_cast(mContainer)); - mPresShell->SetForwardingContainer(container); + mPresShell->SetForwardingContainer(mContainer); } // Do the same for our children. Note that we need to get the child @@ -1993,7 +1992,7 @@ nsDocumentViewer::Show(void) mPresContext->SetLinkHandler(linkHandler); } - mPresContext->SetContainer(base_win); + mPresContext->SetContainer(mContainer); } if (mPresContext) { diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 8ab4b2c8e885..30d4f6025165 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -22,6 +22,7 @@ #include "mozilla/EventForwards.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/WeakPtr.h" #include "gfxPoint.h" #include "nsTHashtable.h" #include "nsHashKeys.h" @@ -42,6 +43,7 @@ #include "nsMargin.h" class nsIContent; +class nsDocShell; class nsIDocument; class nsIFrame; class nsPresContext; @@ -956,7 +958,7 @@ public: * user events at the docshell's parent. This pointer allows us to do that. * It should not be used for any other purpose. */ - void SetForwardingContainer(nsWeakPtr aContainer) + void SetForwardingContainer(const mozilla::WeakPtr &aContainer) { mForwardingContainer = aContainer; } @@ -1508,7 +1510,7 @@ protected: // Pointer into mFrameConstructor - this is purely so that FrameManager() and // GetRootFrame() can be inlined: nsFrameManagerBase* mFrameManager; - nsWeakPtr mForwardingContainer; + mozilla::WeakPtr mForwardingContainer; nsRefreshDriver* mHiddenInvalidationObserverRefreshDriver; #ifdef ACCESSIBILITY mozilla::a11y::DocAccessible* mDocAccessible; diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index a181bf8141b5..6127b581b1f8 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -12,7 +12,7 @@ #include "nsCOMPtr.h" #include "nsPresContext.h" #include "nsIPresShell.h" -#include "nsIDocShell.h" +#include "nsDocShell.h" #include "nsIContentViewer.h" #include "nsPIDOMWindow.h" #include "nsStyleSet.h" @@ -621,7 +621,7 @@ nsPresContext::GetDocumentColorPreferences() { int32_t useAccessibilityTheme = 0; bool usePrefColors = true; - nsCOMPtr docShell(do_QueryReferent(mContainer)); + nsCOMPtr docShell(mContainer); if (docShell) { int32_t docShellType; docShell->GetItemType(&docShellType); @@ -894,7 +894,7 @@ nsPresContext::UpdateAfterPreferencesChanged() { mPrefChangedTimer = nullptr; - nsCOMPtr docShell(do_QueryReferent(mContainer)); + nsCOMPtr docShell(mContainer); if (docShell) { int32_t docShellType; docShell->GetItemType(&docShellType); @@ -1479,26 +1479,35 @@ nsPresContext::ScreenWidthInchesForFontInflation(bool* aChanged) } void -nsPresContext::SetContainer(nsISupports* aHandler) +nsPresContext::SetContainer(nsIDocShell* aDocShell) { - mContainer = do_GetWeakReference(aHandler); + if (aDocShell) { + mContainer = static_cast(aDocShell)->asWeakPtr(); + } else { + mContainer = WeakPtr(); + } InvalidateIsChromeCache(); if (mContainer) { GetDocumentColorPreferences(); } } -already_AddRefed -nsPresContext::GetContainerInternal() const +nsISupports* +nsPresContext::GetContainerWeakInternal() const { - nsCOMPtr result = do_QueryReferent(mContainer); - return result.forget(); + return static_cast(mContainer); } -already_AddRefed -nsPresContext::GetContainerExternal() const +nsISupports* +nsPresContext::GetContainerWeakExternal() const { - return GetContainerInternal(); + return GetContainerWeakInternal(); +} + +nsIDocShell* +nsPresContext::GetDocShell() const +{ + return mContainer; } bool @@ -1596,7 +1605,7 @@ nsPresContext::GetBidi() const bool nsPresContext::IsTopLevelWindowInactive() { - nsCOMPtr treeItem(do_QueryReferent(mContainer)); + nsCOMPtr treeItem(mContainer); if (!treeItem) return false; @@ -1919,7 +1928,7 @@ nsPresContext::SetPrintSettings(nsIPrintSettings *aPrintSettings) bool nsPresContext::EnsureVisible() { - nsCOMPtr docShell(do_QueryReferent(mContainer)); + nsCOMPtr docShell(mContainer); if (docShell) { nsCOMPtr cv; docShell->GetContentViewer(getter_AddRefs(cv)); @@ -1953,16 +1962,12 @@ bool nsPresContext::IsChromeSlow() const { bool isChrome = false; - nsCOMPtr container = GetContainer(); - if (container) { - nsresult result; - nsCOMPtr docShell(do_QueryInterface(container, &result)); - if (NS_SUCCEEDED(result) && docShell) { - int32_t docShellType; - result = docShell->GetItemType(&docShellType); - if (NS_SUCCEEDED(result)) { - isChrome = nsIDocShellTreeItem::typeChrome == docShellType; - } + nsCOMPtr docShell(mContainer); + if (docShell) { + int32_t docShellType; + nsresult result = docShell->GetItemType(&docShellType); + if (NS_SUCCEEDED(result)) { + isChrome = nsIDocShellTreeItem::typeChrome == docShellType; } } mIsChrome = isChrome; @@ -2697,7 +2702,7 @@ bool nsPresContext::IsDeviceSizePageSize() { bool isDeviceSizePageSize = false; - nsCOMPtr docShell(do_QueryReferent(mContainer)); + nsCOMPtr docShell(mContainer); if (docShell) { isDeviceSizePageSize = docShell->GetDeviceSizeIsPageSize(); } diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index 267028903357..4add2ffe7418 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -9,6 +9,7 @@ #define nsPresContext_h___ #include "mozilla/Attributes.h" +#include "mozilla/WeakPtr.h" #include "nsColor.h" #include "nsCoord.h" #include "nsCOMPtr.h" @@ -42,6 +43,8 @@ class nsBidiPresUtils; class nsAString; class nsIPrintSettings; +class nsDocShell; +class nsIDocShell; class nsIDocument; class nsILanguageAtomService; class nsITheme; @@ -410,18 +413,20 @@ public: bool GetFocusRingOnAnything() const { return mFocusRingOnAnything; } uint8_t GetFocusRingStyle() const { return mFocusRingStyle; } - NS_HIDDEN_(void) SetContainer(nsISupports* aContainer); + NS_HIDDEN_(void) SetContainer(nsIDocShell* aContainer); - virtual NS_HIDDEN_(already_AddRefed) GetContainerExternal() const; - NS_HIDDEN_(already_AddRefed) GetContainerInternal() const; + virtual nsISupports* GetContainerWeakExternal() const; + nsISupports* GetContainerWeakInternal() const; #ifdef MOZILLA_INTERNAL_API - already_AddRefed GetContainer() const - { return GetContainerInternal(); } + nsISupports* GetContainerWeak() const + { return GetContainerWeakInternal(); } #else - already_AddRefed GetContainer() const - { return GetContainerExternal(); } + nsISupports* GetContainerWeak() const + { return GetContainerWeakExternal(); } #endif + nsIDocShell* GetDocShell() const; + // XXX this are going to be replaced with set/get container void SetLinkHandler(nsILinkHandler* aHandler) { mLinkHandler = aHandler; } nsILinkHandler* GetLinkHandler() { return mLinkHandler; } @@ -1171,7 +1176,7 @@ public: protected: - nsWeakPtr mContainer; + mozilla::WeakPtr mContainer; PRCList mDOMMediaQueryLists; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 79bd1f451038..a600299c2b9f 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -80,7 +80,7 @@ #include "pldhash.h" #include "mozilla/dom/Touch.h" #include "nsIObserverService.h" -#include "nsIDocShell.h" // for reflow observation +#include "nsDocShell.h" // for reflow observation #include "nsIBaseWindow.h" #include "nsError.h" #include "nsLayoutUtils.h" @@ -1369,8 +1369,7 @@ nsresult PresShell::SetPrefNoFramesRule(void) NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null"); bool allowSubframes = true; - nsCOMPtr container = mPresContext->GetContainer(); - nsCOMPtr docShell(do_QueryInterface(container)); + nsCOMPtr docShell(mPresContext->GetDocShell()); if (docShell) { docShell->GetAllowSubframes(&allowSubframes); } @@ -3645,11 +3644,7 @@ PresShell::CaptureHistoryState(nsILayoutHistoryState** aState) // content viewer's Hide() method... by that point the docshell's // state could be wrong. We should sort out a better ownership // model for the layout history state. - nsCOMPtr container = mPresContext->GetContainer(); - if (!container) - return NS_ERROR_FAILURE; - - nsCOMPtr docShell(do_QueryInterface(container)); + nsCOMPtr docShell(mPresContext->GetDocShell()); if (!docShell) return NS_ERROR_FAILURE; @@ -5081,8 +5076,7 @@ PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder, static bool IsTransparentContainerElement(nsPresContext* aPresContext) { - nsCOMPtr container = aPresContext->GetContainerInternal(); - nsCOMPtr docShellItem = do_QueryInterface(container); + nsCOMPtr docShellItem = aPresContext->GetDocShell(); nsCOMPtr pwin(do_GetInterface(docShellItem)); if (!pwin) return false; @@ -5567,8 +5561,7 @@ PresShell::AssumeAllImagesVisible() if (!sImageVisibilityEnabled && sImageVisibilityEnabledForBrowserElementsOnly) { - nsCOMPtr container = mPresContext->GetContainer(); - nsCOMPtr docshell(do_QueryInterface(container)); + nsCOMPtr docshell(mPresContext->GetDocShell()); if (!docshell || !docshell->GetIsInBrowserElement()) { return true; } @@ -6006,13 +5999,13 @@ already_AddRefed PresShell::GetParentPresShell() { NS_ENSURE_TRUE(mPresContext, nullptr); - nsCOMPtr container = mPresContext->GetContainer(); - if (!container) { - container = do_QueryReferent(mForwardingContainer); - } // Now, find the parent pres shell and send the event there - nsCOMPtr treeItem = do_QueryInterface(container); + nsCOMPtr treeItem = mPresContext->GetDocShell(); + if (!treeItem) { + treeItem = mForwardingContainer.get(); + } + // Might have gone away, or never been around to start with NS_ENSURE_TRUE(treeItem, nullptr); @@ -6327,8 +6320,8 @@ PresShell::HandleEvent(nsIFrame* aFrame, // would occur if the mouse button is held down while a tab change occurs. // If the docshell is visible, look for a scrolling container. bool vis; - nsCOMPtr supports = mPresContext->GetContainer(); - nsCOMPtr baseWin(do_QueryInterface(supports)); + nsCOMPtr baseWin = + do_QueryInterface(mPresContext->GetContainerWeak()); if (baseWin && NS_SUCCEEDED(baseWin->GetVisibility(&vis)) && vis) { captureRetarget = gCaptureInfo.mRetargetToElement; if (!captureRetarget) { @@ -6702,8 +6695,7 @@ PresShell::GetTouchEventTargetDocument() return nullptr; } - nsCOMPtr container = context->GetContainer(); - nsCOMPtr shellAsTreeItem = do_QueryInterface(container); + nsCOMPtr shellAsTreeItem = context->GetDocShell(); if (!shellAsTreeItem) { return nullptr; } @@ -7183,7 +7175,7 @@ PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent, // and the js context is out of date. This check detects the case // that caused a crash in bug 41013, but there may be a better way // to handle this situation! - nsCOMPtr container = mPresContext->GetContainer(); + nsCOMPtr container = mPresContext->GetContainerWeak(); if (container) { // Dispatch event to content @@ -7204,7 +7196,7 @@ PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent, nsresult rv = NS_OK; PushCurrentEventInfo(nullptr, aTargetContent); - nsCOMPtr container = mPresContext->GetContainer(); + nsCOMPtr container = mPresContext->GetContainerWeak(); if (container) { rv = nsEventDispatcher::DispatchDOMEvent(aTargetContent, nullptr, aEvent, mPresContext, aStatus); @@ -7877,13 +7869,10 @@ PresShell::DidDoReflow(bool aInterruptible, bool aWasInterrupted) HandlePostedReflowCallbacks(aInterruptible); - nsCOMPtr container = mPresContext->GetContainer(); - if (container) { - nsCOMPtr docShell = do_QueryInterface(container); - if (docShell) { - DOMHighResTimeStamp now = GetPerformanceNow(); - docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now); - } + nsCOMPtr docShell = mPresContext->GetDocShell(); + if (docShell) { + DOMHighResTimeStamp now = GetPerformanceNow(); + docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now); } if (sSynthMouseMove) { @@ -9628,7 +9617,7 @@ void nsIPresShell::ReleaseStatics() // Asks our docshell whether we're active. void PresShell::QueryIsActive() { - nsCOMPtr container = mPresContext->GetContainer(); + nsCOMPtr container = mPresContext->GetContainerWeak(); if (mDocument) { nsIDocument* displayDoc = mDocument->GetDisplayDocument(); if (displayDoc) { @@ -9641,7 +9630,7 @@ void PresShell::QueryIsActive() nsIPresShell* displayPresShell = displayDoc->GetShell(); if (displayPresShell) { - container = displayPresShell->GetPresContext()->GetContainer(); + container = displayPresShell->GetPresContext()->GetContainerWeak(); } } } diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 97239c0bc0b8..2712405a1861 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2491,7 +2491,7 @@ ScrollFrameHelper::GetScrollbarStylesFromFrame() const } ScrollbarStyles result = presContext->GetViewportOverflowOverride(); - nsCOMPtr container = presContext->GetContainer(); + nsCOMPtr container = presContext->GetContainerWeak(); nsCOMPtr scrollable = do_QueryInterface(container); if (scrollable) { HandleScrollPref(scrollable, nsIScrollable::ScrollOrientation_X, diff --git a/layout/mathml/nsMathMLmactionFrame.cpp b/layout/mathml/nsMathMLmactionFrame.cpp index 673e42e70c3f..5f8d9d093089 100644 --- a/layout/mathml/nsMathMLmactionFrame.cpp +++ b/layout/mathml/nsMathMLmactionFrame.cpp @@ -8,7 +8,7 @@ #include "nsPresContext.h" #include "nsINameSpaceManager.h" #include "prprf.h" // For PR_snprintf() -#include "nsIDocShellTreeItem.h" +#include "nsIDocShell.h" #include "nsIDocShellTreeOwner.h" #include "nsIWebBrowserChrome.h" #include "nsIInterfaceRequestorUtils.h" @@ -246,17 +246,14 @@ NS_IMPL_ISUPPORTS1(nsMathMLmactionFrame::MouseListener, void ShowStatus(nsPresContext* aPresContext, nsString& aStatusMsg) { - nsCOMPtr cont = aPresContext->GetContainer(); - if (cont) { - nsCOMPtr docShellItem(do_QueryInterface(cont)); - if (docShellItem) { - nsCOMPtr treeOwner; - docShellItem->GetTreeOwner(getter_AddRefs(treeOwner)); - if (treeOwner) { - nsCOMPtr browserChrome(do_GetInterface(treeOwner)); - if (browserChrome) { - browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, aStatusMsg.get()); - } + nsCOMPtr docShellItem(aPresContext->GetDocShell()); + if (docShellItem) { + nsCOMPtr treeOwner; + docShellItem->GetTreeOwner(getter_AddRefs(treeOwner)); + if (treeOwner) { + nsCOMPtr browserChrome(do_GetInterface(treeOwner)); + if (browserChrome) { + browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, aStatusMsg.get()); } } } diff --git a/layout/printing/nsPrintEngine.cpp b/layout/printing/nsPrintEngine.cpp index 31445e87face..e24b1fb8c5c9 100644 --- a/layout/printing/nsPrintEngine.cpp +++ b/layout/printing/nsPrintEngine.cpp @@ -2214,8 +2214,7 @@ nsPrintEngine::ReflowPrintObject(nsPrintObject * aPO) // This docshell stuff is weird; will go away when we stop having multiple // presentations per document - nsCOMPtr supps(do_QueryInterface(aPO->mDocShell)); - aPO->mPresContext->SetContainer(supps); + aPO->mPresContext->SetContainer(aPO->mDocShell); aPO->mPresShell->BeginObservingDocument(); diff --git a/layout/svg/nsSVGOuterSVGFrame.cpp b/layout/svg/nsSVGOuterSVGFrame.cpp index b3deae5305d9..c472e8111595 100644 --- a/layout/svg/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/nsSVGOuterSVGFrame.cpp @@ -915,7 +915,7 @@ nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(nsIFrame **aEmbeddingFrame) { if (!mContent->GetParent()) { // Our content is the document element - nsCOMPtr container = PresContext()->GetContainer(); + nsCOMPtr container = PresContext()->GetContainerWeak(); nsCOMPtr window = do_GetInterface(container); if (window) { nsCOMPtr frameElement; diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp index f5b4e02d2149..f307e35ddd08 100644 --- a/layout/xul/nsMenuPopupFrame.cpp +++ b/layout/xul/nsMenuPopupFrame.cpp @@ -29,7 +29,7 @@ #include "nsBoxLayoutState.h" #include "nsIScrollableFrame.h" #include "nsIRootBox.h" -#include "nsIDocShellTreeItem.h" +#include "nsIDocShell.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsLayoutUtils.h" @@ -147,8 +147,7 @@ nsMenuPopupFrame::Init(nsIContent* aContent, mIsDragPopup = true; } - nsCOMPtr cont = PresContext()->GetContainer(); - nsCOMPtr dsti = do_QueryInterface(cont); + nsCOMPtr dsti = PresContext()->GetDocShell(); int32_t type = -1; if (dsti && NS_SUCCEEDED(dsti->GetItemType(&type)) && type == nsIDocShellTreeItem::typeChrome) @@ -280,8 +279,7 @@ nsMenuPopupFrame::CreateWidgetForView(nsView* aView) // should be in front of it. nsCOMPtr parentWidget; if (widgetData.mPopupLevel != ePopupLevelTop) { - nsCOMPtr cont = PresContext()->GetContainer(); - nsCOMPtr dsti = do_QueryInterface(cont); + nsCOMPtr dsti = PresContext()->GetDocShell(); if (!dsti) return NS_ERROR_FAILURE; diff --git a/layout/xul/nsResizerFrame.cpp b/layout/xul/nsResizerFrame.cpp index 41483ca6c6b2..2e082ef9f8b2 100644 --- a/layout/xul/nsResizerFrame.cpp +++ b/layout/xul/nsResizerFrame.cpp @@ -338,8 +338,7 @@ nsResizerFrame::GetContentToResize(nsIPresShell* aPresShell, nsIBaseWindow** aWi // don't allow resizing windows in content shells bool isChromeShell = false; - nsCOMPtr cont = aPresShell->GetPresContext()->GetContainer(); - nsCOMPtr dsti = do_QueryInterface(cont); + nsCOMPtr dsti = aPresShell->GetPresContext()->GetDocShell(); if (dsti) { int32_t type = -1; isChromeShell = (NS_SUCCEEDED(dsti->GetItemType(&type)) && diff --git a/layout/xul/nsTitleBarFrame.cpp b/layout/xul/nsTitleBarFrame.cpp index 44f5f47fa1a6..3e44250d37da 100644 --- a/layout/xul/nsTitleBarFrame.cpp +++ b/layout/xul/nsTitleBarFrame.cpp @@ -12,7 +12,7 @@ #include "nsIWidget.h" #include "nsMenuPopupFrame.h" #include "nsPresContext.h" -#include "nsIDocShellTreeItem.h" +#include "nsIDocShell.h" #include "nsPIDOMWindow.h" #include "nsEventDispatcher.h" #include "nsDisplayList.h" @@ -72,8 +72,7 @@ nsTitleBarFrame::HandleEvent(nsPresContext* aPresContext, case NS_MOUSE_BUTTON_DOWN: { if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { // titlebar has no effect in non-chrome shells - nsCOMPtr cont = aPresContext->GetContainer(); - nsCOMPtr dsti = do_QueryInterface(cont); + nsCOMPtr dsti = aPresContext->GetDocShell(); if (dsti) { int32_t type = -1; if (NS_SUCCEEDED(dsti->GetItemType(&type)) && diff --git a/layout/xul/nsXULPopupManager.cpp b/layout/xul/nsXULPopupManager.cpp index e666547c46d3..5a5e1f3abf42 100644 --- a/layout/xul/nsXULPopupManager.cpp +++ b/layout/xul/nsXULPopupManager.cpp @@ -1466,8 +1466,7 @@ nsXULPopupManager::MayShowPopup(nsMenuPopupFrame* aPopup) if (widget && widget->GetLastRollup() == aPopup->GetContent()) return false; - nsCOMPtr cont = aPopup->PresContext()->GetContainer(); - nsCOMPtr dsti = do_QueryInterface(cont); + nsCOMPtr dsti = aPopup->PresContext()->GetDocShell(); nsCOMPtr baseWin = do_QueryInterface(dsti); if (!baseWin) return false; diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp index fbeaf6a448d8..985d7e62897d 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp @@ -311,17 +311,15 @@ nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly, nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); } - nsCOMPtr startingContainer = presContext->GetContainer(); - nsCOMPtr treeItem(do_QueryInterface(startingContainer)); - NS_ASSERTION(treeItem, "Bug 175321 Crashes with Type Ahead Find [@ nsTypeAheadFind::FindItNow]"); - if (!treeItem) + nsCOMPtr startingDocShell(presContext->GetDocShell()); + NS_ASSERTION(startingDocShell, "Bug 175321 Crashes with Type Ahead Find [@ nsTypeAheadFind::FindItNow]"); + if (!startingDocShell) return NS_ERROR_FAILURE; nsCOMPtr rootContentTreeItem; nsCOMPtr currentDocShell; - nsCOMPtr startingDocShell(do_QueryInterface(startingContainer)); - treeItem->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem)); + startingDocShell->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem)); nsCOMPtr rootContentDocShell = do_QueryInterface(rootContentTreeItem); @@ -334,7 +332,7 @@ nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly, getter_AddRefs(docShellEnumerator)); // Default: can start at the current document - nsCOMPtr currentContainer = startingContainer = + nsCOMPtr currentContainer = do_QueryInterface(rootContentDocShell); // Iterate up to current shell, if there's more than 1 that we're @@ -1224,7 +1222,7 @@ nsTypeAheadFind::GetPresShell() nsCOMPtr shell = do_QueryReferent(mPresShell); if (shell) { nsPresContext *pc = shell->GetPresContext(); - if (!pc || !nsCOMPtr(pc->GetContainer())) { + if (!pc || !pc->GetContainerWeak()) { return nullptr; } } diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index 5e14b5ceea47..8640e221a71e 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -1513,8 +1513,7 @@ nsBaseWidget::GetRootAccessible() // If container is null then the presshell is not active. This often happens // when a preshell is being held onto for fastback. nsPresContext* presContext = presShell->GetPresContext(); - nsCOMPtr container = presContext->GetContainer(); - NS_ENSURE_TRUE(container, nullptr); + NS_ENSURE_TRUE(presContext->GetContainerWeak(), nullptr); // Accessible creation might be not safe so use IsSafeToRunScript to // make sure it's not created at unsafe times. From 19a2049c2c94072d1c0b61985197390659d6d585 Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Mon, 18 Nov 2013 13:52:21 -0500 Subject: [PATCH 043/459] bug 938197 - alias atk text attr names to gecko ones r=surkov --- accessible/src/atk/AccessibleWrap.cpp | 2 +- accessible/src/atk/nsMaiInterfaceText.cpp | 86 ++++++++++++++++++++++- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/accessible/src/atk/AccessibleWrap.cpp b/accessible/src/atk/AccessibleWrap.cpp index 639ebec8769f..40609a1264f4 100644 --- a/accessible/src/atk/AccessibleWrap.cpp +++ b/accessible/src/atk/AccessibleWrap.cpp @@ -692,7 +692,7 @@ getRoleCB(AtkObject *aAtkObj) return aAtkObj->role; } -AtkAttributeSet* +static AtkAttributeSet* ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes) { if (!aAttributes) diff --git a/accessible/src/atk/nsMaiInterfaceText.cpp b/accessible/src/atk/nsMaiInterfaceText.cpp index 3e373d2d44f4..946acfd2a011 100644 --- a/accessible/src/atk/nsMaiInterfaceText.cpp +++ b/accessible/src/atk/nsMaiInterfaceText.cpp @@ -12,12 +12,87 @@ #include "nsIAccessibleTypes.h" #include "nsIPersistentProperties2.h" +#include "nsISimpleEnumerator.h" #include "mozilla/Likely.h" +using namespace mozilla; using namespace mozilla::a11y; -AtkAttributeSet* ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes); +static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED]; + +static AtkAttributeSet* +ConvertToAtkTextAttributeSet(nsIPersistentProperties* aAttributes) +{ + if (!aAttributes) + return nullptr; + + AtkAttributeSet* objAttributeSet = nullptr; + nsCOMPtr propEnum; + nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum)); + NS_ENSURE_SUCCESS(rv, nullptr); + + bool hasMore = false; + while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr sup; + rv = propEnum->GetNext(getter_AddRefs(sup)); + NS_ENSURE_SUCCESS(rv, objAttributeSet); + + nsCOMPtr propElem(do_QueryInterface(sup)); + NS_ENSURE_TRUE(propElem, objAttributeSet); + + nsAutoCString name; + rv = propElem->GetKey(name); + NS_ENSURE_SUCCESS(rv, objAttributeSet); + + nsAutoString value; + rv = propElem->GetValue(value); + NS_ENSURE_SUCCESS(rv, objAttributeSet); + + AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute)); + objAttr->name = g_strdup(name.get()); + objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get()); + objAttributeSet = g_slist_prepend(objAttributeSet, objAttr); + + // Handle attributes where atk has its own name. + const char* atkName = nullptr; + nsAutoString atkValue; + if (name.EqualsLiteral("color")) { + // The format of the atk attribute is r,g,b and the gecko one is + // rgb(r,g,b). + atkValue = Substring(value, 5, value.Length() - 1); + atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FG_COLOR]; + } else if (name.EqualsLiteral("background-color")) { + // The format of the atk attribute is r,g,b and the gecko one is + // rgb(r,g,b). + atkValue = Substring(value, 5, value.Length() - 1); + atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_BG_COLOR]; + } else if (name.EqualsLiteral("font-family")) { + atkValue = value; + atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FAMILY_NAME]; + } else if (name.Equals("font-size")) { + // ATK wants the number of pixels without px at the end. + atkValue = StringHead(value, value.Length() - 2); + atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_SIZE]; + } else if (name.EqualsLiteral("font-weight")) { + atkValue = value; + atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_WEIGHT]; + } else if (name.EqualsLiteral("invalid")) { + atkValue = value; + atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_INVALID]; + } + + if (atkName) { + objAttr = static_cast(g_malloc(sizeof(AtkAttribute))); + objAttr->name = g_strdup(atkName); + objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(atkValue).get()); + objAttributeSet = g_slist_prepend(objAttributeSet, objAttr); + } + } + + // libatk-adaptor will free it + return objAttributeSet; +} static void ConvertTexttoAsterisks(AccessibleWrap* accWrap, nsAString& aString) @@ -188,7 +263,7 @@ getRunAttributesCB(AtkText *aText, gint aOffset, *aStartOffset = startOffset; *aEndOffset = endOffset; - return ConvertToAtkAttributeSet(attributes); + return ConvertToAtkTextAttributeSet(attributes); } static AtkAttributeSet* @@ -203,7 +278,7 @@ getDefaultAttributesCB(AtkText *aText) return nullptr; nsCOMPtr attributes = text->DefaultTextAttributes(); - return ConvertToAtkAttributeSet(attributes); + return ConvertToAtkTextAttributeSet(attributes); } static void @@ -415,4 +490,9 @@ textInterfaceInitCB(AtkTextIface* aIface) aIface->remove_selection = removeTextSelectionCB; aIface->set_selection = setTextSelectionCB; aIface->set_caret_offset = setCaretOffsetCB; + + // Cache the string values of the atk text attribute names. + for (uint32_t i = 0; i < ArrayLength(sAtkTextAttrNames); i++) + sAtkTextAttrNames[i] = + atk_text_attribute_get_name(static_cast(i)); } From 612fb9d2cb9d8eb8156579a39c4aa85e4bf192b6 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 12 Dec 2013 14:42:04 -0500 Subject: [PATCH 044/459] Backed out changeset b6b911ca4dd8 (bug 837202) for OSX bustage. CLOSED TREE --- .../downloads/ApplicationReputation.cpp | 53 +------------------ toolkit/components/telemetry/Histograms.json | 18 ------- 2 files changed, 2 insertions(+), 69 deletions(-) diff --git a/toolkit/components/downloads/ApplicationReputation.cpp b/toolkit/components/downloads/ApplicationReputation.cpp index 9e3e5f2cbe47..d8fc7c633f29 100644 --- a/toolkit/components/downloads/ApplicationReputation.cpp +++ b/toolkit/components/downloads/ApplicationReputation.cpp @@ -21,7 +21,6 @@ #include "mozilla/Preferences.h" #include "mozilla/Services.h" -#include "mozilla/Telemetry.h" #include "nsCOMPtr.h" #include "nsDebug.h" @@ -34,7 +33,6 @@ #include "nsXPCOMStrings.h" using mozilla::Preferences; -using mozilla::Telemetry::Accumulate; // Preferences that we need to initialize the query. We may need another // preference than browser.safebrowsing.malware.enabled, or simply use @@ -63,27 +61,6 @@ public: ~PendingLookup(); private: - /** - * Telemetry states. - */ - /** - * The download appeared on the allowlist, blocklist, or no list (and thus - * could trigger a remote query). - */ - enum { - ALLOW_LIST = 0, - BLOCK_LIST = 1, - NO_LIST = 2, - } LIST_TYPES; - /** - * Status of the remote response (valid or not). - */ - enum { - SERVER_RESPONSE_VALID = 0, - SERVER_RESPONSE_FAILED = 1, - SERVER_RESPONSE_INVALID = 2, - } SERVER_RESPONSE_TYPES; - nsCOMPtr mQuery; nsCOMPtr mCallback; /** @@ -128,8 +105,6 @@ PendingLookup::~PendingLookup() { nsresult PendingLookup::OnComplete(bool shouldBlock, nsresult rv) { - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK, - shouldBlock); nsresult res = mCallback->OnComplete(shouldBlock, rv); return res; } @@ -144,18 +119,15 @@ PendingLookup::HandleEvent(const nsACString& tables) { nsCString allow_list; Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allow_list); if (FindInReadable(tables, allow_list)) { - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST); return OnComplete(false, NS_OK); } nsCString block_list; Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &block_list); if (FindInReadable(tables, block_list)) { - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST); return OnComplete(true, NS_OK); } - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST); #if 0 nsresult rv = SendRemoteQuery(); if (NS_FAILED(rv)) { @@ -296,32 +268,16 @@ PendingLookup::OnStopRequestInternal(nsIRequest *aRequest, nsISupports *aContext, nsresult aResult, bool* aShouldBlock) { - if (NS_FAILED(aResult)) { - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, - SERVER_RESPONSE_FAILED); - return aResult; - } - *aShouldBlock = false; nsresult rv; nsCOMPtr channel = do_QueryInterface(aRequest, &rv); - if (NS_FAILED(rv)) { - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, - SERVER_RESPONSE_FAILED); - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); uint32_t status = 0; rv = channel->GetResponseStatus(&status); - if (NS_FAILED(rv)) { - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, - SERVER_RESPONSE_FAILED); - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); if (status != 200) { - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, - SERVER_RESPONSE_FAILED); return NS_ERROR_NOT_AVAILABLE; } @@ -329,15 +285,11 @@ PendingLookup::OnStopRequestInternal(nsIRequest *aRequest, safe_browsing::ClientDownloadResponse response; if (!response.ParseFromString(buf)) { NS_WARNING("Could not parse protocol buffer"); - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, - SERVER_RESPONSE_INVALID); return NS_ERROR_CANNOT_CONVERT_DATA; } // There are several more verdicts, but we only respect one for now and treat // everything else as SAFE. - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, - SERVER_RESPONSE_VALID); if (response.verdict() == safe_browsing::ClientDownloadResponse::DANGEROUS) { *aShouldBlock = true; } @@ -383,7 +335,6 @@ ApplicationReputationService::QueryReputation( NS_ENSURE_ARG_POINTER(aQuery); NS_ENSURE_ARG_POINTER(aCallback); - Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true); nsresult rv = QueryReputationInternal(aQuery, aCallback); if (NS_FAILED(rv)) { aCallback->OnComplete(false, rv); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 6a2cab5804b6..a8e5707d4a06 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -23,24 +23,6 @@ "extended_statistics_ok": true, "description": "time spent updating accessibility (ms)" }, - "APPLICATION_REPUTATION_SHOULD_BLOCK": { - "kind": "boolean", - "description": "Application reputation verdict (shouldBlock=false is OK)" - }, - "APPLICATION_REPUTATION_LOCAL": { - "kind": "enumerated", - "n_values": 3, - "description": "Application reputation local results (0=ALLOW, 1=BLOCK, 2=NONE)" - }, - "APPLICATION_REPUTATION_SERVER": { - "kind": "enumerated", - "n_values": 3, - "description": "Application reputation remote status (0 = OK, 1 = FAIL, 2 = INVALID)" - }, - "APPLICATION_REPUTATION_COUNT": { - "kind": "flag", - "description": "Application reputation query count (both local and remote)" - }, "BACKGROUNDFILESAVER_THREAD_COUNT": { "kind": "enumerated", "n_values": 21, From dcde6d6dea4e6a72d905d663a8f3707685ff5fcd Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Wed, 11 Dec 2013 14:06:04 -0800 Subject: [PATCH 045/459] Bug 949195 - Don't refer to IsInRequest() when JS_DEBUG is not defined. r=efaust --- js/public/RootingAPI.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index 45c4e0d596e2..bf48234811e7 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -648,7 +648,7 @@ struct GCMethods #endif }; -#if defined(JS_DEBUG) +#ifdef JS_DEBUG /* This helper allows us to assert that Rooted is scoped within a request. */ extern JS_PUBLIC_API(bool) IsInRequest(JSContext *cx); @@ -688,7 +688,9 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase : ptr(js::GCMethods::initial()) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; +#ifdef JS_DEBUG MOZ_ASSERT(js::IsInRequest(cx)); +#endif init(js::ContextFriendFields::get(cx)); } @@ -697,7 +699,9 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase : ptr(initial) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; +#ifdef JS_DEBUG MOZ_ASSERT(js::IsInRequest(cx)); +#endif init(js::ContextFriendFields::get(cx)); } From f4ddb7d8f99fccff44d5c52c85e678c83201a39f Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 12 Dec 2013 16:05:53 -0500 Subject: [PATCH 046/459] Backed out changeset 71f9f57e5ddb (bug 949474) for test_bug808374.html failures. CLOSED TREE --- content/media/webaudio/AudioContext.cpp | 4 ++-- .../media/webaudio/test/test_AudioBuffer.html | 16 +++++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index 850727f1ef6c..f269163652f7 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -174,8 +174,8 @@ AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels, uint32_t aLength, float aSampleRate, ErrorResult& aRv) { - if (aSampleRate < 8000 || aSampleRate > 192000 || !aLength || !aNumberOfChannels) { - aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + if (aSampleRate < 8000 || aSampleRate > 96000 || !aLength) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } diff --git a/content/media/webaudio/test/test_AudioBuffer.html b/content/media/webaudio/test/test_AudioBuffer.html index 47716853bf6c..75eeb7ff00e5 100644 --- a/content/media/webaudio/test/test_AudioBuffer.html +++ b/content/media/webaudio/test/test_AudioBuffer.html @@ -81,21 +81,15 @@ addLoadEvent(function() { expectException(function() { context.createBuffer(2, 2048, 7999); - }, DOMException.INDEX_SIZE_ERR); + }, DOMException.NOT_SUPPORTED_ERR); expectException(function() { - context.createBuffer(2, 2048, 192001); - }, DOMException.INDEX_SIZE_ERR); + context.createBuffer(2, 2048, 96001); + }, DOMException.NOT_SUPPORTED_ERR); context.createBuffer(2, 2048, 8000); // no exception - context.createBuffer(2, 2048, 192000); // no exception - context.createBuffer(32, 2048, 48000); // no exception - // Null length + context.createBuffer(2, 2048, 96000); // no exception expectException(function() { context.createBuffer(2, 0, 48000); - }, DOMException.INDEX_SIZE_ERR); - // Null number of channels - expectException(function() { - context.createBuffer(0, 2048, 48000); - }, DOMException.INDEX_SIZE_ERR); + }, DOMException.NOT_SUPPORTED_ERR); SimpleTest.finish(); }); From 5990fb7b8b54d064b7c439cfdd71a9c54a8ad252 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Thu, 12 Dec 2013 13:10:54 -0800 Subject: [PATCH 047/459] Bug 932982 - Trace type constraints and allow preserving jitcode in GCs without also marking all type information, r=billm,jandem. --- js/public/MemoryMetrics.h | 1 - js/src/gc/Marking.cpp | 46 +---- js/src/gc/RootMarking.cpp | 5 - js/src/gc/Statistics.cpp | 1 - js/src/gc/Statistics.h | 1 - js/src/gc/Zone.cpp | 31 +-- js/src/gc/Zone.h | 2 - js/src/jit/CodeGenerator.cpp | 6 +- js/src/jit/Ion.cpp | 17 +- js/src/jit/Ion.h | 2 +- js/src/jit/IonBuilder.cpp | 6 +- js/src/jit/IonCode.h | 3 + js/src/jit/MIR.cpp | 2 +- js/src/jscompartment.cpp | 11 +- js/src/jscompartment.h | 3 - js/src/jsgc.cpp | 14 +- js/src/jsgc.h | 7 +- js/src/jsinfer.cpp | 329 +++++++++++++++--------------- js/src/jsinfer.h | 122 ++++------- js/src/jsinferinlines.h | 78 ++----- js/src/vm/MemoryMetrics.cpp | 1 - js/xpconnect/src/XPCJSRuntime.cpp | 4 - 22 files changed, 258 insertions(+), 434 deletions(-) diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index 95ac91b9f6d5..f974259467d5 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -393,7 +393,6 @@ struct CompartmentStats macro(Other, NotLiveGCThing, baselineStubsOptimized) \ macro(Other, NotLiveGCThing, ionData) \ macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \ - macro(Other, NotLiveGCThing, typeInferencePendingArrays) \ macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \ macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \ macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \ diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index b0c8a6555988..d0e3bde29e8a 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1122,14 +1122,11 @@ gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape) static void ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) { - /* Don't mark properties for singletons. They'll be purged by the GC. */ - if (!type->singleton) { - unsigned count = type->getPropertyCount(); - for (unsigned i = 0; i < count; i++) { - types::Property *prop = type->getProperty(i); - if (prop && JSID_IS_STRING(prop->id)) - PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); - } + unsigned count = type->getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + types::Property *prop = type->getProperty(i); + if (prop && JSID_IS_STRING(prop->id)) + PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); } if (TaggedProto(type->proto).isObject()) @@ -1350,7 +1347,7 @@ GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp) } void -GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr) +GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr) { if (tag == TypeTag) { ScanTypeObject(this, reinterpret_cast(addr)); @@ -1364,35 +1361,6 @@ GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t ad pushObject(obj); } else if (tag == IonCodeTag) { MarkChildren(this, reinterpret_cast(addr)); - } else if (tag == ArenaTag) { - ArenaHeader *aheader = reinterpret_cast(addr); - AllocKind thingKind = aheader->getAllocKind(); - size_t thingSize = Arena::thingSize(thingKind); - - for ( ; aheader; aheader = aheader->next) { - Arena *arena = aheader->getArena(); - FreeSpan firstSpan(aheader->getFirstFreeSpan()); - const FreeSpan *span = &firstSpan; - - for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) { - JS_ASSERT(thing <= arena->thingsEnd()); - if (thing == span->first) { - if (!span->hasNext()) - break; - thing = span->last; - span = span->nextSpan(); - } else { - JSObject *object = reinterpret_cast(thing); - if (object->hasSingletonType() && object->markIfUnmarked(getMarkColor())) - pushObject(object); - budget.step(); - } - } - if (budget.isOverBudget()) { - pushArenaList(aheader); - return; - } - } } } @@ -1430,7 +1398,7 @@ GCMarker::processMarkStackTop(SliceBudget &budget) goto scan_obj; } - processMarkStackOther(budget, tag, addr); + processMarkStackOther(tag, addr); return; scan_value_array: diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 8ae3e85fd559..541ad84d996c 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -726,11 +726,6 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting()) continue; - if (IS_GC_MARKING_TRACER(trc) && zone->isPreservingCode()) { - gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_TYPES); - zone->markTypes(trc); - } - /* Do not discard scripts with counts while profiling. */ if (rt->profilingScripts && !rt->isHeapMinorCollecting()) { for (CellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) { diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 7744b61d53f5..be76eee734a1 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -276,7 +276,6 @@ static const PhaseInfo phases[] = { { PHASE_PURGE, "Purge", PHASE_NO_PARENT }, { PHASE_MARK, "Mark", PHASE_NO_PARENT }, { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MARK }, - { PHASE_MARK_TYPES, "Mark Types", PHASE_MARK_ROOTS }, { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK }, { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT }, { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP }, diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 3240fd238f53..1029fd52fe8e 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -28,7 +28,6 @@ enum Phase { PHASE_PURGE, PHASE_MARK, PHASE_MARK_ROOTS, - PHASE_MARK_TYPES, PHASE_MARK_DELAYED, PHASE_SWEEP, PHASE_SWEEP_MARK, diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index ad0c2fae7fde..539faabbd7c8 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -80,35 +80,6 @@ Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon) needsBarrier_ = needs; } -void -Zone::markTypes(JSTracer *trc) -{ - /* - * Mark all scripts, type objects and singleton JS objects in the - * compartment. These can be referred to directly by type sets, which we - * cannot modify while code which depends on these type sets is active. - */ - JS_ASSERT(isPreservingCode()); - - for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { - JSScript *script = i.get(); - MarkScriptRoot(trc, &script, "mark_types_script"); - JS_ASSERT(script == i.get()); - } - - for (size_t thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) { - ArenaHeader *aheader = allocator.arenas.getFirstArena(static_cast(thingKind)); - if (aheader) - trc->runtime->gcMarker.pushArenaList(aheader); - } - - for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { - types::TypeObject *type = i.get(); - MarkTypeObjectRoot(trc, &type, "mark_types_scan"); - JS_ASSERT(type == i.get()); - } -} - void Zone::resetGCMallocBytes() { @@ -144,7 +115,7 @@ Zone::sweep(FreeOp *fop, bool releaseTypes) if (active) releaseTypes = false; - if (!isPreservingCode()) { + { gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); types.sweep(fop, releaseTypes); } diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index ebad06d0b6c9..a2fe6a22a022 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -310,8 +310,6 @@ struct Zone : public JS::shadow::Zone, js_ReportAllocationOverflow(nullptr); } - void markTypes(JSTracer *trc); - js::types::TypeZone types; void sweep(js::FreeOp *fop, bool releaseTypes); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 7cbe48d16495..f8a69038c4ac 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -5887,7 +5887,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) safepoints_.size(), callTargets.length(), patchableBackedges_.length()); if (!ionScript) { - recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); + recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); return false; } @@ -5912,7 +5912,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) // Use js_free instead of IonScript::Destroy: the cache list and // backedge list are still uninitialized. js_free(ionScript); - recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); + recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); return false; } @@ -5933,7 +5933,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) /* resetUses */ false, /* cancelOffThread*/ false)) { js_free(ionScript); - recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); + recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); return false; } } diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index ba3ee46a154d..5bf13a880301 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2481,7 +2481,7 @@ jit::InvalidateAll(FreeOp *fop, Zone *zone) void -jit::Invalidate(types::TypeCompartment &types, FreeOp *fop, +jit::Invalidate(types::TypeZone &types, FreeOp *fop, const Vector &invalid, bool resetUses, bool cancelOffThread) { @@ -2564,7 +2564,7 @@ void jit::Invalidate(JSContext *cx, const Vector &invalid, bool resetUses, bool cancelOffThread) { - jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses, + jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses, cancelOffThread); } @@ -2611,14 +2611,13 @@ FinishInvalidationOf(FreeOp *fop, JSScript *script, IonScript *ionScript, bool p else script->setIonScript(nullptr); - // If this script has Ion code on the stack, invalidation() will return - // true. In this case we have to wait until destroying it. - if (!ionScript->invalidated()) { - types::TypeCompartment &types = script->compartment()->types; - ionScript->recompileInfo().compilerOutput(types)->invalidate(); + types::TypeZone &types = script->zone()->types; + ionScript->recompileInfo().compilerOutput(types)->invalidate(); + // If this script has Ion code on the stack, invalidated() will return + // true. In this case we have to wait until destroying it. + if (!ionScript->invalidated()) jit::IonScript::Destroy(fop, ionScript); - } } void @@ -2637,8 +2636,6 @@ jit::FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp) // Free optimized baseline stubs. if (comp->jitCompartment()) comp->jitCompartment()->optimizedStubSpace()->free(); - - comp->types.clearCompilerOutputs(fop); } void diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index 37ad4e778387..db6c5ee4f811 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -364,7 +364,7 @@ IonExecStatus IonCannon(JSContext *cx, RunState &state); IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args); // Walk the stack and invalidate active Ion frames for the invalid scripts. -void Invalidate(types::TypeCompartment &types, FreeOp *fop, +void Invalidate(types::TypeZone &types, FreeOp *fop, const Vector &invalid, bool resetUses = true, bool cancelOffThread = true); void Invalidate(JSContext *cx, const Vector &invalid, bool resetUses = true, diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 28b2285a8fdc..a85d3f37e01a 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6286,7 +6286,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc types::HeapTypeSetKey property = staticType->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || - property.configured(constraints(), staticType)) + property.configured(constraints())) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. @@ -6376,7 +6376,7 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name) types::HeapTypeSetKey property = staticType->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || - property.configured(constraints(), staticType)) + property.configured(constraints())) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. @@ -7826,7 +7826,7 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name, *property = type->property(id); return property->maybeTypes() && property->maybeTypes()->definiteProperty() && - !property->configured(constraints(), type); + !property->configured(constraints()); } bool diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index 8167d1fa80e9..4acc08d50993 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -526,6 +526,9 @@ struct IonScript const types::RecompileInfo& recompileInfo() const { return recompileInfo_; } + types::RecompileInfo& recompileInfoRef() { + return recompileInfo_; + } uint32_t incrOsrPcMismatchCounter() { return ++osrPcMismatchCounter_; } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index aad9234a367f..882b468f6a7b 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -3025,7 +3025,7 @@ jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, // Check if the property has been reconfigured or is a getter. types::HeapTypeSetKey property = object->property(NameToId(name)); - if (property.configured(constraints, object)) + if (property.configured(constraints)) return false; } } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index a4132018c96f..f9dd807606db 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -48,7 +48,6 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options = #endif global_(nullptr), enterCompartmentDepth(0), - lastCodeRelease(0), data(nullptr), objectMetadataCallback(nullptr), lastAnimationTime(0), @@ -545,13 +544,6 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes) WeakMapBase::sweepCompartment(this); } - if (zone()->isPreservingCode()) { - gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); - types.sweepShapes(fop); - } else { - JS_ASSERT(!types.constrainedOutputs); - } - NativeIterator *ni = enumerators->next(); while (ni != enumerators) { JSObject *iterObj = ni->iterObj(); @@ -864,7 +856,6 @@ JSCompartment::clearTraps(FreeOp *fop) void JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *tiPendingArrays, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, @@ -876,7 +867,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *baselineStubsOptimized) { *compartmentObject += mallocSizeOf(this); - types.addSizeOfExcludingThis(mallocSizeOf, tiPendingArrays, tiAllocationSiteTables, + types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, tiArrayTypeTables, tiObjectTypeTables); *shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf) + initialShapes.sizeOfExcludingThis(mallocSizeOf) diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 15a93afe1280..f44c2b09e390 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -187,8 +187,6 @@ struct JSCompartment */ void adoptWorkerAllocator(js::Allocator *workerAllocator); - - int64_t lastCodeRelease; bool activeAnalysis; /* Type information about the scripts and objects in this compartment. */ @@ -221,7 +219,6 @@ struct JSCompartment public: void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *tiPendingArrays, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index dd3cde985825..27242bdcdb39 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2854,13 +2854,9 @@ ShouldPreserveJITCode(JSCompartment *comp, int64_t currentTime) if (rt->alwaysPreserveCode) return true; - if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime && - comp->lastCodeRelease + (PRMJ_USEC_PER_SEC * 300) >= currentTime) - { + if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime) return true; - } - comp->lastCodeRelease = currentTime; return false; } @@ -3939,12 +3935,12 @@ BeginSweepingZoneGroup(JSRuntime *rt) bool releaseTypes = ReleaseObservedTypes(rt); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex); - c->sweep(&fop, releaseTypes); + c->sweep(&fop, releaseTypes && !c->zone()->isPreservingCode()); } for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex); - zone->sweep(&fop, releaseTypes); + zone->sweep(&fop, releaseTypes && !zone->isPreservingCode()); } } @@ -5293,10 +5289,6 @@ js::ReleaseAllJITCode(FreeOp *fop) jit::FinishDiscardBaselineScript(fop, script); } } - - /* Sweep now invalidated compiler outputs from each compartment. */ - for (CompartmentsIter comp(fop->runtime(), SkipAtoms); !comp.done(); comp.next()) - comp->types.clearCompilerOutputs(fop); #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 2e5cbe360930..9159f31ec024 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1091,7 +1091,6 @@ struct GCMarker : public JSTracer { ObjectTag, TypeTag, XmlTag, - ArenaTag, SavedValueArrayTag, IonCodeTag, LastTag = IonCodeTag @@ -1119,10 +1118,6 @@ struct GCMarker : public JSTracer { pushTaggedPtr(ObjectTag, obj); } - void pushArenaList(gc::ArenaHeader *firstArena) { - pushTaggedPtr(ArenaTag, firstArena); - } - void pushType(types::TypeObject *type) { pushTaggedPtr(TypeTag, type); } @@ -1229,7 +1224,7 @@ struct GCMarker : public JSTracer { bool restoreValueArray(JSObject *obj, void **vpp, void **endp); void saveValueRanges(); inline void processMarkStackTop(SliceBudget &budget); - void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr); + void processMarkStackOther(uintptr_t tag, uintptr_t addr); void appendGrayRoot(void *thing, JSGCTraceKind kind); diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 0c1fe0167a3c..51c19af54640 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -243,14 +243,6 @@ types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &val if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) return true; - /* - * If we called in here while resolving a type constraint, we may be in the - * middle of resolving a standard class and the type sets will not be updated - * until the outer TypeSet::add finishes. - */ - if (cx->compartment()->types.pendingCount) - return true; - Type type = GetValueType(value); AutoEnterAnalysis enter(cx); @@ -742,12 +734,12 @@ class TypeCompilerConstraint : public TypeConstraint void newType(JSContext *cx, TypeSet *source, Type type) { if (data.invalidateOnNewType(type)) - cx->compartment()->types.addPendingRecompile(cx, compilation); + cx->zone()->types.addPendingRecompile(cx, compilation); } void newPropertyState(JSContext *cx, TypeSet *source) { if (data.invalidateOnNewPropertyState(source)) - cx->compartment()->types.addPendingRecompile(cx, compilation); + cx->zone()->types.addPendingRecompile(cx, compilation); } void newObjectState(JSContext *cx, TypeObject *object) { @@ -755,7 +747,16 @@ class TypeCompilerConstraint : public TypeConstraint // will be sent on changes to its state, so always invalidate any // associated compilations. if (object->unknownProperties() || data.invalidateOnNewObjectState(object)) - cx->compartment()->types.addPendingRecompile(cx, compilation); + cx->zone()->types.addPendingRecompile(cx, compilation); + } + + TypeConstraint *sweep(TypeZone &zone) { + if (data.shouldSweep() || compilation.shouldSweep(zone)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_ >(compilation, data); + if (!res) + zone.setPendingNukeTypes(); + return res; } }; @@ -908,13 +909,21 @@ class TypeConstraintFreezeStack : public TypeConstraint const char *kind() { return "freezeStack"; } - void newType(JSContext *cx, TypeSet *source, Type type) - { + void newType(JSContext *cx, TypeSet *source, Type type) { /* * Unlike TypeConstraintFreeze, triggering this constraint once does * not disable it on future changes to the type set. */ - cx->compartment()->types.addPendingRecompile(cx, script_); + cx->zone()->types.addPendingRecompile(cx, script_); + } + + TypeConstraint *sweep(TypeZone &zone) { + if (IsScriptAboutToBeFinalized(&script_)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_(script_); + if (!res) + zone.setPendingNukeTypes(); + return res; } }; @@ -929,15 +938,22 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu CompilerOutput co(script, executionMode); - TypeCompartment &types = cx->compartment()->types; - if (!types.constrainedOutputs) { - types.constrainedOutputs = cx->new_< Vector >(cx); - if (!types.constrainedOutputs) + TypeZone &types = cx->zone()->types; + if (!types.compilerOutputs) { + types.compilerOutputs = cx->new_< Vector >(cx); + if (!types.compilerOutputs) return false; } - uint32_t index = types.constrainedOutputs->length(); - if (!types.constrainedOutputs->append(co)) +#ifdef DEBUG + for (size_t i = 0; i < types.compilerOutputs->length(); i++) { + const CompilerOutput &co = (*types.compilerOutputs)[i]; + JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode); + } +#endif + + uint32_t index = types.compilerOutputs->length(); + if (!types.compilerOutputs->append(co)) return false; *precompileInfo = RecompileInfo(index); @@ -979,8 +995,8 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu array[i].add(cx, cx->typeLifoAlloc().new_(entry.script), false); } - if (!succeeded || types.constrainedOutputs->back().pendingInvalidation()) { - types.constrainedOutputs->back().invalidate(); + if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) { + types.compilerOutputs->back().invalidate(); script->resetUseCount(); return false; } @@ -1009,6 +1025,8 @@ class ConstraintDataFreeze ? property.maybeTypes()->isSubset(expected) : property.maybeTypes()->empty(); } + + bool shouldSweep() { return false; } }; } /* anonymous namespace */ @@ -1195,6 +1213,8 @@ class ConstraintDataFreezeObjectFlags { return !invalidateOnNewObjectState(property.object()->maybeType()); } + + bool shouldSweep() { return false; } }; } /* anonymous namespace */ @@ -1289,6 +1309,8 @@ class ConstraintDataFreezeObjectForInlinedCall { return true; } + + bool shouldSweep() { return false; } }; // Constraint which triggers recompilation when the template object for a @@ -1315,6 +1337,11 @@ class ConstraintDataFreezeObjectForNewScriptTemplate { return !invalidateOnNewObjectState(property.object()->maybeType()); } + + bool shouldSweep() { + // Note: |templateObject| is only used for equality testing. + return false; + } }; // Constraint which triggers recompilation when the underlying data pointer for @@ -1341,6 +1368,11 @@ class ConstraintDataFreezeObjectForTypedArrayBuffer { return !invalidateOnNewObjectState(property.object()->maybeType()); } + + bool shouldSweep() { + // Note: |viewData| is only used for equality testing. + return false; + } }; } /* anonymous namespace */ @@ -1413,10 +1445,7 @@ namespace { class ConstraintDataFreezeConfiguredProperty { public: - TypeObjectKey *object; - - ConstraintDataFreezeConfiguredProperty(TypeObjectKey *object) - : object(object) + ConstraintDataFreezeConfiguredProperty() {} const char *kind() { return "freezeConfiguredProperty"; } @@ -1430,31 +1459,16 @@ class ConstraintDataFreezeConfiguredProperty bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) { - // Everywhere compiled code depends on definite properties associated - // with a type object's newScript, we need to make sure there are - // constraints in place which will mark those properties as configured - // should the definite properties be invalidated. - TypeObject *type = object->isSingleObject() - ? object->asSingleObject()->type() - : object->asTypeObject(); - if (type->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { - type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; - if (type->hasNewScript()) { - CheckNewScriptProperties(cx, type, type->newScript()->fun); - } else { - JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED); - type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; - } - } - return !property.maybeTypes()->configuredProperty(); } + + bool shouldSweep() { return false; } }; } /* anonymous namespace */ bool -HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type) +HeapTypeSetKey::configured(CompilerConstraintList *constraints) { if (maybeTypes() && maybeTypes()->configuredProperty()) return true; @@ -1463,7 +1477,7 @@ HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *t typedef CompilerConstraintInstance T; constraints->add(alloc->new_(alloc, *this, - ConstraintDataFreezeConfiguredProperty(type))); + ConstraintDataFreezeConfiguredProperty())); return false; } @@ -1986,7 +2000,7 @@ PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj) if (type->unknownProperties()) return true; HeapTypeSetKey index = type->property(JSID_VOID); - if (index.configured(constraints, type) || index.isOwnProperty(constraints)) + if (index.configured(constraints) || index.isOwnProperty(constraints)) return true; obj = obj->getProto(); } while (obj); @@ -2024,27 +2038,8 @@ types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, return PrototypeHasIndexedProperty(constraints, proto); } -bool -TypeCompartment::growPendingArray(JSContext *cx) -{ - unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2); - PendingWork *newArray = js_pod_calloc(newCapacity); - if (!newArray) { - cx->compartment()->types.setPendingNukeTypes(cx); - return false; - } - - PodCopy(newArray, pendingArray, pendingCount); - js_free(pendingArray); - - pendingArray = newArray; - pendingCapacity = newCapacity; - - return true; -} - void -TypeCompartment::processPendingRecompiles(FreeOp *fop) +TypeZone::processPendingRecompiles(FreeOp *fop) { if (!pendingRecompiles) return; @@ -2095,11 +2090,9 @@ TypeZone::nukeTypes(FreeOp *fop) */ JS_ASSERT(pendingNukeTypes); - for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) { - if (comp->types.pendingRecompiles) { - fop->free_(comp->types.pendingRecompiles); - comp->types.pendingRecompiles = nullptr; - } + if (pendingRecompiles) { + fop->free_(pendingRecompiles); + pendingRecompiles = nullptr; } inferenceEnabled = false; @@ -2119,7 +2112,7 @@ TypeZone::nukeTypes(FreeOp *fop) } void -TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) +TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info) { CompilerOutput *co = info.compilerOutput(cx); if (!co || !co->isValid() || co->pendingInvalidation()) @@ -2145,7 +2138,7 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) } void -TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script) +TypeZone::addPendingRecompile(JSContext *cx, JSScript *script) { JS_ASSERT(script); @@ -3146,6 +3139,15 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint } void newType(JSContext *cx, TypeSet *source, Type type) {} + + TypeConstraint *sweep(TypeZone &zone) { + if (IsTypeObjectAboutToBeFinalized(&object)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_(object); + if (!res) + zone.setPendingNukeTypes(); + return res; + } }; bool @@ -3192,6 +3194,15 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint if (source->baseFlags() || source->getObjectCount() > 1) object->clearAddendum(cx); } + + TypeConstraint *sweep(TypeZone &zone) { + if (IsTypeObjectAboutToBeFinalized(&object)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_(object); + if (!res) + zone.setPendingNukeTypes(); + return res; + } }; void @@ -3941,8 +3952,19 @@ ConstraintTypeSet::sweep(Zone *zone) } } - /* All constraints are wiped out on each GC. */ + /* + * Type constraints only hold weak references. Copy constraints referring + * to data that is still live into the zone's new arena. + */ + TypeConstraint *constraint = constraintList; constraintList = nullptr; + while (constraint) { + if (TypeConstraint *copy = constraint->sweep(zone->types)) { + copy->next = constraintList; + constraintList = copy; + } + constraint = constraint->next; + } } inline void @@ -3962,18 +3984,6 @@ TypeObject::clearProperties() inline void TypeObject::sweep(FreeOp *fop) { - if (singleton) { - JS_ASSERT(!hasNewScript()); - - /* - * All properties can be discarded. We will regenerate them as needed - * as code gets reanalyzed. - */ - clearProperties(); - - return; - } - if (!isMarked()) { if (addendum) fop->free_(addendum); @@ -3996,6 +4006,14 @@ TypeObject::sweep(FreeOp *fop) for (unsigned i = 0; i < oldCapacity; i++) { Property *prop = oldArray[i]; if (prop) { + if (singleton && !prop->types.constraintList) { + /* + * Don't copy over properties of singleton objects which + * don't have associated constraints. The contents of these + * type sets will be regenerated as necessary. + */ + continue; + } Property *newProp = typeLifoAlloc.new_(*prop); if (newProp) { Property **pentry = @@ -4015,12 +4033,17 @@ TypeObject::sweep(FreeOp *fop) setBasePropertyCount(propertyCount); } else if (propertyCount == 1) { Property *prop = (Property *) propertySet; - Property *newProp = typeLifoAlloc.new_(*prop); - if (newProp) { - propertySet = (Property **) newProp; - newProp->types.sweep(zone()); + if (singleton && !prop->types.constraintList) { + // Skip, as above. + clearProperties(); } else { - zone()->types.setPendingNukeTypes(); + Property *newProp = typeLifoAlloc.new_(*prop); + if (newProp) { + propertySet = (Property **) newProp; + newProp->types.sweep(zone()); + } else { + zone()->types.setPendingNukeTypes(); + } } } @@ -4028,14 +4051,6 @@ TypeObject::sweep(FreeOp *fop) for (unsigned i = 0; i < basePropertyCount(); i++) JS_ASSERT(propertySet[i]); } - - /* - * The GC will clear out the constraints ensuring the correctness of the - * newScript information, these constraints will need to be regenerated - * the next time we compile code which depends on this info. - */ - if (hasNewScript()) - flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE; } void @@ -4119,52 +4134,6 @@ TypeCompartment::sweep(FreeOp *fop) e.rekeyFront(key); } } - - /* - * The pending array is reset on GC, it can grow large (75+ KB) and is easy - * to reallocate if the compartment becomes active again. - */ - if (pendingArray) - fop->free_(pendingArray); - - pendingArray = nullptr; - pendingCapacity = 0; -} - -void -TypeCompartment::sweepShapes(FreeOp *fop) -{ - /* - * Sweep any weak shape references that may be finalized even if a GC is - * preserving type information. - */ - if (objectTypeTable) { - for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) { - const ObjectTableKey &key = e.front().key(); - ObjectTableEntry &entry = e.front().value(); - - if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) { - fop->free_(key.properties); - fop->free_(entry.types); - e.removeFront(); - } - } - } -} - -void -TypeCompartment::clearCompilerOutputs(FreeOp *fop) -{ - if (constrainedOutputs) { - fop->delete_(constrainedOutputs); - constrainedOutputs = nullptr; - } - - if (pendingRecompiles) { - JS_ASSERT(pendingRecompiles->length() == 0); - fop->delete_(pendingRecompiles); - pendingRecompiles = nullptr; - } } void @@ -4193,7 +4162,6 @@ JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table) TypeCompartment::~TypeCompartment() { - js_free(pendingArray); js_delete(arrayTypeTable); js_delete(objectTypeTable); js_delete(allocationSiteTable); @@ -4212,12 +4180,6 @@ TypeScript::Sweep(FreeOp *fop, JSScript *script) /* Remove constraints and references to dead objects from the persistent type sets. */ for (unsigned i = 0; i < num; i++) typeArray[i].sweep(compartment->zone()); - - /* - * Freeze constraints on stack type sets need to be regenerated the next - * time the script is analyzed. - */ - script->clearHasFreezeConstraints(); } void @@ -4234,20 +4196,10 @@ Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePoo void TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *pendingArrays, size_t *allocationSiteTables, size_t *arrayTypeTables, size_t *objectTypeTables) { - /* Pending arrays are cleared on GC along with the analysis pool. */ - *pendingArrays += mallocSizeOf(pendingArray); - - /* - * TypeCompartment::pendingRecompiles is non-nullptr only while inference - * code is running. - */ - JS_ASSERT(!pendingRecompiles); - if (allocationSiteTable) *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); @@ -4289,6 +4241,8 @@ TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const TypeZone::TypeZone(Zone *zone) : zone_(zone), typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), + compilerOutputs(nullptr), + pendingRecompiles(nullptr), pendingNukeTypes(false), inferenceEnabled(false) { @@ -4296,6 +4250,8 @@ TypeZone::TypeZone(Zone *zone) TypeZone::~TypeZone() { + js_delete(compilerOutputs); + js_delete(pendingRecompiles); } void @@ -4312,11 +4268,21 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize()); oldAlloc.steal(&typeLifoAlloc); - /* - * Sweep analysis information and everything depending on it from the - * compartment, including all remaining mjit code if inference is - * enabled in the compartment. - */ + /* Sweep and find compressed indexes for each compiler output. */ + size_t newCompilerOutputCount = 0; + if (compilerOutputs) { + for (size_t i = 0; i < compilerOutputs->length(); i++) { + CompilerOutput &output = (*compilerOutputs)[i]; + if (output.isValid()) { + JSScript *script = output.script(); + if (IsScriptAboutToBeFinalized(&script)) + output.invalidate(); + else + output.setSweepIndex(newCompilerOutputCount++); + } + } + } + if (inferenceEnabled) { gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI); @@ -4328,6 +4294,21 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) if (releaseTypes) { script->types->destroy(); script->types = nullptr; + + /* + * Freeze constraints on stack type sets need to be + * regenerated the next time the script is analyzed. + */ + script->clearHasFreezeConstraints(); + + JS_ASSERT(!script->hasIonScript()); + JS_ASSERT(!script->hasParallelIonScript()); + } else { + /* Update the recompile indexes in any IonScripts still on the script. */ + if (script->hasIonScript()) + script->ionScript()->recompileInfoRef().shouldSweep(*this); + if (script->hasParallelIonScript()) + script->parallelIonScript()->recompileInfoRef().shouldSweep(*this); } } } @@ -4347,6 +4328,20 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) comp->types.sweep(fop); } + if (compilerOutputs) { + size_t sweepIndex = 0; + for (size_t i = 0; i < compilerOutputs->length(); i++) { + CompilerOutput output = (*compilerOutputs)[i]; + if (output.isValid()) { + JS_ASSERT(sweepIndex == output.sweepIndex()); + output.setSweepIndex(0); + (*compilerOutputs)[sweepIndex++] = output; + } + } + JS_ASSERT(sweepIndex == newCompilerOutputCount); + JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount)); + } + { gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS); for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) { diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 690631dfa7ad..38c3be127e82 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -177,7 +177,7 @@ namespace analyze { namespace types { -class TypeCompartment; +class TypeZone; class TypeSet; class TypeObjectKey; @@ -278,28 +278,10 @@ inline Type GetValueType(const Value &val); * Type information about the values observed within scripts and about the * contents of the heap is accumulated as the program executes. Compilation * accumulates constraints relating type information on the heap with the - * compilations that should be invalidated when those types change. This data - * is periodically cleared to reduce memory usage. - * - * GCs may clear both analysis information and jitcode. Sometimes GCs will - * preserve all information and code, and will not collect any scripts, type - * objects or singleton JS objects. - * - * The following data is cleared by all non-preserving GCs: - * - * - The ScriptAnalysis for each analyzed script and data from each analysis - * pass performed. - * - * - Property type sets for singleton JS objects. - * - * - Type constraints and dead references in all type sets. - * - * The following data is occasionally cleared by non-preserving GCs: - * - * - TypeScripts and their type sets are occasionally destroyed, per a timer. - * - * - When a JSScript or TypeObject is swept, type information for its contents - * is destroyed. + * compilations that should be invalidated when those types change. Type + * information and constraints are allocated in the zone's typeLifoAlloc, + * and on GC all data referring to live things is copied into a new allocator. + * Thus, type set and constraints only hold weak references. */ /* @@ -334,6 +316,12 @@ public: * state. */ virtual void newObjectState(JSContext *cx, TypeObject *object) {} + + /* + * If the data this constraint refers to is still live, copy it into the + * zone's new allocator. Type constraints only hold weak references. + */ + virtual TypeConstraint *sweep(TypeZone &zone) = 0; }; /* Flags and other state stored in TypeSet::flags */ @@ -394,13 +382,6 @@ enum { /* If set, addendum information should not be installed on this object. */ OBJECT_FLAG_ADDENDUM_CLEARED = 0x2, - /* - * If set, type constraints covering the correctness of the newScript - * definite properties need to be regenerated before compiling any jitcode - * which depends on this information. - */ - OBJECT_FLAG_NEW_SCRIPT_REGENERATE = 0x4, - /* * Whether we have ensured all type sets in the compartment contain * ANYOBJECT instead of this object. @@ -869,12 +850,6 @@ struct TypeTypedObject : public TypeObjectAddendum * information is sensitive to changes in the property's type. Future changes * to the property (whether those uncovered by analysis or those occurring * in the VM) will treat these properties like those of any other type object. - * - * When a GC occurs, we wipe out all analysis information for all the - * compartment's scripts, so can destroy all properties on singleton type - * objects at the same time. If there is no reference on the stack to the - * type object itself, the type object is also destroyed, and the JS object - * reverts to having a lazy type. */ /* Type information about an object accessed by a script. */ @@ -1316,7 +1291,7 @@ class HeapTypeSetKey void freeze(CompilerConstraintList *constraints); JSValueType knownTypeTag(CompilerConstraintList *constraints); - bool configured(CompilerConstraintList *constraints, TypeObjectKey *type); + bool configured(CompilerConstraintList *constraints); bool isOwnProperty(CompilerConstraintList *constraints); bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other); JSObject *singleton(CompilerConstraintList *constraints); @@ -1339,6 +1314,10 @@ class CompilerOutput // Whether this compilation is about to be invalidated. bool pendingInvalidation_ : 1; + // During sweeping, the list of compiler outputs is compacted and invalidated + // outputs are removed. This gives the new index for a valid compiler output. + uint32_t sweepIndex_ : 29; + public: CompilerOutput() : script_(nullptr), mode_(SequentialExecution), pendingInvalidation_(false) @@ -1366,6 +1345,15 @@ class CompilerOutput bool pendingInvalidation() { return pendingInvalidation_; } + + void setSweepIndex(uint32_t index) { + if (index >= 1 << 29) + MOZ_CRASH(); + sweepIndex_ = index; + } + uint32_t sweepIndex() { + return sweepIndex_; + } }; class RecompileInfo @@ -1380,8 +1368,9 @@ class RecompileInfo bool operator == (const RecompileInfo &o) const { return outputIndex == o.outputIndex; } - CompilerOutput *compilerOutput(TypeCompartment &types) const; + CompilerOutput *compilerOutput(TypeZone &types) const; CompilerOutput *compilerOutput(JSContext *cx) const; + bool shouldSweep(TypeZone &types); }; /* Type information for a compartment. */ @@ -1389,32 +1378,9 @@ struct TypeCompartment { /* Constraint solving worklist structures. */ - /* - * Worklist of types which need to be propagated to constraints. We use a - * worklist to avoid blowing the native stack. - */ - struct PendingWork - { - TypeConstraint *constraint; - ConstraintTypeSet *source; - Type type; - }; - PendingWork *pendingArray; - unsigned pendingCount; - unsigned pendingCapacity; - - /* Whether we are currently resolving the pending worklist. */ - bool resolving; - /* Number of scripts in this compartment. */ unsigned scriptCount; - /* Valid & Invalid script referenced by type constraints. */ - Vector *constrainedOutputs; - - /* Pending recompilations to perform before execution of JIT code can resume. */ - Vector *pendingRecompiles; - /* Table for referencing types of objects keyed to an allocation site. */ AllocationSiteTable *allocationSiteTable; @@ -1438,14 +1404,6 @@ struct TypeCompartment inline JSCompartment *compartment(); - /* Add a type to register with a list of constraints. */ - inline void addPending(JSContext *cx, TypeConstraint *constraint, - ConstraintTypeSet *source, Type type); - bool growPendingArray(JSContext *cx); - - /* Resolve pending type registrations, excluding delayed ones. */ - inline void resolvePending(JSContext *cx); - /* Prints results of this compartment if spew is enabled or force is set. */ void print(JSContext *cx, bool force); @@ -1461,26 +1419,16 @@ struct TypeCompartment /* Get or make an object for an allocation site, and add to the allocation site table. */ TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key); - void processPendingRecompiles(FreeOp *fop); - /* Mark all types as needing destruction once inference has 'finished'. */ void setPendingNukeTypes(ExclusiveContext *cx); - /* Mark a script as needing recompilation once inference has finished. */ - void addPendingRecompile(JSContext *cx, const RecompileInfo &info); - void addPendingRecompile(JSContext *cx, JSScript *script); - /* Mark any type set containing obj as having a generic object type. */ void markSetsUnknown(JSContext *cx, TypeObject *obj); void sweep(FreeOp *fop); - void sweepShapes(FreeOp *fop); - void clearCompilerOutputs(FreeOp *fop); - void finalizeObjects(); void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *pendingArrays, size_t *allocationSiteTables, size_t *arrayTypeTables, size_t *objectTypeTables); @@ -1496,6 +1444,16 @@ struct TypeZone static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024; js::LifoAlloc typeLifoAlloc; + /* + * All Ion compilations that have occured in this zone, for indexing via + * RecompileInfo. This includes both valid and invalid compilations, though + * invalidated compilations are swept on GC. + */ + Vector *compilerOutputs; + + /* Pending recompilations to perform before execution of JIT code can resume. */ + Vector *pendingRecompiles; + /* * Bit set if all current types must be marked as unknown, and all scripts * recompiled. Caused by OOM failure within inference operations. @@ -1516,6 +1474,12 @@ struct TypeZone /* Mark all types as needing destruction once inference has 'finished'. */ void setPendingNukeTypes(); + /* Mark a script as needing recompilation once inference has finished. */ + void addPendingRecompile(JSContext *cx, const RecompileInfo &info); + void addPendingRecompile(JSContext *cx, JSScript *script); + + void processPendingRecompiles(FreeOp *fop); + void nukeTypes(FreeOp *fop); }; diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index c4e07abd3271..328daf0a076c 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -104,17 +104,29 @@ CompilerOutput::ion() const } inline CompilerOutput* -RecompileInfo::compilerOutput(TypeCompartment &types) const +RecompileInfo::compilerOutput(TypeZone &types) const { - if (!types.constrainedOutputs || outputIndex >= types.constrainedOutputs->length()) + if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length()) return nullptr; - return &(*types.constrainedOutputs)[outputIndex]; + return &(*types.compilerOutputs)[outputIndex]; } inline CompilerOutput* RecompileInfo::compilerOutput(JSContext *cx) const { - return compilerOutput(cx->compartment()->types); + return compilerOutput(cx->zone()->types); +} + +inline bool +RecompileInfo::shouldSweep(TypeZone &types) +{ + CompilerOutput *output = compilerOutput(types); + if (!output || !output->isValid()) + return true; + + // Update this info for the output's new index in the zone's compiler outputs. + outputIndex = output->sweepIndex(); + return false; } ///////////////////////////////////////////////////////////////////// @@ -288,14 +300,13 @@ struct AutoEnterAnalysis * If there are no more type inference activations on the stack, * process any triggered recompilations. Note that we should not be * invoking any scripted code while type inference is running. - * :TODO: assert this. */ if (!compartment->activeAnalysis) { - TypeCompartment *types = &compartment->types; - if (compartment->zone()->types.pendingNukeTypes) - compartment->zone()->types.nukeTypes(freeOp); - else if (types->pendingRecompiles) - types->processPendingRecompiles(freeOp); + TypeZone &types = compartment->zone()->types; + if (types.pendingNukeTypes) + types.nukeTypes(freeOp); + else if (types.pendingRecompiles) + types.processPendingRecompiles(freeOp); } } @@ -852,50 +863,6 @@ TypeCompartment::compartment() return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); } -inline void -TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, - ConstraintTypeSet *source, Type type) -{ - JS_ASSERT(this == &cx->compartment()->types); - JS_ASSERT(!cx->runtime()->isHeapBusy()); - - InferSpew(ISpewOps, "pending: %sC%p%s %s", - InferSpewColor(constraint), constraint, InferSpewColorReset(), - TypeString(type)); - - if ((pendingCount == pendingCapacity) && !growPendingArray(cx)) - return; - - PendingWork &pending = pendingArray[pendingCount++]; - pending.constraint = constraint; - pending.source = source; - pending.type = type; -} - -inline void -TypeCompartment::resolvePending(JSContext *cx) -{ - JS_ASSERT(this == &cx->compartment()->types); - - if (resolving) { - /* There is an active call further up resolving the worklist. */ - return; - } - - resolving = true; - - /* Handle all pending type registrations. */ - while (pendingCount) { - const PendingWork &pending = pendingArray[--pendingCount]; - InferSpew(ISpewOps, "resolve: %sC%p%s %s", - InferSpewColor(pending.constraint), pending.constraint, - InferSpewColorReset(), TypeString(pending.type)); - pending.constraint->newType(cx, pending.source, pending.type); - } - - resolving = false; -} - ///////////////////////////////////////////////////////////////////// // TypeSet ///////////////////////////////////////////////////////////////////// @@ -1219,10 +1186,9 @@ ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type) if (JSContext *cx = cxArg->maybeJSContext()) { TypeConstraint *constraint = constraintList; while (constraint) { - cx->compartment()->types.addPending(cx, constraint, this, type); + constraint->newType(cx, this, type); constraint = constraint->next; } - cx->compartment()->types.resolvePending(cx); } else { JS_ASSERT(!constraintList); } diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index 3ba7d433c6f8..ee09906df5bd 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -215,7 +215,6 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment) // Measure the compartment object itself, and things hanging off it. compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_, - &cStats.typeInferencePendingArrays, &cStats.typeInferenceAllocationSiteTables, &cStats.typeInferenceArrayTypeTables, &cStats.typeInferenceObjectTypeTables, diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 1a12e11d83b3..b225830e8efe 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2122,10 +2122,6 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats, cStats.typeInferenceTypeScripts, "Memory used by type sets associated with scripts."); - ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/pending-arrays"), - cStats.typeInferencePendingArrays, - "Memory used for solving constraints during type inference."); - ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"), cStats.typeInferenceAllocationSiteTables, "Memory indexing type objects associated with allocation sites."); From 5b212815ea2bfe748287fc23cc77ae0da155e7ba Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 12 Dec 2013 16:11:54 -0500 Subject: [PATCH 048/459] Bug 948096. Warn when people use window._content. r=smaug,myk --- content/base/public/nsDeprecatedOperationList.h | 1 + dom/base/nsGlobalWindow.h | 3 +++ dom/locales/en-US/chrome/dom/dom.properties | 2 ++ webapprt/locales/en-US/webapprt/overrides/dom.properties | 2 ++ 4 files changed, 8 insertions(+) diff --git a/content/base/public/nsDeprecatedOperationList.h b/content/base/public/nsDeprecatedOperationList.h index f0e2c0c6ee38..f8a8433cd02b 100644 --- a/content/base/public/nsDeprecatedOperationList.h +++ b/content/base/public/nsDeprecatedOperationList.h @@ -40,3 +40,4 @@ DEPRECATED_OPERATION(UseOfDOM3LoadMethod) DEPRECATED_OPERATION(ShowModalDialog) DEPRECATED_OPERATION(UnsafeCloneNode) DEPRECATED_OPERATION(UnsafeImportNode) +DEPRECATED_OPERATION(Window_Content) diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 524bbba79674..2218c0e02fc4 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -946,6 +946,9 @@ public: JSObject* GetContent(JSContext* aCx, mozilla::ErrorResult& aError); JSObject* Get_content(JSContext* aCx, mozilla::ErrorResult& aError) { + if (mDoc) { + mDoc->WarnOnceAbout(nsIDocument::eWindow_Content); + } return GetContent(aCx, aError); } diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 9e11a09e59c9..9a4245fba00b 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -146,3 +146,5 @@ ShowModalDialogWarning=Use of window.showModalDialog() is deprecated. Use window UnsafeCloneNodeWarning=The behavior of cloneNode() with no boolean argument is about to change from doing a deep clone to doing a shallow clone. Make sure to pass an explicit boolean argument to keep your current behavior. # LOCALIZATION NOTE: Do not translate "importNode()" UnsafeImportNodeWarning=The behavior of importNode() with no boolean argument is about to change from doing a deep clone to doing a shallow clone. Make sure to pass an explicit boolean argument to keep your current behavior. +# LOCALIZATION NOTE: Do not translate "window._content" or "window.content" +Window_ContentWarning=window._content is deprecated. Please use window.content instead. diff --git a/webapprt/locales/en-US/webapprt/overrides/dom.properties b/webapprt/locales/en-US/webapprt/overrides/dom.properties index 732431b4e827..08870f7479be 100644 --- a/webapprt/locales/en-US/webapprt/overrides/dom.properties +++ b/webapprt/locales/en-US/webapprt/overrides/dom.properties @@ -146,3 +146,5 @@ ShowModalDialogWarning=Use of window.showModalDialog() is deprecated. Use window UnsafeCloneNodeWarning=The behavior of cloneNode() with no boolean argument is about to change from doing a deep clone to doing a shallow clone. Make sure to pass an explicit boolean argument to keep your current behavior. # LOCALIZATION NOTE: Do not translate "importNode()" UnsafeImportNodeWarning=The behavior of importNode() with no boolean argument is about to change from doing a deep clone to doing a shallow clone. Make sure to pass an explicit boolean argument to keep your current behavior. +# LOCALIZATION NOTE: Do not translate "window._content" or "window.content" +Window_ContentWarning=window._content is deprecated. Please use window.content instead. From 3e0c5f04f29bab34aaaa495bcb74aa6dc74fe4aa Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 12 Dec 2013 16:11:55 -0500 Subject: [PATCH 049/459] Bug 949264 part 1. Share more codegen between events and CGNativeMember. r=smaug --- dom/bindings/Codegen.py | 72 +++++++++-------------------------------- 1 file changed, 15 insertions(+), 57 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index d9d1ef26ee68..3bc27bf59c4e 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -11435,71 +11435,33 @@ class CGEventGetter(CGNativeMember): attr), (attr.type, []), ea) - - def retval(self, type): - if type.isPrimitive() and type.tag() in builtinNames: - result = CGGeneric(builtinNames[type.tag()]) - if type.nullable(): - result = CGTemplatedType("Nullable", result) - return result.define() - if type.isDOMString(): - return "void" - if type.isByteString(): - return "void" - if type.isEnum(): - enumName = type.unroll().inner.identifier.name - if type.nullable(): - enumName = CGTemplatedType("Nullable", - CGGeneric(enumName)).define() - return enumName - if type.isGeckoInterface(): - iface = type.unroll().inner; - nativeType = self.descriptorProvider.getDescriptor( - iface.identifier.name).nativeType - # Now trim off unnecessary namespaces - nativeType = nativeType.split("::") - if nativeType[0] == "mozilla": - nativeType.pop(0) - if nativeType[0] == "dom": - nativeType.pop(0) - return CGWrapper(CGGeneric("::".join(nativeType)), post="*").define() - if type.isAny(): - return "JS::Value" - if type.isObject(): - return "JSObject*" - if type.isSpiderMonkeyInterface(): - return "JSObject*" - raise TypeError("Don't know how to declare return value for %s" % - type) + self.body = self.getMethodBody() def getArgs(self, returnType, argList): - args = [self.getArg(arg) for arg in argList] - if returnType.isDOMString(): - args.append(Argument("nsString&", "aRetVal")) - elif returnType.isByteString(): - args.append(Argument("nsCString&", "aRetVal")) - if needCx(returnType, argList, self.extendedAttrs, True): - args.insert(0, Argument("JSContext*", "aCx")) if not 'infallible' in self.extendedAttrs: raise TypeError("Event code generator does not support [Throws]!") - return args + if not self.member.isAttr(): + raise TypeError("Event code generator does not support methods") + if self.member.isStatic(): + raise TypeError("Event code generators does not support static attributes") + return CGNativeMember.getArgs(self, returnType, argList) def getMethodBody(self): type = self.member.type memberName = CGDictionary.makeMemberName(self.member.identifier.name) if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface(): - return " return " + memberName + ";" + return "return " + memberName + ";" if type.isDOMString() or type.isByteString(): - return " aRetVal = " + memberName + ";"; + return "retval = " + memberName + ";"; if type.isSpiderMonkeyInterface() or type.isObject(): - ret = " if (%s) {\n" % memberName - ret += " JS::ExposeObjectToActiveJS(%s);\n" % memberName - ret += " }\n" - ret += " return " + memberName + ";" + ret = "if (%s) {\n" % memberName + ret += " JS::ExposeObjectToActiveJS(%s);\n" % memberName + ret += "}\n" + ret += "return " + memberName + ";" return ret; if type.isAny(): - ret = " JS::ExposeValueToActiveJS("+ memberName + ");\n" - ret += " return " + memberName + ";" + ret = "JS::ExposeValueToActiveJS("+ memberName + ");\n" + ret += "return " + memberName + ";" return ret; raise TypeError("Event code generator does not support this type!") @@ -11513,11 +11475,7 @@ class CGEventGetter(CGNativeMember): if getattr(self.member, "originatingInterface", cgClass.descriptor.interface) != cgClass.descriptor.interface: return "" - ret = self.retval(self.member.type); - methodName = self.descriptorProvider.name + '::' + self.name - args = (', '.join([a.declare() for a in self.args])) - body = self.getMethodBody() - return ret + "\n" + methodName + '(' + args + ') const\n{\n' + body + "\n}\n" + return CGNativeMember.define(self, cgClass) class CGEventSetter(CGNativeMember): def __init__(self): From 6f31ec040a7bb2378fde84101dd029a5e86c5b64 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 12 Dec 2013 16:11:55 -0500 Subject: [PATCH 050/459] Bug 949264 part 2. Support union members in generated events. r=smaug --- dom/bindings/Codegen.py | 50 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 3bc27bf59c4e..4302a7721e2b 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3277,7 +3277,7 @@ for (uint32_t i = 0; i < length; ++i) { exceptionCodeIndented.define())) templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}") - typeName = ("Owning" if isMember else "") + type.name + typeName = CGUnionStruct.unionTypeDecl(type, isMember) argumentTypeName = typeName + "Argument" if nullable: typeName = "Nullable<" + typeName + " >" @@ -4702,7 +4702,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider, resultArgs = None return result, True, None, resultArgs if returnType.isUnion(): - result = CGGeneric("Owning" + returnType.unroll().name) + result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True)) if not isMember and typeNeedsRooting(returnType): if returnType.nullable(): result = CGTemplatedType("NullableRootedUnion", result) @@ -6891,7 +6891,7 @@ class CGUnionStruct(CGThing): default=CGGeneric("return false;")).define(), const=True)) constructors = [ctor] - selfName = ("Owning" if self.ownsMembers else "") + str(self.type) + selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers) if self.ownsMembers: if len(traceCases): traceBody = CGSwitch("mType", traceCases, @@ -6950,6 +6950,28 @@ class CGUnionStruct(CGThing): def isUnionCopyConstructible(type): return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes) + @staticmethod + def unionTypeName(type, ownsMembers): + """ + Returns a string name for this known union type. + """ + assert type.isUnion() and not type.nullable() + return ("Owning" if ownsMembers else "") + type.name + + @staticmethod + def unionTypeDecl(type, ownsMembers): + """ + Returns a string for declaring this possibly-nullable union type. + """ + assert type.isUnion() + nullable = type.nullable() + if nullable: + type = type.inner + decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers)) + if nullable: + decl = CGTemplatedType("Nullable", decl) + return decl.define() + class CGUnionConversionStruct(CGThing): def __init__(self, type, descriptorProvider): CGThing.__init__(self) @@ -9854,6 +9876,14 @@ class CGNativeMember(ClassMethod): return CGDictionary.makeDictionaryName(type.inner), None, None # In this case we convert directly into our outparam to start with return "void", "", "" + if type.isUnion(): + if isMember: + # Only the first member of the tuple matters here, but return + # bogus values for the others in case someone decides to use + # them. + return CGUnionStruct.unionTypeDecl(type, True), None, None + # In this case we convert directly into our outparam to start with + return "void", "", "" raise TypeError("Don't know how to declare return value for %s" % type) @@ -9883,6 +9913,10 @@ class CGNativeMember(ClassMethod): if nullable: dictType = CGTemplatedType("Nullable", dictType) args.append(Argument("%s&" % dictType.define(), "retval")) + elif returnType.isUnion(): + args.append(Argument("%s&" % + CGUnionStruct.unionTypeDecl(returnType, True), + "retval")) # And the ErrorResult if not 'infallible' in self.extendedAttrs: # Use aRv so it won't conflict with local vars named "rv" @@ -11463,6 +11497,8 @@ class CGEventGetter(CGNativeMember): ret = "JS::ExposeValueToActiveJS("+ memberName + ");\n" ret += "return " + memberName + ";" return ret; + if type.isUnion(): + return "retval = " + memberName + ";" raise TypeError("Event code generator does not support this type!") def declare(self, cgClass): @@ -11612,6 +11648,11 @@ class CGEventClass(CGBindingImplClass): nativeType = "JS::Heap" elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): nativeType = "JS::Heap" + elif m.type.isUnion(): + nativeType = CGUnionStruct.unionTypeDecl(m.type, True) + else: + raise TypeError("Don't know how to declare member of type %s" % + m.type) members.append(ClassMember(CGDictionary.makeMemberName(m.identifier.name), nativeType, visibility="private", @@ -11673,6 +11714,9 @@ class CGEventClass(CGBindingImplClass): retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(" + name + ")\n" elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n" + elif typeNeedsRooting(m.type): + raise TypeError("Need to implement tracing for event " + "member of type %s" % m.type) return retVal def define(self): From ac153799bdf616a21fcc83022b6dc81f8dc9e3d9 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 12 Dec 2013 16:11:55 -0500 Subject: [PATCH 051/459] Bug 949264 part 3. Use aRetVal for the outparam return value for CGNativeMember. r=smaug --- dom/bindings/Codegen.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 4302a7721e2b..d94557fd328c 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3845,7 +3845,7 @@ for (uint32_t i = 0; i < length; ++i) { if not isMember and isCallbackReturnValue: # Go ahead and just convert directly into our actual return value declType = CGWrapper(declType, post="&") - declArgs = "retval" + declArgs = "aRetVal" elif not isMember and typeNeedsRooting(type): declType = CGTemplatedType("RootedDictionary", declType); declArgs = "cx" @@ -9789,13 +9789,13 @@ class CGNativeMember(ClassMethod): # No need for a third element in the isMember case return "nsString", None, None # Outparam - return "void", "", "retval = ${declName};" + return "void", "", "aRetVal = ${declName};" if type.isByteString(): if isMember: # No need for a third element in the isMember case return "nsCString", None, None # Outparam - return "void", "", "retval = ${declName};" + return "void", "", "aRetVal = ${declName};" if type.isEnum(): enumName = type.unroll().inner.identifier.name if type.nullable(): @@ -9855,12 +9855,12 @@ class CGNativeMember(ClassMethod): # Outparam. if type.nullable(): returnCode = ("if (${declName}.IsNull()) {\n" - " retval.SetNull();\n" + " aRetVal.SetNull();\n" "} else {\n" - " retval.SetValue().SwapElements(${declName}.Value());\n" + " aRetVal.SetValue().SwapElements(${declName}.Value());\n" "}") else: - returnCode = "retval.SwapElements(${declName});" + returnCode = "aRetVal.SwapElements(${declName});" return "void", "", returnCode if type.isDate(): result = CGGeneric("Date") @@ -9892,9 +9892,9 @@ class CGNativeMember(ClassMethod): args = [self.getArg(arg) for arg in argList] # Now the outparams if returnType.isDOMString(): - args.append(Argument("nsString&", "retval")) + args.append(Argument("nsString&", "aRetVal")) elif returnType.isByteString(): - args.append(Argument("nsCString&", "retval")) + args.append(Argument("nsCString&", "aRetVal")) elif returnType.isSequence(): nullable = returnType.nullable() if nullable: @@ -9904,7 +9904,7 @@ class CGNativeMember(ClassMethod): type = CGTemplatedType("nsTArray", CGGeneric(elementDecl)) if nullable: type = CGTemplatedType("Nullable", type) - args.append(Argument("%s&" % type.define(), "retval")) + args.append(Argument("%s&" % type.define(), "aRetVal")) elif returnType.isDictionary(): nullable = returnType.nullable() if nullable: @@ -9912,11 +9912,11 @@ class CGNativeMember(ClassMethod): dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner)) if nullable: dictType = CGTemplatedType("Nullable", dictType) - args.append(Argument("%s&" % dictType.define(), "retval")) + args.append(Argument("%s&" % dictType.define(), "aRetVal")) elif returnType.isUnion(): args.append(Argument("%s&" % CGUnionStruct.unionTypeDecl(returnType, True), - "retval")) + "aRetVal")) # And the ErrorResult if not 'infallible' in self.extendedAttrs: # Use aRv so it won't conflict with local vars named "rv" @@ -11486,7 +11486,7 @@ class CGEventGetter(CGNativeMember): if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface(): return "return " + memberName + ";" if type.isDOMString() or type.isByteString(): - return "retval = " + memberName + ";"; + return "aRetVal = " + memberName + ";"; if type.isSpiderMonkeyInterface() or type.isObject(): ret = "if (%s) {\n" % memberName ret += " JS::ExposeObjectToActiveJS(%s);\n" % memberName @@ -11498,7 +11498,7 @@ class CGEventGetter(CGNativeMember): ret += "return " + memberName + ";" return ret; if type.isUnion(): - return "retval = " + memberName + ";" + return "aRetVal = " + memberName + ";" raise TypeError("Event code generator does not support this type!") def declare(self, cgClass): From f5f63077e215d0ae945e9b9ff728316a076e0f0b Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 12 Dec 2013 16:11:56 -0500 Subject: [PATCH 052/459] Bug 949271. Fix callback and JS-implemented codegen for sequence-of-union and variadic-union arguments. r=smaug --- dom/bindings/Codegen.py | 11 +++++++---- dom/bindings/test/TestBindingHeader.h | 3 +++ dom/bindings/test/TestCodeGen.webidl | 3 +++ dom/bindings/test/TestExampleGen.webidl | 3 +++ dom/bindings/test/TestJSImplGen.webidl | 11 ++++++----- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index d94557fd328c..c32b821fa513 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -9506,7 +9506,10 @@ class CGForwardDeclarations(CGWrapper): elif t.isCallbackInterface(): builder.addInMozillaDom(t.inner.identifier.name) elif t.isUnion(): - builder.addInMozillaDom(str(t)) + # Forward declare both the owning and non-owning version, + # since we don't know which one we might want + builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False)) + builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, True)) # Don't need to do anything for void, primitive, string, any or object. # There may be some other cases we are missing. @@ -9961,9 +9964,9 @@ class CGNativeMember(ClassMethod): return decl.define(), True, True if type.isUnion(): - if type.nullable(): - type = type.inner - return str(type), True, True + # unionTypeDecl will handle nullable types, so return False for + # auto-wrapping in Nullable + return CGUnionStruct.unionTypeDecl(type, isMember), True, False if type.isGeckoInterface() and not type.isCallbackInterface(): iface = type.unroll().inner diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index 305cdb7a9b04..01b641c2bb4b 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -559,6 +559,9 @@ public: void PassNullableUnionWithDefaultValue11(const Nullable& arg); void PassNullableUnionWithDefaultValue12(const Nullable& arg); + void PassSequenceOfUnions(const Sequence&); + void PassVariadicUnion(const Sequence&); + void ReceiveUnion(OwningCanvasPatternOrCanvasGradient&); void ReceiveUnion2(JSContext*, OwningObjectOrLong&); void ReceiveUnionContainingNull(OwningCanvasPatternOrNullOrCanvasGradient&); diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl index a2c0f99b6af0..667395af6591 100644 --- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -505,6 +505,9 @@ interface TestInterface { void passNullableUnionWithDefaultValue11(optional (unrestricted float or DOMString)? arg = 1); void passNullableUnionWithDefaultValue12(optional (unrestricted float or DOMString)? arg = null); + void passSequenceOfUnions(sequence<(CanvasPattern or CanvasGradient)> arg); + void passVariadicUnion((CanvasPattern or CanvasGradient)... arg); + (CanvasPattern or CanvasGradient) receiveUnion(); (object or long) receiveUnion2(); (CanvasPattern? or CanvasGradient) receiveUnionContainingNull(); diff --git a/dom/bindings/test/TestExampleGen.webidl b/dom/bindings/test/TestExampleGen.webidl index 63777e4c26a5..45638ec4b62a 100644 --- a/dom/bindings/test/TestExampleGen.webidl +++ b/dom/bindings/test/TestExampleGen.webidl @@ -401,6 +401,9 @@ interface TestExampleInterface { void passNullableUnionWithDefaultValue11(optional (unrestricted float or DOMString)? arg = 1); void passNullableUnionWithDefaultValue12(optional (unrestricted float or DOMString)? arg = null); + void passSequenceOfUnions(sequence<(CanvasPattern or CanvasGradient)> arg); + void passVariadicUnion((CanvasPattern or CanvasGradient)... arg); + //(CanvasPattern or CanvasGradient) receiveUnion(); //(object or long) receiveUnion2(); //(CanvasPattern? or CanvasGradient) receiveUnionContainingNull(); diff --git a/dom/bindings/test/TestJSImplGen.webidl b/dom/bindings/test/TestJSImplGen.webidl index 6135630c82e7..db9535aa1748 100644 --- a/dom/bindings/test/TestJSImplGen.webidl +++ b/dom/bindings/test/TestJSImplGen.webidl @@ -380,12 +380,10 @@ interface TestJSImplInterface { void passUnion14(optional (object or long?) arg = 5); #endif void passUnionWithNullable((object? or long) arg); - // FIXME: Bug 863948 Nullable unions not supported yet - // void passNullableUnion((object or long)? arg); + void passNullableUnion((object or long)? arg); void passOptionalUnion(optional (object or long) arg); - // FIXME: Bug 863948 Nullable unions not supported yet - // void passOptionalNullableUnion(optional (object or long)? arg); - // void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null); + void passOptionalNullableUnion(optional (object or long)? arg); + void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null); //void passUnionWithInterfaces((TestJSImplInterface or TestExternalInterface) arg); //void passUnionWithInterfacesAndNullable((TestJSImplInterface? or TestExternalInterface) arg); //void passUnionWithSequence((sequence or long) arg); @@ -425,6 +423,9 @@ interface TestJSImplInterface { void passNullableUnionWithDefaultValue11(optional (unrestricted float or DOMString)? arg = 1); void passNullableUnionWithDefaultValue12(optional (unrestricted float or DOMString)? arg = null); + void passSequenceOfUnions(sequence<(CanvasPattern or CanvasGradient)> arg); + void passVariadicUnion((CanvasPattern or CanvasGradient)... arg); + //(CanvasPattern or CanvasGradient) receiveUnion(); //(object or long) receiveUnion2(); //(CanvasPattern? or CanvasGradient) receiveUnionContainingNull(); From 3676b948cfbd194bb5ea6332012b72b2b576b894 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 12 Dec 2013 16:11:56 -0500 Subject: [PATCH 053/459] Bug 949471. history.state should be null, not undefined, when there is no state. r=smaug --- dom/base/nsHistory.cpp | 2 +- dom/base/test/mochitest.ini | 1 + dom/base/test/test_history_state_null.html | 25 +++++++++++++++++++ .../mochitest/whatwg/test_bug500328.html | 12 ++++----- 4 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 dom/base/test/test_history_state_null.html diff --git a/dom/base/nsHistory.cpp b/dom/base/nsHistory.cpp index 66a31e9af083..a2c3f9d67c59 100644 --- a/dom/base/nsHistory.cpp +++ b/dom/base/nsHistory.cpp @@ -139,7 +139,7 @@ nsHistory::GetState(JSContext* aCx, ErrorResult& aRv) const return jsData; } - return JS::UndefinedValue(); + return JS::NullValue(); } void diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 51ded34a81c5..ab46010005f5 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -19,6 +19,7 @@ support-files = [test_gsp-quirks.html] [test_gsp-standards.html] [test_history_document_open.html] +[test_history_state_null.html] [test_innersize_scrollport.html] [test_messageChannel.html] [test_messageChannel_cloning.html] diff --git a/dom/base/test/test_history_state_null.html b/dom/base/test/test_history_state_null.html new file mode 100644 index 000000000000..9e53de749587 --- /dev/null +++ b/dom/base/test/test_history_state_null.html @@ -0,0 +1,25 @@ + + + + + + Test for Bug 949471 + + + + + +Mozilla Bug 949471 +

+ +
+
+ + diff --git a/dom/tests/mochitest/whatwg/test_bug500328.html b/dom/tests/mochitest/whatwg/test_bug500328.html index a7dbefb4ad61..bbfb5cf666e0 100644 --- a/dom/tests/mochitest/whatwg/test_bug500328.html +++ b/dom/tests/mochitest/whatwg/test_bug500328.html @@ -278,18 +278,18 @@ function runTest() { iframeCw.history.back(); popstateExpected("Going back to page 0 should trigger a popstate."); - is(gLastPopStateEvent.state, null, + ise(gLastPopStateEvent.state, null, "Going back to page 0 should pop a null state."); - is(iframeCw.history.state, null, + ise(iframeCw.history.state, null, "Going back to page 0 should pop a null state."); - is(iframeCw.location.search, "", + ise(iframeCw.location.search, "", "Going back to page 0 should clear the querystring."); iframeCw.history.forward(); popstateExpected("Going forward to page 1 should trigger a popstate."); is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj1), "Wrong state object popped after going forward to page 1."); - ok(gLastPopStateEvent.state === iframeCw.history.state, + ise(gLastPopStateEvent.state, iframeCw.history.state, "Wrong state object in document after going forward to page 1."); ok(iframeCw.location.toString().match(/file_bug500328_1.html$/), "Going forward to page 1 should leave us at original page."); @@ -300,7 +300,7 @@ function runTest() { popstateExpected("Going forward to page 2 should trigger a popstate."); is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj2), "Wrong state object popped after going forward to page 2."); - ok(iframeCw.history.state === gLastPopStateEvent.state, + ise(iframeCw.history.state, gLastPopStateEvent.state, "Wrong state object in document after going forward to page 2."); ok(iframeCw.location.toString().match(/file_bug500328_1.html\?test1#foo$/), "Going forward to page 2 took us to " + iframeCw.location.toString()); @@ -329,7 +329,7 @@ function runTest() { "search should be ?test1 after clicking link."); is(iframeCw.location.hash, "#1", "hash should be #1 after clicking link."); - ok(iframeCw.history.state === null, + ise(iframeCw.history.state, null, "Wrong state object in document after clicking link to hash '#1'."); /* From 572e483ff0acd954d6e0c10cc081041c5a0bec85 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 12 Dec 2013 16:17:35 -0500 Subject: [PATCH 054/459] Bug 942164 - Refcount imgRequest consumers. r=seth --- image/src/imgRequest.cpp | 2 +- image/src/imgStatusTracker.cpp | 24 ++++++++++++------------ image/src/imgStatusTracker.h | 6 +++--- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/image/src/imgRequest.cpp b/image/src/imgRequest.cpp index 67529d330fd1..b570496f0197 100644 --- a/image/src/imgRequest.cpp +++ b/image/src/imgRequest.cpp @@ -637,7 +637,7 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt PR_LOG(GetImgLog(), PR_LOG_WARNING, ("[this=%p] imgRequest::OnStartRequest -- " "RetargetDeliveryTo rv %d=%s\n", - this, NS_SUCCEEDED(rv) ? "succeeded" : "failed", rv)); + this, rv, NS_SUCCEEDED(rv) ? "succeeded" : "failed")); } return NS_OK; diff --git a/image/src/imgStatusTracker.cpp b/image/src/imgStatusTracker.cpp index b4fd8147d976..3fb2640c9c50 100644 --- a/image/src/imgStatusTracker.cpp +++ b/image/src/imgStatusTracker.cpp @@ -357,7 +357,7 @@ imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy) #define NOTIFY_IMAGE_OBSERVERS(func) \ do { \ - nsTObserverArray::ForwardIterator iter(proxies); \ + nsTObserverArray >::ForwardIterator iter(proxies); \ while (iter.HasMore()) { \ nsRefPtr proxy = iter.GetNext(); \ if (!proxy->NotificationsDeferred()) { \ @@ -367,7 +367,7 @@ imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy) } while (false); /* static */ void -imgStatusTracker::SyncNotifyState(nsTObserverArray& proxies, +imgStatusTracker::SyncNotifyState(nsTObserverArray >& proxies, bool hasImage, uint32_t state, nsIntRect& dirtyRect, bool hadLastPart) { @@ -505,7 +505,7 @@ imgStatusTracker::SyncNotifyDifference(const ImageStatusDiff& diff) mInvalidRect.SetEmpty(); if (diff.unblockedOnload) { - nsTObserverArray::ForwardIterator iter(mConsumers); + nsTObserverArray >::ForwardIterator iter(mConsumers); while (iter.HasMore()) { // Hold on to a reference to this proxy, since notifying the state can // cause it to disappear. @@ -550,7 +550,7 @@ imgStatusTracker::SyncNotify(imgRequestProxy* proxy) r = mImage->FrameRect(imgIContainer::FRAME_CURRENT); } - nsTObserverArray array; + nsTObserverArray > array; array.AppendElement(proxy); SyncNotifyState(array, !!mImage, mState, r, mHadLastPart); } @@ -781,7 +781,7 @@ imgStatusTracker::OnUnlockedDraw() { MOZ_ASSERT(NS_IsMainThread()); RecordUnlockedDraw(); - nsTObserverArray::ForwardIterator iter(mConsumers); + nsTObserverArray >::ForwardIterator iter(mConsumers); while (iter.HasMore()) { SendUnlockedDraw(iter.GetNext()); } @@ -838,7 +838,7 @@ imgStatusTracker::OnStartRequest() { MOZ_ASSERT(NS_IsMainThread()); RecordStartRequest(); - nsTObserverArray::ForwardIterator iter(mConsumers); + nsTObserverArray >::ForwardIterator iter(mConsumers); while (iter.HasMore()) { SendStartRequest(iter.GetNext()); } @@ -909,7 +909,7 @@ imgStatusTracker::OnStopRequest(bool aLastPart, RecordStopRequest(aLastPart, aStatus); /* notify the kids */ - nsTObserverArray::ForwardIterator srIter(mConsumers); + nsTObserverArray >::ForwardIterator srIter(mConsumers); while (srIter.HasMore()) { SendStopRequest(srIter.GetNext(), aLastPart, aStatus); } @@ -926,7 +926,7 @@ imgStatusTracker::OnDiscard() RecordDiscard(); /* notify the kids */ - nsTObserverArray::ForwardIterator iter(mConsumers); + nsTObserverArray >::ForwardIterator iter(mConsumers); while (iter.HasMore()) { SendDiscard(iter.GetNext()); } @@ -939,7 +939,7 @@ imgStatusTracker::FrameChanged(const nsIntRect* aDirtyRect) RecordFrameChanged(aDirtyRect); /* notify the kids */ - nsTObserverArray::ForwardIterator iter(mConsumers); + nsTObserverArray >::ForwardIterator iter(mConsumers); while (iter.HasMore()) { SendFrameChanged(iter.GetNext(), aDirtyRect); } @@ -952,7 +952,7 @@ imgStatusTracker::OnStopFrame() RecordStopFrame(); /* notify the kids */ - nsTObserverArray::ForwardIterator iter(mConsumers); + nsTObserverArray >::ForwardIterator iter(mConsumers); while (iter.HasMore()) { SendStopFrame(iter.GetNext()); } @@ -970,7 +970,7 @@ imgStatusTracker::OnDataAvailable() return; } // Notify any imgRequestProxys that are observing us that we have an Image. - nsTObserverArray::ForwardIterator iter(mConsumers); + nsTObserverArray >::ForwardIterator iter(mConsumers); while (iter.HasMore()) { iter.GetNext()->SetHasImage(); } @@ -1021,7 +1021,7 @@ imgStatusTracker::MaybeUnblockOnload() RecordUnblockOnload(); - nsTObserverArray::ForwardIterator iter(mConsumers); + nsTObserverArray >::ForwardIterator iter(mConsumers); while (iter.HasMore()) { SendUnblockOnload(iter.GetNext()); } diff --git a/image/src/imgStatusTracker.h b/image/src/imgStatusTracker.h index d70fdbef97d8..6ca06c95c062 100644 --- a/image/src/imgStatusTracker.h +++ b/image/src/imgStatusTracker.h @@ -9,7 +9,6 @@ class imgDecoderObserver; class imgIContainer; -class imgRequestProxy; class imgStatusNotifyRunnable; class imgRequestNotifyRunnable; class imgStatusTrackerObserver; @@ -21,6 +20,7 @@ class nsIRunnable; #include "nsTObserverArray.h" #include "nsThreadUtils.h" #include "nsRect.h" +#include "imgRequestProxy.h" namespace mozilla { namespace image { @@ -306,7 +306,7 @@ private: // Main thread only, since imgRequestProxy calls are expected on the main // thread, and mConsumers is not threadsafe. - static void SyncNotifyState(nsTObserverArray& proxies, + static void SyncNotifyState(nsTObserverArray >& proxies, bool hasImage, uint32_t state, nsIntRect& dirtyRect, bool hadLastPart); @@ -322,7 +322,7 @@ private: // List of proxies attached to the image. Each proxy represents a consumer // using the image. Array and/or individual elements should only be accessed // on the main thread. - nsTObserverArray mConsumers; + nsTObserverArray > mConsumers; mozilla::RefPtr mTrackerObserver; From 8368a30b640bab1af2a5ba092e3b5f65dbf32c3b Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Thu, 12 Dec 2013 13:21:38 -0800 Subject: [PATCH 055/459] Backed out changeset a9654d11cb26 (bug 948757) for m-bc bustage --- netwerk/base/public/nsISpeculativeConnect.idl | 9 ++++++++- netwerk/base/src/Seer.cpp | 9 ++++++++- netwerk/protocol/http/nsHttpConnectionMgr.cpp | 6 +++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/netwerk/base/public/nsISpeculativeConnect.idl b/netwerk/base/public/nsISpeculativeConnect.idl index 7d436519b7e7..f4bcd3cf4039 100644 --- a/netwerk/base/public/nsISpeculativeConnect.idl +++ b/netwerk/base/public/nsISpeculativeConnect.idl @@ -35,7 +35,7 @@ interface nsISpeculativeConnect : nsISupports * inline) to determine whether or not to actually make a speculative * connection. */ -[builtinclass, uuid(f5f70897-2edf-4c00-a1c3-87d51b92cec5)] +[builtinclass, uuid(2b6d6fb6-ab28-4f4c-af84-bfdbb7866d72)] interface nsISpeculativeConnectionOverrider : nsISupports { /** @@ -50,4 +50,11 @@ interface nsISpeculativeConnectionOverrider : nsISupports * connect via SPDY or not. */ [infallible] readonly attribute boolean ignorePossibleSpdyConnections; + + /** + * Used to determine if we will ignore the existence of any currently idle + * connections when we decide whether or not to make a speculative + * connection. + */ + [infallible] readonly attribute boolean ignoreIdle; }; diff --git a/netwerk/base/src/Seer.cpp b/netwerk/base/src/Seer.cpp index 85f0fbcdee85..f024345f1831 100644 --- a/netwerk/base/src/Seer.cpp +++ b/netwerk/base/src/Seer.cpp @@ -305,6 +305,13 @@ Seer::Observe(nsISupports *subject, const char *topic, // Seer::nsISpeculativeConnectionOverrider +NS_IMETHODIMP +Seer::GetIgnoreIdle(bool *ignoreIdle) +{ + *ignoreIdle = true; + return NS_OK; +} + NS_IMETHODIMP Seer::GetIgnorePossibleSpdyConnections(bool *ignorePossibleSpdyConnections) { @@ -896,7 +903,7 @@ Seer::PredictForLink(nsIURI *targetURI, nsIURI *sourceURI, } } - mSpeculativeService->SpeculativeConnect(targetURI, nullptr); + mSpeculativeService->SpeculativeConnect(targetURI, this); if (verifier) { verifier->OnPredictPreconnect(targetURI); } diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 529702bf92d8..05cd4c8ee39e 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -334,6 +334,7 @@ public: // intentional! bool mOverridesOK; uint32_t mParallelSpeculativeConnectLimit; + bool mIgnoreIdle; bool mIgnorePossibleSpdyConnections; // As above, added manually so we can use nsRefPtr without inheriting from @@ -380,6 +381,7 @@ nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci, args->mOverridesOK = true; overrider->GetParallelSpeculativeConnectLimit( &args->mParallelSpeculativeConnectLimit); + overrider->GetIgnoreIdle(&args->mIgnoreIdle); overrider->GetIgnorePossibleSpdyConnections( &args->mIgnorePossibleSpdyConnections); } @@ -2588,14 +2590,16 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param) uint32_t parallelSpeculativeConnectLimit = gHttpHandler->ParallelSpeculativeConnectLimit(); bool ignorePossibleSpdyConnections = false; + bool ignoreIdle = false; if (args->mOverridesOK) { parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit; ignorePossibleSpdyConnections = args->mIgnorePossibleSpdyConnections; + ignoreIdle = args->mIgnoreIdle; } if (mNumHalfOpenConns < parallelSpeculativeConnectLimit && - ent->mIdleConns.Length() < parallelSpeculativeConnectLimit && + (ignoreIdle || !ent->mIdleConns.Length()) && !RestrictConnections(ent, ignorePossibleSpdyConnections) && !AtActiveConnectionLimit(ent, args->mTrans->Caps())) { CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true); From 07d5ad43a7b49929eaa1ff5c3ecd4a3fef601ca2 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 12 Dec 2013 13:25:52 -0800 Subject: [PATCH 056/459] Bug 946835 part 1: Add reftest to test all the possible values of the "flex-flow" shorthand. r=mats --- .../flexbox/flexbox-flex-flow-1-ref.html | 127 ++++++++++++++++++ .../flexbox/flexbox-flex-flow-1.html | 120 +++++++++++++++++ .../w3c-css/submitted/flexbox/reftest.list | 3 + 3 files changed, 250 insertions(+) create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-flex-flow-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-flex-flow-1.html diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-flex-flow-1-ref.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-flex-flow-1-ref.html new file mode 100644 index 000000000000..f46e11b39af2 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-flex-flow-1-ref.html @@ -0,0 +1,127 @@ + + + + + CSS Reftest Reference + + + + + + +
+
1
2
3
4
+
+
+
4
3
2
1
+
+
+
1
2
3
4
+
+
+
4
3
2
1
+
+ +
+ + +
+
1
2
3
4
+
+
+
2
1
4
3
+
+
+
1
3
2
4
+
+
+
2
4
1
3
+
+ +
+ + +
+
1
2
3
4
+
+
+
2
1
4
3
+
+
+
1
3
2
4
+
+
+
2
4
1
3
+
+ +
+ + +
+
3
4
1
2
+
+
+
4
3
2
1
+
+
+
3
1
4
2
+
+
+
4
2
3
1
+
+ +
+ + +
+
3
4
1
2
+
+
+
4
3
2
1
+
+
+
3
1
4
2
+
+
+
4
2
3
1
+
+ +
+ + +
+
1
2
3
4
+
+
+
3
4
1
2
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-flex-flow-1.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-flex-flow-1.html new file mode 100644 index 000000000000..f7814d3fd984 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-flex-flow-1.html @@ -0,0 +1,120 @@ + + + + + CSS Test: Testing all the values of the "flex-flow" shorthand property + + + + + + + + +
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+ +
+ + +
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+ +
+ + +
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+ +
+ + +
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+ +
+ + +
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+
+
1
2
3
4
+
+ +
+ + +
+
1
2
3
4
+
+
+
1
2
3
4
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/reftest.list b/layout/reftests/w3c-css/submitted/flexbox/reftest.list index 72ca7f89ca79..f5d030c89c6e 100644 --- a/layout/reftests/w3c-css/submitted/flexbox/reftest.list +++ b/layout/reftests/w3c-css/submitted/flexbox/reftest.list @@ -64,6 +64,9 @@ fuzzy-if(Android,158,32) == flexbox-align-self-vert-rtl-1.xhtml flexbox-align-s == flexbox-break-request-vert-2a.html flexbox-break-request-vert-2-ref.html == flexbox-break-request-vert-2b.html flexbox-break-request-vert-2-ref.html +# Tests for flex-flow shorthand property +== flexbox-flex-flow-1.html flexbox-flex-flow-1-ref.html + # Tests for flex-wrap property == flexbox-flex-wrap-horiz-1.html flexbox-flex-wrap-horiz-1-ref.html == flexbox-flex-wrap-horiz-2.html flexbox-flex-wrap-horiz-2-ref.html From d75a744000ee902daa106bc7999e48c0e7fd80bc Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 12 Dec 2013 13:25:53 -0800 Subject: [PATCH 057/459] Bug 946835 part 2: Add reftest for baseline alignment in multiple lines in a 'flex-wrap:wrap" flex container. r=mats --HG-- rename : layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-1-ref.xhtml => layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2-ref.xhtml rename : layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-1.xhtml => layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2.xhtml --- ...xbox-align-self-baseline-horiz-2-ref.xhtml | 56 +++++++++++++++++++ .../flexbox-align-self-baseline-horiz-2.xhtml | 56 +++++++++++++++++++ .../w3c-css/submitted/flexbox/reftest.list | 3 +- 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2-ref.xhtml create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2.xhtml diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2-ref.xhtml b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2-ref.xhtml new file mode 100644 index 000000000000..73a31237552c --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2-ref.xhtml @@ -0,0 +1,56 @@ + + + + + + CSS Reftest Reference + + + + +
+ +
a
+
b
+
c
+
+
+ +
d
+
e
+
f
+
+
+ +
g
+
h
+
i
+
+ + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2.xhtml b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2.xhtml new file mode 100644 index 000000000000..204b57cafa5b --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2.xhtml @@ -0,0 +1,56 @@ + + + + + + CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' in a multi-line flex container + + + + + + +
+ +
a
+
b
+
c
+ + +
d
+
e
+
f
+ + +
g
+
h
+
i
+
+ + diff --git a/layout/reftests/w3c-css/submitted/flexbox/reftest.list b/layout/reftests/w3c-css/submitted/flexbox/reftest.list index f5d030c89c6e..c13a8bb08a89 100644 --- a/layout/reftests/w3c-css/submitted/flexbox/reftest.list +++ b/layout/reftests/w3c-css/submitted/flexbox/reftest.list @@ -5,7 +5,8 @@ == flexbox-align-content-vert-1b.xhtml flexbox-align-content-vert-1-ref.xhtml # Tests for cross-axis alignment (align-self / align-items properties) -== flexbox-align-self-baseline-horiz-1.xhtml flexbox-align-self-baseline-horiz-1-ref.xhtml +== flexbox-align-self-baseline-horiz-1.xhtml flexbox-align-self-baseline-horiz-1-ref.xhtml +== flexbox-align-self-baseline-horiz-2.xhtml flexbox-align-self-baseline-horiz-2-ref.xhtml == flexbox-align-self-stretch-vert-1.html flexbox-align-self-stretch-vert-1-ref.html From 53f5416ef289cde6fab5470eee48b145e77ddd95 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 12 Dec 2013 13:25:55 -0800 Subject: [PATCH 058/459] Bug 946835 part 3: Add reftest for baseline alignment in multiple lines in a 'flex-wrap:wrap-reverse" flex container. r=mats --HG-- rename : layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2-ref.xhtml => layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml rename : layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-2.xhtml => layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3.xhtml --- ...xbox-align-self-baseline-horiz-3-ref.xhtml | 58 +++++++++++++++++++ .../flexbox-align-self-baseline-horiz-3.xhtml | 56 ++++++++++++++++++ .../w3c-css/submitted/flexbox/reftest.list | 1 + 3 files changed, 115 insertions(+) create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3.xhtml diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml new file mode 100644 index 000000000000..e8b2791f44d4 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml @@ -0,0 +1,58 @@ + + + + + + CSS Reftest Reference + + + + + +
+ +
g
+
h
+
i
+
+
+ +
d
+
e
+
f
+
+
+ +
a
+
b
+
c
+
+ + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3.xhtml b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3.xhtml new file mode 100644 index 000000000000..24f300f9c181 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3.xhtml @@ -0,0 +1,56 @@ + + + + + + CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' in a multi-line flex container + + + + + + +
+ +
a
+
b
+
c
+ + +
d
+
e
+
f
+ + +
g
+
h
+
i
+
+ + diff --git a/layout/reftests/w3c-css/submitted/flexbox/reftest.list b/layout/reftests/w3c-css/submitted/flexbox/reftest.list index c13a8bb08a89..bd89be0f7662 100644 --- a/layout/reftests/w3c-css/submitted/flexbox/reftest.list +++ b/layout/reftests/w3c-css/submitted/flexbox/reftest.list @@ -7,6 +7,7 @@ # Tests for cross-axis alignment (align-self / align-items properties) == flexbox-align-self-baseline-horiz-1.xhtml flexbox-align-self-baseline-horiz-1-ref.xhtml == flexbox-align-self-baseline-horiz-2.xhtml flexbox-align-self-baseline-horiz-2-ref.xhtml +== flexbox-align-self-baseline-horiz-3.xhtml flexbox-align-self-baseline-horiz-3-ref.xhtml == flexbox-align-self-stretch-vert-1.html flexbox-align-self-stretch-vert-1-ref.html From 14d4d00983978f250477e2922d86c5ee8a9097d1 Mon Sep 17 00:00:00 2001 From: Ethan Hugg Date: Fri, 30 Aug 2013 12:51:05 -0700 Subject: [PATCH 059/459] Bug 901560 - Datachannel no longer make second ICE component r=abr --- .../nICEr/src/ice/ice_media_stream.c | 3 +- .../signaling/src/sipcc/core/gsm/gsm_sdp.c | 54 +++---------------- 2 files changed, 9 insertions(+), 48 deletions(-) diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c index df56d964d63e..92aaf1d16cbc 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c @@ -846,7 +846,8 @@ int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int compo ABORT(r); /* Can only disable before pairing */ - if (comp->state != NR_ICE_COMPONENT_UNPAIRED) + if (comp->state != NR_ICE_COMPONENT_UNPAIRED && + comp->state != NR_ICE_COMPONENT_DISABLED) ABORT(R_FAILED); comp->state = NR_ICE_COMPONENT_DISABLED; diff --git a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c index 57cca165a2e3..daae2e6a3084 100644 --- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c +++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c @@ -4,8 +4,6 @@ * 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 - #include "cpr_in.h" #include "cpr_rand.h" #include "cpr_stdlib.h" @@ -103,38 +101,6 @@ extern cc_media_cap_table_t g_media_table; extern boolean g_disable_mass_reg_debug_print; -/* - * gsmsdp_requires_two_dc_components - * - * returns TRUE if we are talking to Firefox and it's - * a version that required two components for datachannel. - */ -static boolean gsmsdp_requires_two_dc_components(void *sdp) { -#define FIRST_VERSION_TO_USE_ONE_DC_COMPONENT 26 - const char *owner_name = sdp_get_owner_username(sdp); - unsigned long remote_version; - char* strtoul_end; - - if (strncmp(owner_name, SIPSDP_ORIGIN_APPNAME, - strlen(SIPSDP_ORIGIN_APPNAME)) == 0) { - /* This means we are talking to firefox, now read the major version */ - errno = 0; - remote_version = strtoul(owner_name + strlen(SIPSDP_ORIGIN_APPNAME), - &strtoul_end, 10); - if (errno || - strtoul_end == (owner_name + strlen(SIPSDP_ORIGIN_APPNAME)) || - !remote_version) { - /* Unable to parse remote, must not be earlier firefox */ - return FALSE; - } - - return (remote_version < FIRST_VERSION_TO_USE_ONE_DC_COMPONENT) ? - TRUE : FALSE; - } - - return FALSE; -} - /** * A wraper function to return the media capability supported by * the platform and session. This is a convient place if policy @@ -5731,6 +5697,13 @@ gsmsdp_create_local_sdp (fsmdef_dcb_t *dcb_p, boolean force_streams_enabled, */ if (media_enabled && ( media_cap->enabled || force_streams_enabled)) { level = level + 1; /* next level */ + + /* Only audio and video use two ICE components */ + if (media_cap->type != SDP_MEDIA_AUDIO && + media_cap->type != SDP_MEDIA_VIDEO) { + vcmDisableRtcpComponent(dcb_p->peerconnection, level); + } + ip_mode = platform_get_ip_address_mode(); if (ip_mode >= CPR_IP_MODE_IPV6) { if (gsmsdp_add_media_line(dcb_p, media_cap, cap_index, @@ -7019,19 +6992,6 @@ gsmsdp_install_peer_ice_attributes(fsm_fcb_t *fcb_p) } } - /* If this is Datachannel and we are talking to anything other - than an older version of Firefox then disable the second component - of the ICE stream */ - if (media->type == DATA && - !gsmsdp_requires_two_dc_components(sdp_p->dest_sdp)) { - vcm_res = vcmDisableRtcpComponent(dcb_p->peerconnection, - media->level); - - if (vcm_res) { - return CC_CAUSE_SETTING_ICE_SESSION_PARAMETERS_FAILED; - } - } - sdp_res = sdp_attr_get_ice_attribute(sdp_p->dest_sdp, media->level, 0, SDP_ATTR_ICE_UFRAG, 1, &ufrag); if (sdp_res != SDP_SUCCESS) From b85bf618cb9026f24a92a904e814b0e99149d9a1 Mon Sep 17 00:00:00 2001 From: Brandon Benvie Date: Fri, 13 Dec 2013 11:11:20 -0800 Subject: [PATCH 060/459] Bug 943681 - Convert to Promise.jsm in the webconsole. r=msucan --- browser/devtools/webconsole/hudservice.js | 4 +-- browser/devtools/webconsole/panel.js | 2 +- ...r_webconsole_bug_613642_maintain_scroll.js | 36 +++++++------------ browser/devtools/webconsole/test/head.js | 2 +- browser/devtools/webconsole/webconsole.js | 2 +- 5 files changed, 17 insertions(+), 29 deletions(-) diff --git a/browser/devtools/webconsole/hudservice.js b/browser/devtools/webconsole/hudservice.js index caca70d3502b..fed0a290ab69 100644 --- a/browser/devtools/webconsole/hudservice.js +++ b/browser/devtools/webconsole/hudservice.js @@ -11,9 +11,9 @@ const {Cc, Ci, Cu} = require("chrome"); let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils; let Heritage = require("sdk/core/heritage"); -loader.lazyGetter(this, "promise", () => require("sdk/core/promise")); loader.lazyGetter(this, "Telemetry", () => require("devtools/shared/telemetry")); loader.lazyGetter(this, "WebConsoleFrame", () => require("devtools/webconsole/webconsole").WebConsoleFrame); +loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm"); loader.lazyImporter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm"); loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); @@ -110,6 +110,7 @@ HUD_SERVICE.prototype = function HS_openBrowserConsole(aTarget, aIframeWindow, aChromeWindow) { let hud = new BrowserConsole(aTarget, aIframeWindow, aChromeWindow); + this._browserConsoleID = hud.hudId; this.consoles.set(hud.hudId, hud); return hud.init(); }, @@ -259,7 +260,6 @@ HUD_SERVICE.prototype = connect().then(getTarget).then(openWindow).then((aWindow) => { this.openBrowserConsole(target, aWindow, aWindow) .then((aBrowserConsole) => { - this._browserConsoleID = aBrowserConsole.hudId; this._browserConsoleDefer.resolve(aBrowserConsole); this._browserConsoleDefer = null; }) diff --git a/browser/devtools/webconsole/panel.js b/browser/devtools/webconsole/panel.js index 48a71f576ec5..466f84834dec 100644 --- a/browser/devtools/webconsole/panel.js +++ b/browser/devtools/webconsole/panel.js @@ -6,7 +6,7 @@ const {Cc, Ci, Cu} = require("chrome"); -loader.lazyGetter(this, "promise", () => require("sdk/core/promise")); +loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); loader.lazyGetter(this, "HUDService", () => require("devtools/webconsole/hudservice")); loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter")); diff --git a/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js b/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js index 13f018556268..63da224c9a48 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js @@ -95,37 +95,25 @@ function testGen() { testNext(); }; EventUtils.synthesizeKey("VK_END", {}); - yield; + yield undefined; let oldScrollTop = scrollBox.scrollTop; content.console.log("test message 151"); - waitForMessages({ - webconsole: hud, - messages: [{ - text: "test message 151", - category: CATEGORY_WEBDEV, - severity: SEVERITY_LOG, - }], - }).then(() => { - scrollBox.onscroll = () => { - if (scrollBox.scrollTop == oldScrollTop) { - // Wait for scroll to change. - return; - } - scrollBox.onscroll = null; - isnot(scrollBox.scrollTop, oldScrollTop, "scroll location updated (moved to bottom again)"); - testNext(); - }; - }); + scrollBox.onscroll = () => { + dump("\n\nSCROLLED\n\n"); + if (scrollBox.scrollTop == oldScrollTop) { + // Wait for scroll to change. + return; + } + scrollBox.onscroll = null; + isnot(scrollBox.scrollTop, oldScrollTop, "scroll location updated (moved to bottom again)"); + hud = testDriver = null; + finishTest(); + }; yield undefined; - - hud = testDriver = null; - finishTest(); - - yield undefined; } function test() { diff --git a/browser/devtools/webconsole/test/head.js b/browser/devtools/webconsole/test/head.js index c60e972e605a..744401b971de 100644 --- a/browser/devtools/webconsole/test/head.js +++ b/browser/devtools/webconsole/test/head.js @@ -8,7 +8,7 @@ let WebConsoleUtils, gDevTools, TargetFactory, console, promise, require; (() => { gDevTools = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}).gDevTools; console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console; - promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise; + promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; let tools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools; let utils = tools.require("devtools/toolkit/webconsole/utils"); diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index 5e477227fc98..46673010ec1e 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -14,7 +14,7 @@ loader.lazyServiceGetter(this, "clipboardHelper", "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); -loader.lazyGetter(this, "promise", () => require("sdk/core/promise")); +loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter")); loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup); From e7550d5c34b0960b0ab4a843f36b0725cb860a41 Mon Sep 17 00:00:00 2001 From: Sriram Ramasubramanian Date: Thu, 26 Sep 2013 15:17:22 -0700 Subject: [PATCH 061/459] Bug 919777: Change the width to be fill_parent for titles in TwoLinePageRow. [r=lucasr] --- mobile/android/base/resources/layout/two_line_page_row.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/base/resources/layout/two_line_page_row.xml b/mobile/android/base/resources/layout/two_line_page_row.xml index 65ec3844fd3b..94125a24e9cd 100644 --- a/mobile/android/base/resources/layout/two_line_page_row.xml +++ b/mobile/android/base/resources/layout/two_line_page_row.xml @@ -20,7 +20,7 @@ From 5f8f582d79bf37f0fe0de1fc28437452e3ec16b5 Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Thu, 12 Dec 2013 00:00:23 -0800 Subject: [PATCH 062/459] Bumping gaia.json for 2 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/837a95ee7a5d Author: Cristian Rodriguez Desc: Merge pull request #14565 from crdlc/bug-924843 Bug 924843 - Icons on grid do not re-arrange automatically when dragging... ======== https://hg.mozilla.org/integration/gaia-central/rev/18421b9ecd89 Author: crdlc Desc: Bug 924843 - Icons on grid do not re-arrange automatically when dragging over collections --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 073e1333333b..7a469a0bf06a 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "5bfef5faac50d14e055f642a44ed2df8483fb2fe", + "revision": "837a95ee7a5d0745c91ab868a3a24a183cb30f60", "repo_path": "/integration/gaia-central" } From 6729764aed36ebe8e44a6e2a8f660213ccba5bd9 Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Thu, 12 Dec 2013 00:35:23 -0800 Subject: [PATCH 063/459] Bumping gaia.json for 2 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/cfb69584f3b0 Author: Timothy Guan-tin Chien Desc: Merge pull request #14224 from flodolo/separate_header Bug 944749 - [Settings][l12y] Use different labels for menu items and headers, r=kaze ======== https://hg.mozilla.org/integration/gaia-central/rev/c62a6e4de4f7 Author: Francesco Lodolo (:flod) Desc: Bug 944749 - [Settings][l12y] Use different labels for menu items and headers Don't reuse labels in different contexts (and with different space available). Use fallback to original string for headers ({{id}}) to reduce impact on existing localizations. --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 7a469a0bf06a..3b7f57518676 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "837a95ee7a5d0745c91ab868a3a24a183cb30f60", + "revision": "cfb69584f3b0181f31248efa42040558a3639e27", "repo_path": "/integration/gaia-central" } From 9edf9eff133fd3f0b79c87f6e2e9a8e98250b695 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 12 Dec 2013 09:57:56 +0100 Subject: [PATCH 064/459] Bug 947905: Release OmxDecoder on main thread OmxDecoder refers to MediaResource, which must be released on the main thread. This patch changes the MP3 parser logic to send a runnable to the main thread for releasing OmxDecoder. --- content/media/omx/OmxDecoder.cpp | 43 ++++++++++++++++++++++++++------ content/media/omx/OmxDecoder.h | 2 +- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/content/media/omx/OmxDecoder.cpp b/content/media/omx/OmxDecoder.cpp index 5ce86774dcb9..1d53cde4f766 100644 --- a/content/media/omx/OmxDecoder.cpp +++ b/content/media/omx/OmxDecoder.cpp @@ -41,6 +41,25 @@ using namespace mozilla; namespace mozilla { +class ReleaseOmxDecoderRunnable : public nsRunnable +{ +public: + ReleaseOmxDecoderRunnable(const android::sp& aOmxDecoder) + : mOmxDecoder(aOmxDecoder) + { + } + + NS_METHOD Run() MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + mOmxDecoder = nullptr; // release OmxDecoder + return NS_OK; + } + +private: + android::sp mOmxDecoder; +}; + class OmxDecoderProcessCachedDataTask : public Task { public: @@ -53,7 +72,13 @@ public: { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(mOmxDecoder.get()); - mOmxDecoder->ProcessCachedData(mOffset, false); + int64_t rem = mOmxDecoder->ProcessCachedData(mOffset, false); + + if (rem <= 0) { + ReleaseOmxDecoderRunnable* r = new ReleaseOmxDecoderRunnable(mOmxDecoder); + mOmxDecoder.clear(); + NS_DispatchToMainThread(r); + } } private: @@ -292,6 +317,8 @@ OmxDecoder::OmxDecoder(MediaResource *aResource, OmxDecoder::~OmxDecoder() { + MOZ_ASSERT(NS_IsMainThread()); + ReleaseMediaResources(); // unregister AMessage handler from ALooper. @@ -398,7 +425,7 @@ bool OmxDecoder::TryLoad() { // Feed MP3 parser with cached data. Local files will be fully // cached already, network streams will update with sucessive // calls to NotifyDataArrived. - if (ProcessCachedData(0, true)) { + if (ProcessCachedData(0, true) >= 0) { durationUs = mMP3FrameParser.GetDuration(); if (durationUs > totalDurationUs) { totalDurationUs = durationUs; @@ -1017,7 +1044,7 @@ void OmxDecoder::ReleaseAllPendingVideoBuffersLocked() releasingVideoBuffers.clear(); } -bool OmxDecoder::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion) +int64_t OmxDecoder::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion) { // We read data in chunks of 32 KiB. We can reduce this // value if media, such as sdcards, is too slow. @@ -1030,10 +1057,10 @@ bool OmxDecoder::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion) MOZ_ASSERT(mResource); int64_t resourceLength = mResource->GetCachedDataEnd(0); - NS_ENSURE_TRUE(resourceLength >= 0, false); + NS_ENSURE_TRUE(resourceLength >= 0, -1); if (aOffset >= resourceLength) { - return true; // Cache is empty, nothing to do + return 0; // Cache is empty, nothing to do } int64_t bufferLength = std::min(resourceLength-aOffset, sReadSize); @@ -1041,7 +1068,7 @@ bool OmxDecoder::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion) nsAutoArrayPtr buffer(new char[bufferLength]); nsresult rv = mResource->ReadFromCache(buffer.get(), aOffset, bufferLength); - NS_ENSURE_SUCCESS(rv, false); + NS_ENSURE_SUCCESS(rv, -1); nsRefPtr runnable( new OmxDecoderNotifyDataArrivedRunnable(this, @@ -1051,11 +1078,11 @@ bool OmxDecoder::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion) resourceLength)); rv = NS_DispatchToMainThread(runnable.get()); - NS_ENSURE_SUCCESS(rv, false); + NS_ENSURE_SUCCESS(rv, -1); if (aWaitForCompletion) { runnable->WaitForCompletion(); } - return true; + return resourceLength - aOffset - bufferLength; } diff --git a/content/media/omx/OmxDecoder.h b/content/media/omx/OmxDecoder.h index b0e7ce3b91cb..96b55c068cd9 100644 --- a/content/media/omx/OmxDecoder.h +++ b/content/media/omx/OmxDecoder.h @@ -238,7 +238,7 @@ public: // Called on ALooper thread. void onMessageReceived(const sp &msg); - bool ProcessCachedData(int64_t aOffset, bool aWaitForCompletion); + int64_t ProcessCachedData(int64_t aOffset, bool aWaitForCompletion); }; } From 9e5ce04585a5f3ae229f4f7280ee26a2240d02a6 Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Thu, 12 Dec 2013 01:25:24 -0800 Subject: [PATCH 065/459] Bumping gaia.json for 2 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/041a8edf5c4a Author: Amir Nissim Desc: Merge pull request #14405 from EverythingMe/946176-collection-tap Bug 946176 - [e.me][Bug] Collections Tapping issues [r=evyatar] ======== https://hg.mozilla.org/integration/gaia-central/rev/386b3376c582 Author: Ran Ben Aharon Desc: Bug 946176 - [e.me][Bug] Collections Tapping issues [r=evyatar] --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 3b7f57518676..79d09453ff37 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "cfb69584f3b0181f31248efa42040558a3639e27", + "revision": "041a8edf5c4a212d8d19dc367a5dddddd0c76321", "repo_path": "/integration/gaia-central" } From f20d4af27024f3914fc24b3cb141c240386689be Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 12 Dec 2013 11:41:47 +0100 Subject: [PATCH 066/459] Bug 946999 - [Homescreen] With APZ turned on the homescreen is not rendered until you touch the screen in certain conditions. r=kats --- gfx/layers/ipc/AsyncPanZoomController.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index 14c16ffc77b9..a1a034ffa564 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -1276,7 +1276,9 @@ void AsyncPanZoomController::RequestContentRepaint() { mFrameMetrics.mScrollOffset.x) < EPSILON && fabsf(mLastPaintRequestMetrics.mScrollOffset.y - mFrameMetrics.mScrollOffset.y) < EPSILON && - mFrameMetrics.mZoom == mLastPaintRequestMetrics.mZoom) { + mFrameMetrics.mZoom == mLastPaintRequestMetrics.mZoom && + fabsf(mFrameMetrics.mViewport.width - mLastPaintRequestMetrics.mViewport.width) < EPSILON && + fabsf(mFrameMetrics.mViewport.height - mLastPaintRequestMetrics.mViewport.height) < EPSILON) { return; } @@ -1463,8 +1465,10 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri aLayerMetrics.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) { // Remote content has sync'd up to the composition geometry // change, so we can accept the viewport it's calculated. - if (mFrameMetrics.mViewport.width != aLayerMetrics.mViewport.width) + if (mFrameMetrics.mViewport.width != aLayerMetrics.mViewport.width || + mFrameMetrics.mViewport.height != aLayerMetrics.mViewport.height) { needContentRepaint = true; + } mFrameMetrics.mViewport = aLayerMetrics.mViewport; } From cdaec2a0c8c4afaa5e9173e9ed6349d6cc13faa1 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 12 Dec 2013 11:43:45 +0100 Subject: [PATCH 067/459] Bug 946339 - Tap and hold does not highlight the targeted element. r=fabrice --- dom/browser-element/BrowserElementPanning.js | 64 ++++++++++---------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/dom/browser-element/BrowserElementPanning.js b/dom/browser-element/BrowserElementPanning.js index af22e553e5f7..e2a4b58eb683 100644 --- a/dom/browser-element/BrowserElementPanning.js +++ b/dom/browser-element/BrowserElementPanning.js @@ -45,21 +45,16 @@ const ContentPanning = { this.watchedEventsType = 'mouse'; } - // If we are using an AsyncPanZoomController for the parent frame, - // it will handle subframe scrolling too. We don't need to listen for - // these events. - if (!docShell.asyncPanZoomEnabled) { - let els = Cc["@mozilla.org/eventlistenerservice;1"] - .getService(Ci.nsIEventListenerService); + let els = Cc["@mozilla.org/eventlistenerservice;1"] + .getService(Ci.nsIEventListenerService); - events.forEach(function(type) { - // Using the system group for mouse/touch events to avoid - // missing events if .stopPropagation() has been called. - els.addSystemEventListener(global, type, - this.handleEvent.bind(this), - /* useCapture = */ false); - }.bind(this)); - } + events.forEach(function(type) { + // Using the system group for mouse/touch events to avoid + // missing events if .stopPropagation() has been called. + els.addSystemEventListener(global, type, + this.handleEvent.bind(this), + /* useCapture = */ false); + }.bind(this)); addMessageListener("Viewport:Change", this._recvViewportChange.bind(this)); addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this)); @@ -70,6 +65,8 @@ const ContentPanning = { }, handleEvent: function cp_handleEvent(evt) { + this._tryDelayMouseEvents(); + if (evt.defaultPrevented || evt.multipleActionsPrevented) { // clean up panning state even if touchend/mouseup has been preventDefault. if(evt.type === 'touchend' || evt.type === 'mouseup') { @@ -82,13 +79,6 @@ const ContentPanning = { return; } - let start = Date.now(); - let thread = Services.tm.currentThread; - while (this._delayEvents && (Date.now() - start) < this._activeDurationMs) { - thread.processNextEvent(true); - } - this._delayEvents = false; - switch (evt.type) { case 'mousedown': case 'touchstart': @@ -189,7 +179,8 @@ const ContentPanning = { // We prevent start events to avoid sending a focus event at the end of this // touch series. See bug 889717. - if (this.panning || this.preventNextClick) { + if (docShell.asyncPanZoomEnabled === false && + (this.panning || this.preventNextClick)) { evt.preventDefault(); } }, @@ -228,7 +219,7 @@ const ContentPanning = { let view = target.ownerDocument ? target.ownerDocument.defaultView : target; view.addEventListener('click', this, true, true); - } else { + } else if (docShell.asyncPanZoomEnabled === false) { // We prevent end events to avoid sending a focus event. See bug 889717. evt.preventDefault(); } @@ -236,12 +227,7 @@ const ContentPanning = { this.notify(this._activationTimer); this._delayEvents = true; - let start = Date.now(); - let thread = Services.tm.currentThread; - while (this._delayEvents && (Date.now() - start) < this._activeDurationMs) { - thread.processNextEvent(true); - } - this._delayEvents = false; + this._tryDelayMouseEvents(); } this._finishPanning(); @@ -278,7 +264,12 @@ const ContentPanning = { } let isPan = KineticPanning.isPan(); - this.scrollCallback(delta.scale(-1)); + + // If the application is not managed by the AsyncPanZoomController, then + // scroll manually. + if (docShell.asyncPanZoomEnabled === false) { + this.scrollCallback(delta.scale(-1)); + } // If we've detected a pan gesture, cancel the active state of the // current target. @@ -454,6 +445,15 @@ const ContentPanning = { return this._activeDurationMs = duration; }, + _tryDelayMouseEvents: function cp_tryDelayMouseEvents() { + let start = Date.now(); + let thread = Services.tm.currentThread; + while (this._delayEvents && (Date.now() - start) < this._activeDurationMs) { + thread.processNextEvent(true); + } + this._delayEvents = false; + }, + _resetActive: function cp_resetActive() { let elt = this.pointerDownTarget || this.target; let root = elt.ownerDocument || elt.document; @@ -594,7 +594,9 @@ const ContentPanning = { delete this.primaryPointerId; this._activationTimer.cancel(); - if (this.panning) { + // If there is a scroll action but the application is not managed by + // the AsyncPanZoom controller, let's do a manual kinetic panning action. + if (this.panning && docShell.asyncPanZoomEnabled === false) { KineticPanning.start(this); } } From e6d324a04214e79809b19020bd107b4e53712e67 Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Thu, 12 Dec 2013 02:50:24 -0800 Subject: [PATCH 068/459] Bumping gaia.json for 3 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/5eec0ba9c37b Author: Malini Das Desc: Bug 947001 - Add requirements.txt to MANIFEST.in file so it gets packaged to pypi, bump version. r=dhunt ======== https://hg.mozilla.org/integration/gaia-central/rev/d55ebc02e92f Author: EragonJ Desc: Merge pull request #14602 from EragonJ/bug-947432 Bug 947432 - [DSDS] Marketplace and Payment indication in Settings ======== https://hg.mozilla.org/integration/gaia-central/rev/2bdcb009fdc9 Author: EragonJ Desc: Bug 947432 - [DSDS] Marketplace and Payment indication in Settings - update strings --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 79d09453ff37..c7ef08352cfe 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "041a8edf5c4a212d8d19dc367a5dddddd0c76321", + "revision": "5eec0ba9c37b697e3055427ebdd54e8bdc3194dd", "repo_path": "/integration/gaia-central" } From c533d73a07c518b0882cc5d67d4ba3016da95039 Mon Sep 17 00:00:00 2001 From: Shawn Huang Date: Thu, 12 Dec 2013 19:37:42 +0800 Subject: [PATCH 069/459] Bug 941462 - Reply internal error code when getting I/O error from BlueZ, r=gyeh --- dom/bluetooth/BluetoothCommon.h | 3 +++ dom/bluetooth/bluez/linux/BluetoothDBusService.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/dom/bluetooth/BluetoothCommon.h b/dom/bluetooth/BluetoothCommon.h index 2cf6d41eaa25..ac3e488b6f35 100644 --- a/dom/bluetooth/BluetoothCommon.h +++ b/dom/bluetooth/BluetoothCommon.h @@ -108,6 +108,9 @@ extern bool gBluetoothDebugFlag; #define BLUETOOTH_ADDRESS_NONE "00:00:00:00:00:00" #define BLUETOOTH_ADDRESS_BYTES 6 +// Bluetooth stack internal error, such as I/O error +#define ERR_INTERNAL_ERROR "InternalError" + BEGIN_BLUETOOTH_NAMESPACE enum BluetoothSocketType { diff --git a/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp b/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp index 7dbbc202b6aa..dcbbc96d04fc 100644 --- a/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp +++ b/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp @@ -497,6 +497,14 @@ RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable, nsAutoString replyError; BluetoothValue v; aFunc(aMsg, nullptr, v, replyError); + + // Bug 941462. When blueZ replys 'I/O error', we treat it as 'internal error'. + // This usually happned when the first pairing request has not yet finished, + // the second pairing request issued immediately. + if (replyError.EqualsLiteral("I/O error")) { + replyError.AssignLiteral(ERR_INTERNAL_ERROR); + } + DispatchBluetoothReply(replyRunnable, v, replyError); } From 880a39208d0a1bc7bce3fd6c5dc7ed8d951ef087 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 29 Oct 2013 16:03:39 +0100 Subject: [PATCH 070/459] Bug 932728: Don't inherit DBusWatcher from RawDBusConnection, r=qdot DBusWatcher is not a DBus connection, so it shouldn't inherit from it. This patch converts the inheritance into a class member. --- ipc/dbus/DBusThread.cpp | 71 +++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/ipc/dbus/DBusThread.cpp b/ipc/dbus/DBusThread.cpp index 346815ad5da1..1ad3deae6b7d 100644 --- a/ipc/dbus/DBusThread.cpp +++ b/ipc/dbus/DBusThread.cpp @@ -71,7 +71,7 @@ namespace mozilla { namespace ipc { -class DBusWatcher : public RawDBusConnection +class DBusWatcher { public: DBusWatcher() @@ -94,17 +94,6 @@ public: void HandleWatchAdd(); void HandleWatchRemove(); - // Information about the sockets we're polling. Socket counts - // increase/decrease depending on how many add/remove watch signals - // we're received via the control sockets. - nsTArray mPollData; - nsTArray mWatchData; - - // Sockets for receiving dbus control information (watch - // add/removes, loop shutdown, etc...) - ScopedClose mControlFdR; - ScopedClose mControlFdW; - private: struct PollFdComparator { bool Equals(const pollfd& a, const pollfd& b) const { @@ -131,6 +120,19 @@ private: static void DBusWakeupFunction(void* aData); bool SetUp(); + + // Information about the sockets we're polling. Socket counts + // increase/decrease depending on how many add/remove watch signals + // we're received via the control sockets. + nsTArray mPollData; + nsTArray mWatchData; + + // Sockets for receiving dbus control information (watch + // add/removes, loop shutdown, etc...) + ScopedClose mControlFdR; + ScopedClose mControlFdW; + + nsRefPtr mConnection; }; bool @@ -149,12 +151,12 @@ DBusWatcher::CleanUp() { MOZ_ASSERT(!NS_IsMainThread()); - dbus_connection_set_wakeup_main_function(mConnection, nullptr, - nullptr, nullptr); - dbus_bool_t success = dbus_connection_set_watch_functions(mConnection, - nullptr, nullptr, - nullptr, nullptr, - nullptr); + dbus_connection_set_wakeup_main_function(mConnection->GetConnection(), + nullptr, nullptr, nullptr); + dbus_bool_t success = + dbus_connection_set_watch_functions(mConnection->GetConnection(), + nullptr, nullptr, nullptr, + nullptr, nullptr); if (success != TRUE) { NS_WARNING("dbus_connection_set_watch_functions failed"); } @@ -258,7 +260,8 @@ DBusWatcher::Poll() DBusDispatchStatus dbusDispatchStatus; do { - dbusDispatchStatus = dbus_connection_dispatch(GetConnection()); + dbusDispatchStatus = + dbus_connection_dispatch(mConnection->GetConnection()); } while (dbusDispatchStatus == DBUS_DISPATCH_DATA_REMAINS); // Break at this point since we don't know if the operation @@ -513,21 +516,27 @@ DBusWatcher::SetUp() mWatchData.AppendElement(static_cast(nullptr)); + nsRefPtr connection = new RawDBusConnection(); + // If we can't establish a connection to dbus, nothing else will work - nsresult rv = EstablishDBusConnection(); + nsresult rv = connection->EstablishDBusConnection(); if (NS_FAILED(rv)) { NS_WARNING("Cannot create DBus Connection for DBus Thread!"); return false; } dbus_bool_t success = - dbus_connection_set_watch_functions(mConnection, AddWatchFunction, - RemoveWatchFunction, + dbus_connection_set_watch_functions(connection->GetConnection(), + AddWatchFunction, RemoveWatchFunction, ToggleWatchFunction, this, nullptr); NS_ENSURE_TRUE(success == TRUE, false); - dbus_connection_set_wakeup_main_function(mConnection, DBusWakeupFunction, + dbus_connection_set_wakeup_main_function(connection->GetConnection(), + DBusWakeupFunction, this, nullptr); + + mConnection = connection; + return true; } @@ -565,11 +574,11 @@ public: } private: - nsRefPtr mDBusWatcher; + nsAutoPtr mDBusWatcher; }; -static StaticRefPtr gDBusWatcher; -static StaticRefPtr gDBusServiceThread; +static DBusWatcher* gDBusWatcher; +static StaticRefPtr gDBusServiceThread; // Startup/Shutdown utility functions @@ -579,7 +588,7 @@ StartDBus() MOZ_ASSERT(!NS_IsMainThread()); NS_ENSURE_TRUE(!gDBusWatcher, true); - nsRefPtr dbusWatcher(new DBusWatcher()); + nsAutoPtr dbusWatcher(new DBusWatcher()); bool eventLoopStarted = dbusWatcher->Initialize(); NS_ENSURE_TRUE(eventLoopStarted, false); @@ -603,7 +612,7 @@ StartDBus() rv = gDBusServiceThread->Dispatch(pollTask, NS_DISPATCH_NORMAL); NS_ENSURE_SUCCESS(rv, false); - gDBusWatcher = dbusWatcher; + gDBusWatcher = dbusWatcher.forget(); return true; } @@ -614,7 +623,7 @@ StopDBus() MOZ_ASSERT(!NS_IsMainThread()); NS_ENSURE_TRUE(gDBusServiceThread, true); - nsRefPtr dbusWatcher(gDBusWatcher); + DBusWatcher* dbusWatcher = gDBusWatcher; gDBusWatcher = nullptr; if (dbusWatcher && !dbusWatcher->Stop()) { @@ -630,9 +639,9 @@ nsresult DispatchToDBusThread(nsIRunnable* event) { nsRefPtr dbusServiceThread(gDBusServiceThread); - nsRefPtr dbusWatcher(gDBusWatcher); + DBusWatcher* dbusWatcher = gDBusWatcher; - NS_ENSURE_TRUE(dbusServiceThread.get() && dbusWatcher.get(), + NS_ENSURE_TRUE(dbusServiceThread.get() && dbusWatcher, NS_ERROR_NOT_INITIALIZED); nsresult rv = dbusServiceThread->Dispatch(event, NS_DISPATCH_NORMAL); From d6f5d45a38a6884816efb4c10448817d6d2e73b9 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 12 Dec 2013 12:56:00 +0100 Subject: [PATCH 071/459] Bug 932728: Open only one connection to DBus, r=echou,qdot Bluetooth maintains two connections to the DBus server and the DBus system itself maintains a third one. This implies some overhead and makes the code more difficult to understand. This patch changes the Bluetooth code to use the connection that is established by the DBus system. --- .../bluez/linux/BluetoothDBusService.cpp | 190 ++++++++++-------- .../bluez/linux/BluetoothDBusService.h | 2 - ipc/dbus/DBusThread.cpp | 29 ++- ipc/dbus/DBusThread.h | 5 + ipc/dbus/RawDBusConnection.h | 3 +- 5 files changed, 135 insertions(+), 94 deletions(-) diff --git a/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp b/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp index dcbbc96d04fc..c6bfb8c5487f 100644 --- a/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp +++ b/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp @@ -167,12 +167,6 @@ static const char* sBluetoothDBusSignals[] = "type='signal',interface='org.bluez.Control'" }; -/** - * DBus Connection held for the BluetoothCommandThread to use. Should never be - * used by any other thread. - */ -static nsRefPtr gThreadConnection; - // Only A2DP and HID are authorized. static nsTArray sAuthorizedServiceClass; @@ -974,9 +968,9 @@ AppendDeviceName(BluetoothSignal& aSignal) nsString devicePath = arr[0].value().get_nsString(); - nsRefPtr threadConnection = gThreadConnection; + RawDBusConnection* connection = GetDBusConnection(); - if (!threadConnection.get()) { + if (!connection) { BT_WARNING("%s: DBus connection has been closed.", __FUNCTION__); return; } @@ -985,7 +979,7 @@ AppendDeviceName(BluetoothSignal& aSignal) new AppendDeviceNameReplyHandler(nsCString(DBUS_DEVICE_IFACE), devicePath, aSignal); - bool success = threadConnection->SendWithReply( + bool success = connection->SendWithReply( AppendDeviceNameReplyHandler::Callback, handler.get(), 1000, NS_ConvertUTF16toUTF8(devicePath).get(), DBUS_DEVICE_IFACE, "GetProperties", DBUS_TYPE_INVALID); @@ -1219,9 +1213,9 @@ public: return; } - nsRefPtr threadConnection = gThreadConnection; + RawDBusConnection* connection = GetDBusConnection(); - if (!threadConnection.get()) { + if (!connection) { BT_WARNING("%s: DBus connection has been closed.", __FUNCTION__); return; } @@ -1229,7 +1223,7 @@ public: // There is no "RegisterAgent" function defined in device interface. // When we call "CreatePairedDevice", it will do device agent registration // for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html) - if (!dbus_connection_register_object_path(threadConnection->GetConnection(), + if (!dbus_connection_register_object_path(connection->GetConnection(), KEY_REMOTE_AGENT, mAgentVTable, nullptr)) { @@ -1305,9 +1299,9 @@ private: const char* agentPath = KEY_LOCAL_AGENT; const char* capabilities = B2G_AGENT_CAPABILITIES; - nsRefPtr threadConnection = gThreadConnection; + RawDBusConnection* connection = GetDBusConnection(); - if (!threadConnection.get()) { + if (!connection) { BT_WARNING("%s: DBus connection has been closed.", __FUNCTION__); return false; } @@ -1318,7 +1312,7 @@ private: // signal will be passed to local agent. If we start pairing process with // calling CreatePairedDevice, we'll get signal which should be passed to // device agent. - if (!dbus_connection_register_object_path(threadConnection->GetConnection(), + if (!dbus_connection_register_object_path(connection->GetConnection(), KEY_LOCAL_AGENT, aAgentVTable, nullptr)) { @@ -1332,7 +1326,7 @@ private: MOZ_ASSERT(handler.get()); MOZ_ASSERT(!sAdapterPath.IsEmpty()); - bool success = threadConnection->SendWithReply( + bool success = connection->SendWithReply( RegisterAgentReplyHandler::Callback, handler.get(), -1, NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, "RegisterAgent", @@ -1367,9 +1361,9 @@ public: MOZ_ASSERT(NS_IsMainThread()); - nsRefPtr threadConnection = gThreadConnection; + RawDBusConnection* connection = GetDBusConnection(); - if (!threadConnection.get()) { + if (!connection) { BT_WARNING("%s: DBus connection has been closed.", __FUNCTION__); return NS_ERROR_FAILURE; } @@ -1383,7 +1377,7 @@ public: const dbus_uint32_t* services = sServices; - bool success = threadConnection->SendWithReply( + bool success = connection->SendWithReply( DBusReplyHandler::Callback, handler.get(), -1, NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, "AddReservedServiceRecords", @@ -1727,7 +1721,7 @@ OnDefaultAdapterReply(DBusMessage* aReply, void* aData) bool BluetoothDBusService::IsReady() { - if (!IsEnabled() || !mConnection || !gThreadConnection || IsToggling()) { + if (!IsEnabled() || !GetDBusConnection() || IsToggling()) { BT_WARNING("Bluetooth service is not ready yet!"); return false; } @@ -1745,25 +1739,8 @@ BluetoothDBusService::StartInternal() return NS_ERROR_FAILURE; } - if (mConnection) { - return NS_OK; - } - - mConnection = new RawDBusConnection(); - - if (NS_FAILED(mConnection->EstablishDBusConnection())) { - BT_WARNING("Cannot start Main Thread DBus connection!"); - StopDBus(); - return NS_ERROR_FAILURE; - } - - gThreadConnection = new RawDBusConnection(); - - if (NS_FAILED(gThreadConnection->EstablishDBusConnection())) { - BT_WARNING("Cannot start Sync Thread DBus connection!"); - StopDBus(); - return NS_ERROR_FAILURE; - } + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); DBusError err; dbus_error_init(&err); @@ -1773,7 +1750,7 @@ BluetoothDBusService::StartInternal() // signals we want, register all of them in this thread at startup. // The event handler will sort the destinations out as needed. for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) { - dbus_bus_add_match(mConnection->GetConnection(), + dbus_bus_add_match(connection->GetConnection(), sBluetoothDBusSignals[i], &err); if (dbus_error_is_set(&err)) { @@ -1782,7 +1759,7 @@ BluetoothDBusService::StartInternal() } // Add a filter for all incoming messages_base - if (!dbus_connection_add_filter(mConnection->GetConnection(), + if (!dbus_connection_add_filter(connection->GetConnection(), EventFilter, nullptr, nullptr)) { BT_WARNING("Cannot create DBus Event Filter for DBus Thread!"); return NS_ERROR_FAILURE; @@ -1800,11 +1777,11 @@ BluetoothDBusService::StartInternal() * explicitly here. */ if (sAdapterPath.IsEmpty()) { - bool success = mConnection->SendWithReply(OnDefaultAdapterReply, nullptr, - 1000, "/", - DBUS_MANAGER_IFACE, - "DefaultAdapter", - DBUS_TYPE_INVALID); + bool success = connection->SendWithReply(OnDefaultAdapterReply, nullptr, + 1000, "/", + DBUS_MANAGER_IFACE, + "DefaultAdapter", + DBUS_TYPE_INVALID); if (!success) { BT_WARNING("Failed to query default adapter!"); } @@ -1834,15 +1811,16 @@ BluetoothDBusService::StopInternal() } } - if (!mConnection) { - StopDBus(); + RawDBusConnection* connection = GetDBusConnection(); + + if (!connection) { return NS_OK; } DBusError err; dbus_error_init(&err); for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) { - dbus_bus_remove_match(mConnection->GetConnection(), + dbus_bus_remove_match(connection->GetConnection(), sBluetoothDBusSignals[i], &err); if (dbus_error_is_set(&err)) { @@ -1850,24 +1828,21 @@ BluetoothDBusService::StopInternal() } } - dbus_connection_remove_filter(mConnection->GetConnection(), + dbus_connection_remove_filter(connection->GetConnection(), EventFilter, nullptr); - if (!dbus_connection_unregister_object_path(gThreadConnection->GetConnection(), + if (!dbus_connection_unregister_object_path(connection->GetConnection(), KEY_LOCAL_AGENT)) { BT_WARNING("%s: Can't unregister object path %s for agent!", __FUNCTION__, KEY_LOCAL_AGENT); } - if (!dbus_connection_unregister_object_path(gThreadConnection->GetConnection(), + if (!dbus_connection_unregister_object_path(connection->GetConnection(), KEY_REMOTE_AGENT)) { BT_WARNING("%s: Can't unregister object path %s for agent!", __FUNCTION__, KEY_REMOTE_AGENT); } - mConnection = nullptr; - gThreadConnection = nullptr; - // unref stored DBusMessages before clear the hashtable sPairingReqTable->EnumerateRead(UnrefDBusMessages, nullptr); sPairingReqTable->Clear(); @@ -1951,14 +1926,14 @@ protected: // Acquire another reference to this reply handler nsRefPtr handler = this; - nsRefPtr threadConnection = gThreadConnection; + RawDBusConnection* connection = GetDBusConnection(); - if (!threadConnection.get()) { + if (!connection) { aReplyError = NS_LITERAL_STRING("DBus connection has been closed."); return false; } - bool success = threadConnection->SendWithReply( + bool success = connection->SendWithReply( DefaultAdapterPathReplyHandler::Callback, handler.get(), 1000, NS_ConvertUTF16toUTF8(mAdapterPath).get(), DBUS_ADAPTER_IFACE, "GetProperties", DBUS_TYPE_INVALID); @@ -2019,7 +1994,10 @@ BluetoothDBusService::GetDefaultAdapterPathInternal( nsRefPtr handler = new DefaultAdapterPathReplyHandler(aRunnable); - bool success = mConnection->SendWithReply( + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool success = connection->SendWithReply( DefaultAdapterPathReplyHandler::Callback, handler.get(), 1000, "/", DBUS_MANAGER_IFACE, "DefaultAdapter", @@ -2064,7 +2042,10 @@ BluetoothDBusService::SendDiscoveryMessage(const char* aMessageName, nsRefPtr runnable(aRunnable); - bool success = mConnection->SendWithReply( + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool success = connection->SendWithReply( OnSendDiscoveryMessageReply, static_cast(aRunnable), -1, NS_ConvertUTF16toUTF8(sAdapterPath).get(), @@ -2104,13 +2085,12 @@ BluetoothDBusService::SendAsyncDBusMessage(const nsAString& aObjectPath, DBusReplyCallback aCallback) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mConnection); MOZ_ASSERT(IsEnabled()); MOZ_ASSERT(aCallback); MOZ_ASSERT(!aObjectPath.IsEmpty()); MOZ_ASSERT(aInterface); - NS_ENSURE_TRUE(mConnection, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(GetDBusConnection(), NS_ERROR_FAILURE); nsAutoPtr serviceClass(new BluetoothServiceClass()); if (!strcmp(aInterface, DBUS_SINK_IFACE)) { @@ -2122,7 +2102,10 @@ BluetoothDBusService::SendAsyncDBusMessage(const nsAString& aObjectPath, return NS_ERROR_FAILURE; } - bool ret = mConnection->SendWithReply( + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool ret = connection->SendWithReply( aCallback, static_cast(serviceClass.forget()), -1, NS_ConvertUTF16toUTF8(aObjectPath).get(), aInterface, NS_ConvertUTF16toUTF8(aMessage).get(), @@ -2267,16 +2250,16 @@ protected: mObjectPath = GetObjectPathFromAddress(sAdapterPath, mDeviceAddresses[mProcessedDeviceAddresses]); - nsRefPtr threadConnection = gThreadConnection; + RawDBusConnection* connection = GetDBusConnection(); - if (!threadConnection.get()) { + if (!connection) { BT_WARNING("%s: DBus connection has been closed.", __FUNCTION__); return false; } nsRefPtr handler = this; - bool success = threadConnection->SendWithReply( + bool success = connection->SendWithReply( BluetoothArrayOfDevicePropertiesReplyHandler::Callback, handler.get(), 1000, NS_ConvertUTF16toUTF8(mObjectPath).get(), @@ -2435,10 +2418,13 @@ BluetoothDBusService::SetProperty(BluetoothObjectType aType, nsRefPtr runnable = aRunnable; + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + // msg is unref'd as part of SendWithReply - bool success = mConnection->SendWithReply(GetVoidCallback, - (void*)aRunnable, - 1000, msg); + bool success = connection->SendWithReply(GetVoidCallback, + (void*)aRunnable, + 1000, msg); if (!success) { BT_WARNING("SendWithReply failed"); return NS_ERROR_FAILURE; @@ -2476,9 +2462,12 @@ BluetoothDBusService::CreatePairedDeviceInternal( nsRefPtr runnable = aRunnable; MOZ_ASSERT(!sAdapterPath.IsEmpty()); + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + // Then send CreatePairedDevice, it will register a temp device agent then // unregister it after pairing process is over - bool ret = mConnection->SendWithReply( + bool ret = connection->SendWithReply( GetObjectPathCallback, (void*)runnable, aTimeout, NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, @@ -2531,7 +2520,10 @@ BluetoothDBusService::RemoveDeviceInternal(const nsAString& aDeviceAddress, nsRefPtr runnable(aRunnable); - bool success = mConnection->SendWithReply( + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool success = connection->SendWithReply( OnRemoveDeviceReply, static_cast(runnable.get()), -1, NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, "RemoveDevice", @@ -2582,7 +2574,9 @@ BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress, errorStr.AssignLiteral("Couldn't append arguments to dbus message."); result = false; } else { - result = mConnection->Send(reply); + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + result = connection->Send(reply); } dbus_message_unref(msg); @@ -2628,7 +2622,9 @@ BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress, errorStr.AssignLiteral("Couldn't append arguments to dbus message."); result = false; } else { - result = mConnection->Send(reply); + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + result = connection->Send(reply); } dbus_message_unref(msg); @@ -2672,7 +2668,10 @@ BluetoothDBusService::SetPairingConfirmationInternal( return false; } - bool result = mConnection->Send(reply); + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool result = connection->Send(reply); if (!result) { errorStr.AssignLiteral("Can't send message!"); } @@ -2915,7 +2914,10 @@ BluetoothDBusService::GetServiceChannel(const nsAString& aDeviceAddress, nsRefPtr handler = new OnGetServiceChannelReplyHandler(objectPath, aServiceUUID, aManager); - bool success = mConnection->SendWithReply( + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool success = connection->SendWithReply( OnGetServiceChannelReplyHandler::Callback, handler, -1, NS_ConvertUTF16toUTF8(objectPath).get(), DBUS_DEVICE_IFACE, "GetServiceAttributeValue", @@ -2960,7 +2962,6 @@ BluetoothDBusService::UpdateSdpRecords(const nsAString& aDeviceAddress, MOZ_ASSERT(!aDeviceAddress.IsEmpty()); MOZ_ASSERT(!sAdapterPath.IsEmpty()); MOZ_ASSERT(aManager); - MOZ_ASSERT(mConnection); nsString objectPath(GetObjectPathFromAddress(sAdapterPath, aDeviceAddress)); @@ -2969,13 +2970,16 @@ BluetoothDBusService::UpdateSdpRecords(const nsAString& aDeviceAddress, OnUpdateSdpRecordsRunnable* callbackRunnable = new OnUpdateSdpRecordsRunnable(objectPath, aManager); - return mConnection->SendWithReply(DiscoverServicesCallback, - (void*)callbackRunnable, -1, - NS_ConvertUTF16toUTF8(objectPath).get(), - DBUS_DEVICE_IFACE, - "DiscoverServices", - DBUS_TYPE_STRING, &EmptyCString(), - DBUS_TYPE_INVALID); + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + return connection->SendWithReply(DiscoverServicesCallback, + (void*)callbackRunnable, -1, + NS_ConvertUTF16toUTF8(objectPath).get(), + DBUS_DEVICE_IFACE, + "DiscoverServices", + DBUS_TYPE_STRING, &EmptyCString(), + DBUS_TYPE_INVALID); } void @@ -3152,7 +3156,10 @@ BluetoothDBusService::SendMetaData(const nsAString& aTitle, nsRefPtr runnable(aRunnable); - bool ret = mConnection->SendWithReply( + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool ret = connection->SendWithReply( GetVoidCallback, (void*)runnable.get(), -1, NS_ConvertUTF16toUTF8(objectPath).get(), DBUS_CTL_IFACE, "UpdateMetaData", @@ -3251,7 +3258,10 @@ BluetoothDBusService::SendPlayStatus(int64_t aDuration, nsRefPtr runnable(aRunnable); - bool ret = mConnection->SendWithReply( + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool ret = connection->SendWithReply( GetVoidCallback, (void*)runnable.get(), -1, NS_ConvertUTF16toUTF8(objectPath).get(), DBUS_CTL_IFACE, "UpdatePlayStatus", @@ -3300,7 +3310,10 @@ BluetoothDBusService::UpdatePlayStatus(uint32_t aDuration, uint32_t tempPlayStatus = aPlayStatus; - bool ret = mConnection->SendWithReply( + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool ret = connection->SendWithReply( ControlCallback, nullptr, -1, NS_ConvertUTF16toUTF8(objectPath).get(), DBUS_CTL_IFACE, "UpdatePlayStatus", @@ -3330,7 +3343,10 @@ BluetoothDBusService::UpdateNotification(ControlEventId aEventId, GetObjectPathFromAddress(sAdapterPath, address); uint16_t eventId = aEventId; - bool ret = mConnection->SendWithReply( + RawDBusConnection* connection = GetDBusConnection(); + MOZ_ASSERT(connection); + + bool ret = connection->SendWithReply( ControlCallback, nullptr, -1, NS_ConvertUTF16toUTF8(objectPath).get(), DBUS_CTL_IFACE, "UpdateNotification", diff --git a/dom/bluetooth/bluez/linux/BluetoothDBusService.h b/dom/bluetooth/bluez/linux/BluetoothDBusService.h index 251efd2982f0..0d939da9da3d 100644 --- a/dom/bluetooth/bluez/linux/BluetoothDBusService.h +++ b/dom/bluetooth/bluez/linux/BluetoothDBusService.h @@ -194,8 +194,6 @@ private: const char* aInterface, const nsAString& aMessage, mozilla::ipc::DBusReplyCallback aCallback); - - nsRefPtr mConnection; }; END_BLUETOOTH_NAMESPACE diff --git a/ipc/dbus/DBusThread.cpp b/ipc/dbus/DBusThread.cpp index 1ad3deae6b7d..a04e2b33f400 100644 --- a/ipc/dbus/DBusThread.cpp +++ b/ipc/dbus/DBusThread.cpp @@ -75,10 +75,14 @@ class DBusWatcher { public: DBusWatcher() + : mConnection(nullptr) { } ~DBusWatcher() - { } + { + // Connection has been released + MOZ_ASSERT(!mConnection); + } bool Initialize(); void CleanUp(); @@ -94,6 +98,8 @@ public: void HandleWatchAdd(); void HandleWatchRemove(); + RawDBusConnection* GetConnection(); + private: struct PollFdComparator { bool Equals(const pollfd& a, const pollfd& b) const { @@ -132,9 +138,15 @@ private: ScopedClose mControlFdR; ScopedClose mControlFdW; - nsRefPtr mConnection; + RawDBusConnection* mConnection; }; +RawDBusConnection* +DBusWatcher::GetConnection() +{ + return mConnection; +} + bool DBusWatcher::Initialize() { @@ -175,6 +187,9 @@ DBusWatcher::CleanUp() // DBusWatch pointers are maintained by DBus, so we won't leak by // clearing. mWatchData.Clear(); + + delete mConnection; + mConnection = nullptr; } void @@ -516,7 +531,7 @@ DBusWatcher::SetUp() mWatchData.AppendElement(static_cast(nullptr)); - nsRefPtr connection = new RawDBusConnection(); + RawDBusConnection* connection = new RawDBusConnection(); // If we can't establish a connection to dbus, nothing else will work nsresult rv = connection->EstablishDBusConnection(); @@ -652,5 +667,13 @@ DispatchToDBusThread(nsIRunnable* event) return NS_OK; } +RawDBusConnection* +GetDBusConnection() +{ + NS_ENSURE_TRUE(gDBusWatcher, nullptr); + + return gDBusWatcher->GetConnection(); +} + } } diff --git a/ipc/dbus/DBusThread.h b/ipc/dbus/DBusThread.h index fdce20afce8b..b67091e3b103 100644 --- a/ipc/dbus/DBusThread.h +++ b/ipc/dbus/DBusThread.h @@ -14,6 +14,8 @@ class nsIRunnable; namespace mozilla { namespace ipc { +class RawDBusConnection; + /** * Starts the DBus thread, which handles returning signals to objects * that call asynchronous functions. This should be called from the @@ -40,6 +42,9 @@ bool StopDBus(); nsresult DispatchToDBusThread(nsIRunnable* event); +RawDBusConnection* +GetDBusConnection(void); + } } diff --git a/ipc/dbus/RawDBusConnection.h b/ipc/dbus/RawDBusConnection.h index 616a964b3e2a..06329b7a1006 100644 --- a/ipc/dbus/RawDBusConnection.h +++ b/ipc/dbus/RawDBusConnection.h @@ -14,7 +14,6 @@ #include #include "nscore.h" #include "mozilla/Scoped.h" -#include #include struct DBusConnection; @@ -26,7 +25,7 @@ namespace ipc { typedef void (*DBusReplyCallback)(DBusMessage*, void*); -class RawDBusConnection : public AtomicRefCounted +class RawDBusConnection { struct ScopedDBusConnectionPtrTraits : ScopedFreePtrTraits { From b3c5ff39857d46237eabcc22b4cfb8ec0584b1b9 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 29 Oct 2013 16:03:40 +0100 Subject: [PATCH 072/459] Bug 932728: Use private DBus connection, r=qdot Using a private DBus connection gives each of its users, such as Bluetooth, its own connection to the DBus server. This simplifies the use of DBusWatch structures and ensures that all resources of a connection are free'd when the connection gets closed. --- ipc/dbus/RawDBusConnection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipc/dbus/RawDBusConnection.cpp b/ipc/dbus/RawDBusConnection.cpp index bc13d2e83b68..9b2e02a3754d 100644 --- a/ipc/dbus/RawDBusConnection.cpp +++ b/ipc/dbus/RawDBusConnection.cpp @@ -202,7 +202,7 @@ nsresult RawDBusConnection::EstablishDBusConnection() } DBusError err; dbus_error_init(&err); - mConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + mConnection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err); if (dbus_error_is_set(&err)) { dbus_error_free(&err); return NS_ERROR_FAILURE; @@ -214,6 +214,7 @@ nsresult RawDBusConnection::EstablishDBusConnection() void RawDBusConnection::ScopedDBusConnectionPtrTraits::release(DBusConnection* ptr) { if (ptr) { + dbus_connection_close(ptr); dbus_connection_unref(ptr); } } From 49a80cc3320bc68159f75f79692edd5bbbaa9bd4 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 29 Oct 2013 16:03:40 +0100 Subject: [PATCH 073/459] Bug 932728: Run DBus on the I/O thread, r=qdot This patch converts DBusWatcher to run on the I/O thread. When a caller starts DBus, StartDBus creates a new connection and adds it to the I/O thread's poll loop. DBusWatchers are created and removed automatically by the DBus libary. The I/O thread provides all features of the DBus thread. So most of the existing code has been removed or rewritten. The former includes the control socket and the DBus thread, the latter is in the DBusWatcher code. --- ipc/dbus/DBusThread.cpp | 632 +++++++-------------------------- ipc/dbus/DBusThread.h | 13 +- ipc/dbus/RawDBusConnection.cpp | 85 +++-- 3 files changed, 183 insertions(+), 547 deletions(-) diff --git a/ipc/dbus/DBusThread.cpp b/ipc/dbus/DBusThread.cpp index a04e2b33f400..53b635468220 100644 --- a/ipc/dbus/DBusThread.cpp +++ b/ipc/dbus/DBusThread.cpp @@ -66,79 +66,46 @@ #define LOG(args...) if (BTDEBUG) printf(args); #endif -#define DEFAULT_INITIAL_POLLFD_COUNT 8 - namespace mozilla { namespace ipc { -class DBusWatcher +class DBusWatcher : public MessageLoopForIO::Watcher { public: - DBusWatcher() - : mConnection(nullptr) - { } - - ~DBusWatcher() + DBusWatcher(RawDBusConnection* aConnection, DBusWatch* aWatch) + : mConnection(aConnection), + mWatch(aWatch) { - // Connection has been released - MOZ_ASSERT(!mConnection); + MOZ_ASSERT(mConnection); + MOZ_ASSERT(mWatch); } - bool Initialize(); - void CleanUp(); + ~DBusWatcher() + { } - void WakeUp(); - bool Stop(); + void StartWatching(); + void StopWatching(); - bool Poll(); - - bool AddWatch(DBusWatch* aWatch); - void RemoveWatch(DBusWatch* aWatch); - - void HandleWatchAdd(); - void HandleWatchRemove(); + static void FreeFunction(void* aData); + static dbus_bool_t AddWatchFunction(DBusWatch* aWatch, void* aData); + static void RemoveWatchFunction(DBusWatch* aWatch, void* aData); + static void ToggleWatchFunction(DBusWatch* aWatch, void* aData); RawDBusConnection* GetConnection(); private: - struct PollFdComparator { - bool Equals(const pollfd& a, const pollfd& b) const { - return ((a.fd == b.fd) && (a.events == b.events)); - } - bool LessThan(const pollfd& a, const pollfd&b) const { - return false; - } - }; + void OnFileCanReadWithoutBlocking(int aFd); + void OnFileCanWriteWithoutBlocking(int aFd); - enum DBusEventTypes { - DBUS_EVENT_LOOP_EXIT = 1, - DBUS_EVENT_LOOP_ADD = 2, - DBUS_EVENT_LOOP_REMOVE = 3, - DBUS_EVENT_LOOP_WAKEUP = 4 - }; + // Read watcher for libevent. Only to be accessed on IO Thread. + MessageLoopForIO::FileDescriptorWatcher mReadWatcher; - static unsigned int UnixEventsToDBusFlags(short events); - static short DBusFlagsToUnixEvents(unsigned int flags); - - static dbus_bool_t AddWatchFunction(DBusWatch* aWatch, void* aData); - static void RemoveWatchFunction(DBusWatch* aWatch, void* aData); - static void ToggleWatchFunction(DBusWatch* aWatch, void* aData); - static void DBusWakeupFunction(void* aData); - - bool SetUp(); - - // Information about the sockets we're polling. Socket counts - // increase/decrease depending on how many add/remove watch signals - // we're received via the control sockets. - nsTArray mPollData; - nsTArray mWatchData; - - // Sockets for receiving dbus control information (watch - // add/removes, loop shutdown, etc...) - ScopedClose mControlFdR; - ScopedClose mControlFdW; + // Write watcher for libevent. Only to be accessed on IO Thread. + MessageLoopForIO::FileDescriptorWatcher mWriteWatcher; + // DBus structures RawDBusConnection* mConnection; + DBusWatch* mWatch; }; RawDBusConnection* @@ -147,487 +114,166 @@ DBusWatcher::GetConnection() return mConnection; } -bool -DBusWatcher::Initialize() +void DBusWatcher::StartWatching() { - if (!SetUp()) { - CleanUp(); - return false; - } + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(mWatch); - return true; + int fd = dbus_watch_get_unix_fd(mWatch); + + MessageLoopForIO* ioLoop = MessageLoopForIO::current(); + ioLoop->WatchFileDescriptor(fd, true, MessageLoopForIO::WATCH_READ, + &mReadWatcher, this); + ioLoop->WatchFileDescriptor(fd, true, MessageLoopForIO::WATCH_WRITE, + &mWriteWatcher, this); } -void -DBusWatcher::CleanUp() +void DBusWatcher::StopWatching() { MOZ_ASSERT(!NS_IsMainThread()); - dbus_connection_set_wakeup_main_function(mConnection->GetConnection(), - nullptr, nullptr, nullptr); - dbus_bool_t success = - dbus_connection_set_watch_functions(mConnection->GetConnection(), - nullptr, nullptr, nullptr, - nullptr, nullptr); - if (success != TRUE) { - NS_WARNING("dbus_connection_set_watch_functions failed"); - } - -#ifdef DEBUG - LOG("Removing DBus Sockets\n"); -#endif - if (mControlFdW.get()) { - mControlFdW.dispose(); - } - if (mControlFdR.get()) { - mControlFdR.dispose(); - } - mPollData.Clear(); - - // DBusWatch pointers are maintained by DBus, so we won't leak by - // clearing. - mWatchData.Clear(); - - delete mConnection; - mConnection = nullptr; -} - -void -DBusWatcher::WakeUp() -{ - static const char control = DBUS_EVENT_LOOP_WAKEUP; - - struct pollfd fds = { - mControlFdW.get(), - POLLOUT, - 0 - }; - - int nfds = TEMP_FAILURE_RETRY(poll(&fds, 1, 0)); - NS_ENSURE_TRUE_VOID(nfds == 1); - NS_ENSURE_TRUE_VOID(fds.revents == POLLOUT); - - ssize_t res = TEMP_FAILURE_RETRY( - write(mControlFdW.get(), &control, sizeof(control))); - if (res < 0) { - NS_WARNING("Cannot write wakeup bit to DBus controller!"); - } -} - -bool -DBusWatcher::Stop() -{ - static const char data = DBUS_EVENT_LOOP_EXIT; - - ssize_t res = - TEMP_FAILURE_RETRY(write(mControlFdW.get(), &data, sizeof(data))); - NS_ENSURE_TRUE(res == 1, false); - - return true; -} - -bool -DBusWatcher::Poll() -{ - int res = TEMP_FAILURE_RETRY(poll(mPollData.Elements(), - mPollData.Length(), -1)); - NS_ENSURE_TRUE(res > 0, false); - - bool continueThread = true; - - nsTArray::size_type i = 0; - - while (i < mPollData.Length()) { - if (mPollData[i].revents == POLLIN) { - if (mPollData[i].fd == mControlFdR.get()) { - char data; - res = TEMP_FAILURE_RETRY(read(mControlFdR.get(), &data, sizeof(data))); - NS_ENSURE_TRUE(res > 0, false); - - switch (data) { - case DBUS_EVENT_LOOP_EXIT: - continueThread = false; - break; - case DBUS_EVENT_LOOP_ADD: - HandleWatchAdd(); - break; - case DBUS_EVENT_LOOP_REMOVE: - HandleWatchRemove(); - // don't increment i, or we'll skip one element - continue; - case DBUS_EVENT_LOOP_WAKEUP: - NS_ProcessPendingEvents(NS_GetCurrentThread(), - PR_INTERVAL_NO_TIMEOUT); - break; - default: -#if DEBUG - nsCString warning("unknown command "); - warning.AppendInt(data); - NS_WARNING(warning.get()); -#endif - break; - } - } else { - short events = mPollData[i].revents; - mPollData[i].revents = 0; - - dbus_watch_handle(mWatchData[i], UnixEventsToDBusFlags(events)); - - DBusDispatchStatus dbusDispatchStatus; - do { - dbusDispatchStatus = - dbus_connection_dispatch(mConnection->GetConnection()); - } while (dbusDispatchStatus == DBUS_DISPATCH_DATA_REMAINS); - - // Break at this point since we don't know if the operation - // was destructive - break; - } - } - - ++i; - } - - return continueThread; -} - -bool -DBusWatcher::AddWatch(DBusWatch* aWatch) -{ - static const char control = DBUS_EVENT_LOOP_ADD; - - if (dbus_watch_get_enabled(aWatch) == FALSE) { - return true; - } - - // note that we can't just send the watch and inspect it later - // because we may get a removeWatch call before this data is reacted - // to by our eventloop and remove this watch.. reading the add first - // and then inspecting the recently deceased watch would be bad. - ssize_t res = - TEMP_FAILURE_RETRY(write(mControlFdW.get(),&control, sizeof(control))); - if (res < 0) { - LOG("Cannot write DBus add watch control data to socket!\n"); - return false; - } - - int fd = dbus_watch_get_unix_fd(aWatch); - res = TEMP_FAILURE_RETRY(write(mControlFdW.get(), &fd, sizeof(fd))); - if (res < 0) { - LOG("Cannot write DBus add watch descriptor data to socket!\n"); - return false; - } - - unsigned int flags = dbus_watch_get_flags(aWatch); - res = TEMP_FAILURE_RETRY(write(mControlFdW.get(), &flags, sizeof(flags))); - if (res < 0) { - LOG("Cannot write DBus add watch flag data to socket!\n"); - return false; - } - - res = TEMP_FAILURE_RETRY(write(mControlFdW.get(), &aWatch, sizeof(aWatch))); - if (res < 0) { - LOG("Cannot write DBus add watch struct data to socket!\n"); - return false; - } - - return true; -} - -void -DBusWatcher::RemoveWatch(DBusWatch* aWatch) -{ - static const char control = DBUS_EVENT_LOOP_REMOVE; - - ssize_t res = - TEMP_FAILURE_RETRY(write(mControlFdW.get(), &control, sizeof(control))); - if (res < 0) { - LOG("Cannot write DBus remove watch control data to socket!\n"); - return; - } - - int fd = dbus_watch_get_unix_fd(aWatch); - res = TEMP_FAILURE_RETRY(write(mControlFdW.get(), &fd, sizeof(fd))); - if (res < 0) { - LOG("Cannot write DBus remove watch descriptor data to socket!\n"); - return; - } - - unsigned int flags = dbus_watch_get_flags(aWatch); - res = TEMP_FAILURE_RETRY(write(mControlFdW.get(), &flags, sizeof(flags))); - if (res < 0) { - LOG("Cannot write DBus remove watch flag data to socket!\n"); - return; - } -} - -void -DBusWatcher::HandleWatchAdd() -{ - int fd; - ssize_t res = TEMP_FAILURE_RETRY(read(mControlFdR.get(), &fd, sizeof(fd))); - if (res < 0) { - LOG("Cannot read DBus watch add descriptor data from socket!\n"); - return; - } - - unsigned int flags; - res = TEMP_FAILURE_RETRY(read(mControlFdR.get(), &flags, sizeof(flags))); - if (res < 0) { - LOG("Cannot read DBus watch add flag data from socket!\n"); - return; - } - - DBusWatch* watch; - res = TEMP_FAILURE_RETRY(read(mControlFdR.get(), &watch, sizeof(watch))); - if (res < 0) { - LOG("Cannot read DBus watch add watch data from socket!\n"); - return; - } - - struct pollfd p = { - fd, // .fd - DBusFlagsToUnixEvents(flags), // .events - 0 // .revents - }; - if (mPollData.Contains(p, PollFdComparator())) { - return; - } - mPollData.AppendElement(p); - mWatchData.AppendElement(watch); -} - -void -DBusWatcher::HandleWatchRemove() -{ - int fd; - ssize_t res = TEMP_FAILURE_RETRY(read(mControlFdR.get(), &fd, sizeof(fd))); - if (res < 0) { - LOG("Cannot read DBus watch remove descriptor data from socket!\n"); - return; - } - - unsigned int flags; - res = TEMP_FAILURE_RETRY(read(mControlFdR.get(), &flags, sizeof(flags))); - if (res < 0) { - LOG("Cannot read DBus watch remove flag data from socket!\n"); - return; - } - - struct pollfd p = { - fd, // .fd - DBusFlagsToUnixEvents(flags), // .events - 0 // .revents - }; - int index = mPollData.IndexOf(p, 0, PollFdComparator()); - // There are times where removes can be requested for watches that - // haven't been added (for example, whenever gecko comes up after - // adapters have already been enabled), so check to make sure we're - // using the watch in the first place - if (index < 0) { - LOG("DBus requested watch removal of non-existant socket, ignoring..."); - return; - } - mPollData.RemoveElementAt(index); - - // DBusWatch pointers are maintained by DBus, so we won't leak by - // removing. - mWatchData.RemoveElementAt(index); -} - -// Flag conversion - -unsigned int -DBusWatcher::UnixEventsToDBusFlags(short events) -{ - return (events & DBUS_WATCH_READABLE ? POLLIN : 0) | - (events & DBUS_WATCH_WRITABLE ? POLLOUT : 0) | - (events & DBUS_WATCH_ERROR ? POLLERR : 0) | - (events & DBUS_WATCH_HANGUP ? POLLHUP : 0); -} - -short -DBusWatcher::DBusFlagsToUnixEvents(unsigned int flags) -{ - return (flags & POLLIN ? DBUS_WATCH_READABLE : 0) | - (flags & POLLOUT ? DBUS_WATCH_WRITABLE : 0) | - (flags & POLLERR ? DBUS_WATCH_ERROR : 0) | - (flags & POLLHUP ? DBUS_WATCH_HANGUP : 0); + mReadWatcher.StopWatchingFileDescriptor(); + mWriteWatcher.StopWatchingFileDescriptor(); } // DBus utility functions, used as function pointers in DBus setup +void +DBusWatcher::FreeFunction(void* aData) +{ + delete static_cast(aData); +} + dbus_bool_t DBusWatcher::AddWatchFunction(DBusWatch* aWatch, void* aData) { - MOZ_ASSERT(aData); - DBusWatcher* dbusWatcher = static_cast(aData); - return dbusWatcher->AddWatch(aWatch); + MOZ_ASSERT(!NS_IsMainThread()); + + RawDBusConnection* connection = static_cast(aData); + + DBusWatcher* dbusWatcher = new DBusWatcher(connection, aWatch); + dbus_watch_set_data(aWatch, dbusWatcher, DBusWatcher::FreeFunction); + + if (dbus_watch_get_enabled(aWatch)) { + dbusWatcher->StartWatching(); + } + + return TRUE; } void DBusWatcher::RemoveWatchFunction(DBusWatch* aWatch, void* aData) { - MOZ_ASSERT(aData); - DBusWatcher* dbusWatcher = static_cast(aData); - dbusWatcher->RemoveWatch(aWatch); + MOZ_ASSERT(!NS_IsMainThread()); + + DBusWatcher* dbusWatcher = + static_cast(dbus_watch_get_data(aWatch)); + dbusWatcher->StopWatching(); } void DBusWatcher::ToggleWatchFunction(DBusWatch* aWatch, void* aData) { - MOZ_ASSERT(aData); - DBusWatcher* dbusWatcher = static_cast(aData); + MOZ_ASSERT(!NS_IsMainThread()); + + DBusWatcher* dbusWatcher = + static_cast(dbus_watch_get_data(aWatch)); if (dbus_watch_get_enabled(aWatch)) { - dbusWatcher->AddWatch(aWatch); + dbusWatcher->StartWatching(); } else { - dbusWatcher->RemoveWatch(aWatch); + dbusWatcher->StopWatching(); } } void -DBusWatcher::DBusWakeupFunction(void* aData) -{ - MOZ_ASSERT(aData); - DBusWatcher* dbusWatcher = static_cast(aData); - dbusWatcher->WakeUp(); -} - -bool -DBusWatcher::SetUp() +DBusWatcher::OnFileCanReadWithoutBlocking(int aFd) { MOZ_ASSERT(!NS_IsMainThread()); - // If we already have a connection, exit - if (mConnection) { - return false; - } + dbus_watch_handle(mWatch, DBUS_WATCH_READABLE); - // socketpair opens two sockets for the process to communicate on. - // This is how android's implementation of the dbus event loop - // communicates with itself in relation to IPC signals. These - // sockets are contained sequentially in the same struct in the - // android code, but we break them out into class members here. - // Therefore we read into a local array and then copy. - - int sockets[2]; - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets) < 0) { - return false; - } - - mControlFdR.rwget() = sockets[0]; - mControlFdW.rwget() = sockets[1]; - - pollfd* p = mPollData.AppendElement(); - - p->fd = mControlFdR.get(); - p->events = POLLIN; - p->revents = 0; - - // Due to the fact that mPollData and mWatchData have to match, we - // push a null to the front of mWatchData since it has the control - // fd in the first slot of mPollData. - - mWatchData.AppendElement(static_cast(nullptr)); - - RawDBusConnection* connection = new RawDBusConnection(); - - // If we can't establish a connection to dbus, nothing else will work - nsresult rv = connection->EstablishDBusConnection(); - if (NS_FAILED(rv)) { - NS_WARNING("Cannot create DBus Connection for DBus Thread!"); - return false; - } - - dbus_bool_t success = - dbus_connection_set_watch_functions(connection->GetConnection(), - AddWatchFunction, RemoveWatchFunction, - ToggleWatchFunction, this, nullptr); - NS_ENSURE_TRUE(success == TRUE, false); - - dbus_connection_set_wakeup_main_function(connection->GetConnection(), - DBusWakeupFunction, - this, nullptr); - - mConnection = connection; - - return true; + DBusDispatchStatus dbusDispatchStatus; + do { + dbusDispatchStatus = + dbus_connection_dispatch(mConnection->GetConnection()); + } while (dbusDispatchStatus == DBUS_DISPATCH_DATA_REMAINS); } -// Main task for polling the DBus system +void +DBusWatcher::OnFileCanWriteWithoutBlocking(int aFd) +{ + MOZ_ASSERT(!NS_IsMainThread()); -class DBusPollTask : public nsRunnable + dbus_watch_handle(mWatch, DBUS_WATCH_WRITABLE); +} + +class WatchDBusConnectionTask : public Task { public: - DBusPollTask(DBusWatcher* aDBusWatcher) - : mDBusWatcher(aDBusWatcher) - { } + WatchDBusConnectionTask(RawDBusConnection* aConnection) + : mConnection(aConnection) + { + MOZ_ASSERT(mConnection); + } - NS_IMETHOD Run() + void Run() { MOZ_ASSERT(!NS_IsMainThread()); - bool continueThread; - - do { - continueThread = mDBusWatcher->Poll(); - } while (continueThread); - - mDBusWatcher->CleanUp(); - - nsIThread* thread; - nsresult rv = NS_GetCurrentThread(&thread); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr runnable = - NS_NewRunnableMethod(thread, &nsIThread::Shutdown); - rv = NS_DispatchToMainThread(runnable); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; + dbus_bool_t success = + dbus_connection_set_watch_functions(mConnection->GetConnection(), + DBusWatcher::AddWatchFunction, + DBusWatcher::RemoveWatchFunction, + DBusWatcher::ToggleWatchFunction, + mConnection, nullptr); + NS_ENSURE_TRUE_VOID(success == TRUE); } private: - nsAutoPtr mDBusWatcher; + RawDBusConnection* mConnection; }; -static DBusWatcher* gDBusWatcher; -static StaticRefPtr gDBusServiceThread; +class DeleteDBusConnectionTask : public Task +{ +public: + DeleteDBusConnectionTask(RawDBusConnection* aConnection) + : mConnection(aConnection) + { + MOZ_ASSERT(mConnection); + } -// Startup/Shutdown utility functions + void Run() + { + MOZ_ASSERT(!NS_IsMainThread()); + + // This command closes the DBus connection and all instances of + // DBusWatch will be removed and free'd. + delete mConnection; + } + +private: + RawDBusConnection* mConnection; +}; + + // Startup/Shutdown utility functions + +static RawDBusConnection* gDBusConnection; bool StartDBus() { MOZ_ASSERT(!NS_IsMainThread()); - NS_ENSURE_TRUE(!gDBusWatcher, true); + NS_ENSURE_TRUE(!gDBusConnection, true); - nsAutoPtr dbusWatcher(new DBusWatcher()); - - bool eventLoopStarted = dbusWatcher->Initialize(); - NS_ENSURE_TRUE(eventLoopStarted, false); - - nsresult rv; - - if (!gDBusServiceThread) { - nsIThread* dbusServiceThread; - rv = NS_NewNamedThread("DBus Thread", &dbusServiceThread); - NS_ENSURE_SUCCESS(rv, false); - gDBusServiceThread = dbusServiceThread; - } - -#ifdef DEBUG - LOG("DBus Thread Starting\n"); -#endif - - nsRefPtr pollTask(new DBusPollTask(dbusWatcher)); - NS_ENSURE_TRUE(pollTask, false); - - rv = gDBusServiceThread->Dispatch(pollTask, NS_DISPATCH_NORMAL); + RawDBusConnection* connection = new RawDBusConnection(); + nsresult rv = connection->EstablishDBusConnection(); NS_ENSURE_SUCCESS(rv, false); - gDBusWatcher = dbusWatcher.forget(); + XRE_GetIOMessageLoop()->PostTask(FROM_HERE, + new WatchDBusConnectionTask(connection)); + + gDBusConnection = connection; return true; } @@ -636,33 +282,21 @@ bool StopDBus() { MOZ_ASSERT(!NS_IsMainThread()); - NS_ENSURE_TRUE(gDBusServiceThread, true); + NS_ENSURE_TRUE(gDBusConnection, true); - DBusWatcher* dbusWatcher = gDBusWatcher; - gDBusWatcher = nullptr; + RawDBusConnection* connection = gDBusConnection; + gDBusConnection = nullptr; - if (dbusWatcher && !dbusWatcher->Stop()) { - return false; - } - - gDBusServiceThread = nullptr; + XRE_GetIOMessageLoop()->PostTask(FROM_HERE, + new DeleteDBusConnectionTask(connection)); return true; } nsresult -DispatchToDBusThread(nsIRunnable* event) +DispatchToDBusThread(Task* task) { - nsRefPtr dbusServiceThread(gDBusServiceThread); - DBusWatcher* dbusWatcher = gDBusWatcher; - - NS_ENSURE_TRUE(dbusServiceThread.get() && dbusWatcher, - NS_ERROR_NOT_INITIALIZED); - - nsresult rv = dbusServiceThread->Dispatch(event, NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv, rv); - - dbusWatcher->WakeUp(); + XRE_GetIOMessageLoop()->PostTask(FROM_HERE, task); return NS_OK; } @@ -670,9 +304,9 @@ DispatchToDBusThread(nsIRunnable* event) RawDBusConnection* GetDBusConnection() { - NS_ENSURE_TRUE(gDBusWatcher, nullptr); + NS_ENSURE_TRUE(gDBusConnection, nullptr); - return gDBusWatcher->GetConnection(); + return gDBusConnection; } } diff --git a/ipc/dbus/DBusThread.h b/ipc/dbus/DBusThread.h index b67091e3b103..e688b1555935 100644 --- a/ipc/dbus/DBusThread.h +++ b/ipc/dbus/DBusThread.h @@ -9,7 +9,7 @@ #include "nscore.h" -class nsIRunnable; +class Task; namespace mozilla { namespace ipc { @@ -34,14 +34,19 @@ bool StartDBus(); bool StopDBus(); /** - * Dispatch an event to the DBus thread + * Dispatch a task to the DBus I/O thread * - * @param event An nsIRunnable to run in the DBus thread + * @param task A task to run on the DBus I/O thread * @return NS_OK on success, or an error code otherwise */ nsresult -DispatchToDBusThread(nsIRunnable* event); +DispatchToDBusThread(Task* task); +/** + * Returns the connection to the DBus server + * + * @return The DBus connection on success, or nullptr otherwise + */ RawDBusConnection* GetDBusConnection(void); diff --git a/ipc/dbus/RawDBusConnection.cpp b/ipc/dbus/RawDBusConnection.cpp index 9b2e02a3754d..588bd8e82dd4 100644 --- a/ipc/dbus/RawDBusConnection.cpp +++ b/ipc/dbus/RawDBusConnection.cpp @@ -5,6 +5,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include "base/message_loop.h" #include "mozilla/Monitor.h" #include "nsThreadUtils.h" #include "DBusThread.h" @@ -34,11 +35,15 @@ using namespace mozilla::ipc; namespace mozilla { namespace ipc { -class DBusConnectionSendRunnableBase : public nsRunnable +class DBusConnectionSendTaskBase : public Task { +public: + virtual ~DBusConnectionSendTaskBase() + { } + protected: - DBusConnectionSendRunnableBase(DBusConnection* aConnection, - DBusMessage* aMessage) + DBusConnectionSendTaskBase(DBusConnection* aConnection, + DBusMessage* aMessage) : mConnection(aConnection), mMessage(aMessage) { @@ -46,9 +51,6 @@ protected: MOZ_ASSERT(mMessage); } - virtual ~DBusConnectionSendRunnableBase() - { } - DBusConnection* mConnection; DBusMessageRefPtr mMessage; }; @@ -57,35 +59,33 @@ protected: // Sends a message and returns the message's serial number to the // disaptching thread. Only run it in DBus thread. // -class DBusConnectionSendRunnable : public DBusConnectionSendRunnableBase +class DBusConnectionSendTask : public DBusConnectionSendTaskBase { public: - DBusConnectionSendRunnable(DBusConnection* aConnection, - DBusMessage* aMessage) - : DBusConnectionSendRunnableBase(aConnection, aMessage) + DBusConnectionSendTask(DBusConnection* aConnection, + DBusMessage* aMessage) + : DBusConnectionSendTaskBase(aConnection, aMessage) { } - NS_IMETHOD Run() + virtual ~DBusConnectionSendTask() + { } + + void Run() MOZ_OVERRIDE { - MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(MessageLoop::current()); - dbus_bool_t success = dbus_connection_send(mConnection, mMessage, nullptr); - - NS_ENSURE_TRUE(success == TRUE, NS_ERROR_FAILURE); - - return NS_OK; + dbus_bool_t success = dbus_connection_send(mConnection, + mMessage, + nullptr); + NS_ENSURE_TRUE_VOID(success == TRUE); } - -protected: - ~DBusConnectionSendRunnable() - { } }; // // Sends a message and executes a callback function for the reply. Only // run it in DBus thread. // -class DBusConnectionSendWithReplyRunnable : public DBusConnectionSendRunnableBase +class DBusConnectionSendWithReplyTask : public DBusConnectionSendTaskBase { private: class NotifyData @@ -130,24 +130,27 @@ private: } public: - DBusConnectionSendWithReplyRunnable(DBusConnection* aConnection, - DBusMessage* aMessage, - int aTimeout, - DBusReplyCallback aCallback, - void* aData) - : DBusConnectionSendRunnableBase(aConnection, aMessage), + DBusConnectionSendWithReplyTask(DBusConnection* aConnection, + DBusMessage* aMessage, + int aTimeout, + DBusReplyCallback aCallback, + void* aData) + : DBusConnectionSendTaskBase(aConnection, aMessage), mCallback(aCallback), mData(aData), mTimeout(aTimeout) { } - NS_IMETHOD Run() + virtual ~DBusConnectionSendWithReplyTask() + { } + + void Run() MOZ_OVERRIDE { - MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(MessageLoop::current()); // Freed at end of Notify nsAutoPtr data(new NotifyData(mCallback, mData)); - NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY); + NS_ENSURE_TRUE_VOID(data); DBusPendingCall* call; @@ -155,21 +158,15 @@ public: mMessage, &call, mTimeout); - NS_ENSURE_TRUE(success == TRUE, NS_ERROR_FAILURE); + NS_ENSURE_TRUE_VOID(success == TRUE); success = dbus_pending_call_set_notify(call, Notify, data, nullptr); - NS_ENSURE_TRUE(success == TRUE, NS_ERROR_FAILURE); + NS_ENSURE_TRUE_VOID(success == TRUE); data.forget(); dbus_message_unref(mMessage); - - return NS_OK; }; -protected: - ~DBusConnectionSendWithReplyRunnable() - { } - private: DBusReplyCallback mCallback; void* mData; @@ -221,8 +218,8 @@ void RawDBusConnection::ScopedDBusConnectionPtrTraits::release(DBusConnection* p bool RawDBusConnection::Send(DBusMessage* aMessage) { - nsRefPtr t( - new DBusConnectionSendRunnable(mConnection, aMessage)); + DBusConnectionSendTask* t = + new DBusConnectionSendTask(mConnection, aMessage); MOZ_ASSERT(t); nsresult rv = DispatchToDBusThread(t); @@ -242,9 +239,9 @@ bool RawDBusConnection::SendWithReply(DBusReplyCallback aCallback, int aTimeout, DBusMessage* aMessage) { - nsRefPtr t( - new DBusConnectionSendWithReplyRunnable(mConnection, aMessage, - aTimeout, aCallback, aData)); + DBusConnectionSendWithReplyTask* t = + new DBusConnectionSendWithReplyTask(mConnection, aMessage, aTimeout, + aCallback, aData); MOZ_ASSERT(t); nsresult rv = DispatchToDBusThread(t); From 6c0a7b39d84036f3b4d4013cb6c1eaebec11f543 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Thu, 12 Dec 2013 13:52:35 -0800 Subject: [PATCH 074/459] Bug 948459: Reliably clean up DebugScopes::liveScopes when DebugScopeObjects are finalized. r=luke --- js/src/vm/ScopeObject.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index b3a069b1c397..4997f4022e25 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -1600,8 +1600,28 @@ DebugScopes::sweep(JSRuntime *rt) * creating an uncollectable cycle with suspended generator frames. */ for (MissingScopeMap::Enum e(missingScopes); !e.empty(); e.popFront()) { - if (IsObjectAboutToBeFinalized(e.front().value().unsafeGet())) + DebugScopeObject **debugScope = e.front().value().unsafeGet(); + if (IsObjectAboutToBeFinalized(debugScope)) { + /* + * Note that onPopCall and onPopBlock rely on missingScopes to find + * scope objects that we synthesized for the debugger's sake, and + * clean up the synthetic scope objects' entries in liveScopes. So + * if we remove an entry frcom missingScopes here, we must also + * remove the corresponding liveScopes entry. + * + * Since the DebugScopeObject is the only thing using its scope + * object, and the DSO is about to be finalized, you might assume + * that the synthetic SO is also about to be finalized too, and thus + * the loop below will take care of things. But complex GC behavior + * means that marks are only conservative approximations of + * liveness; we should assume that anything could be marked. + * + * Thus, we must explicitly remove the entries from both liveScopes + * and missingScopes here. + */ + liveScopes.remove(&(*debugScope)->scope()); e.removeFront(); + } } for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) { From e3463fe50e31aba1602d0f0d3671b52641665404 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Thu, 12 Dec 2013 14:13:20 -0800 Subject: [PATCH 075/459] Bug 948238 - Read browser.tabs.remote once at startup and never again (r=bsmedberg) --- accessible/src/windows/msaa/nsWinUtils.cpp | 2 +- browser/base/content/browser.js | 2 +- browser/base/content/tabbrowser.xml | 4 ++-- browser/components/nsBrowserGlue.js | 2 +- .../sessionstore/src/SessionStore.jsm | 2 +- browser/metro/base/content/browser.js | 2 +- dom/ipc/ContentParent.cpp | 3 ++- dom/ipc/TabChild.cpp | 5 +++-- gfx/thebes/gfxPlatform.cpp | 3 ++- layout/forms/nsListControlFrame.cpp | 4 ++-- .../components/satchel/nsFormAutoComplete.js | 5 ++--- toolkit/xre/nsAppRunner.cpp | 21 +++++++++++++++++++ widget/xpwidgets/nsBaseWidget.cpp | 2 +- xpcom/system/nsIXULRuntime.idl | 17 ++++++++++++++- 14 files changed, 56 insertions(+), 18 deletions(-) diff --git a/accessible/src/windows/msaa/nsWinUtils.cpp b/accessible/src/windows/msaa/nsWinUtils.cpp index 86fc214aeb93..c21bf8835c01 100644 --- a/accessible/src/windows/msaa/nsWinUtils.cpp +++ b/accessible/src/windows/msaa/nsWinUtils.cpp @@ -58,7 +58,7 @@ nsWinUtils::MaybeStartWindowEmulation() // with tabs. if (Compatibility::IsJAWS() || Compatibility::IsWE() || Compatibility::IsDolphin() || - Preferences::GetBool("browser.tabs.remote")) { + BrowserTabsRemote()) { RegisterNativeWindow(kClassNameTabContent); sHWNDCache = new nsRefPtrHashtable, DocAccessible>(4); return true; diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 649bb5091852..453e481035ed 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -742,7 +742,7 @@ var gBrowserInit = { delayedStartupFinished: false, onLoad: function() { - gMultiProcessBrowser = gPrefService.getBoolPref("browser.tabs.remote"); + gMultiProcessBrowser = Services.appinfo.browserTabsRemote; var mustLoadSidebar = false; diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 7ca0cd0f28c3..8dab3c672dd3 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3037,7 +3037,7 @@ "-moz-default-background-color" : Services.prefs.getCharPref("browser.display.background_color"); - if (Services.prefs.getBoolPref("browser.tabs.remote")) { + if (Services.appinfo.browserTabsRemote) { messageManager.addMessageListener("DOMTitleChanged", this); messageManager.addMessageListener("contextmenu", this); } @@ -3103,7 +3103,7 @@ document.removeEventListener("keypress", this, false); window.removeEventListener("sizemodechange", this, false); - if (Services.prefs.getBoolPref("browser.tabs.remote")) { + if (Services.appinfo.browserTabsRemote) { messageManager.removeMessageListener("DOMTitleChanged", this); messageManager.removeMessageListener("contextmenu", this); } diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index b458b436b41b..f17ce6701b2d 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -481,7 +481,7 @@ BrowserGlue.prototype = { SessionStore.init(); BrowserUITelemetry.init(); - if (Services.prefs.getBoolPref("browser.tabs.remote")) + if (Services.appinfo.browserTabsRemote) ContentClick.init(); Services.obs.notifyObservers(null, "browser-ui-startup-complete", ""); diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm index c809c36b6074..0d6b5aacd262 100644 --- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -389,7 +389,7 @@ let SessionStoreInternal = { throw new Error("SessionStore.init() must only be called once!"); } - this._disabledForMultiProcess = Services.prefs.getBoolPref("browser.tabs.remote"); + this._disabledForMultiProcess = Services.appinfo.browserTabsRemote; if (this._disabledForMultiProcess) { this._deferredInitialized.resolve(); return; diff --git a/browser/metro/base/content/browser.js b/browser/metro/base/content/browser.js index 073302c581e8..de5315929d34 100644 --- a/browser/metro/base/content/browser.js +++ b/browser/metro/base/content/browser.js @@ -1429,7 +1429,7 @@ Tab.prototype = { browser.setAttribute("type", "content"); - let useRemote = Services.prefs.getBoolPref("browser.tabs.remote"); + let useRemote = Services.appinfo.browserTabsRemote; let useLocal = Util.isLocalScheme(aURI); browser.setAttribute("remote", (!useLocal && useRemote) ? "true" : "false"); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index ba5c80569a8f..1bda5817ddff 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -79,6 +79,7 @@ #include "nsISupportsPrimitives.h" #include "nsIURIFixup.h" #include "nsIWindowWatcher.h" +#include "nsIXULRuntime.h" #include "nsMemoryReporterManager.h" #include "nsServiceManagerUtils.h" #include "nsStyleSheetService.h" @@ -3113,7 +3114,7 @@ ContentParent::ShouldContinueFromReplyTimeout() { // The only time ContentParent sends blocking messages is for CPOWs, so // timeouts should only ever occur in electrolysis-enabled sessions. - MOZ_ASSERT(Preferences::GetBool("browser.tabs.remote", false)); + MOZ_ASSERT(BrowserTabsRemote()); return false; } diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 2ce740927236..8306641297ff 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -55,6 +55,7 @@ #include "nsIWebBrowserFocus.h" #include "nsIWebBrowserSetup.h" #include "nsIWebProgress.h" +#include "nsIXULRuntime.h" #include "nsInterfaceHashtable.h" #include "nsPIDOMWindow.h" #include "nsPIWindowRoot.h" @@ -2314,8 +2315,8 @@ TabChild::InitRenderingState() false); } - // This state can't really change during the lifetime of the child. - sCpowsEnabled = Preferences::GetBool("browser.tabs.remote", false); + // This state can't change during the lifetime of the child. + sCpowsEnabled = BrowserTabsRemote(); if (Preferences::GetBool("dom.ipc.cpows.force-enabled", false)) sCpowsEnabled = true; diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 69df30cc6475..b563f75429e4 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -85,6 +85,7 @@ #include "mozilla/Mutex.h" #include "nsIGfxInfo.h" +#include "nsIXULRuntime.h" using namespace mozilla; using namespace mozilla::layers; @@ -2075,7 +2076,7 @@ InitLayersAccelerationPrefs() sPrefLayoutFrameRate = Preferences::GetInt("layout.frame_rate", -1); sBufferRotationEnabled = Preferences::GetBool("layers.bufferrotation.enabled", true); sComponentAlphaEnabled = Preferences::GetBool("layers.componentalpha.enabled", true); - sPrefBrowserTabsRemote = Preferences::GetBool("browser.tabs.remote", false); + sPrefBrowserTabsRemote = BrowserTabsRemote(); #ifdef XP_WIN if (sPrefLayersAccelerationForceEnabled) { diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index 99488a81bcec..f5b039c73c2b 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -16,6 +16,7 @@ #include "nsIPresShell.h" #include "nsEventStateManager.h" #include "nsIDOMMouseEvent.h" +#include "nsIXULRuntime.h" #include "nsFontMetrics.h" #include "nsIScrollableFrame.h" #include "nsCSSRendering.h" @@ -1806,8 +1807,7 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent) } else { // NOTE: the combo box is responsible for dropping it down if (mComboboxFrame) { - if (XRE_GetProcessType() == GeckoProcessType_Content && - Preferences::GetBool("browser.tabs.remote", false)) { + if (XRE_GetProcessType() == GeckoProcessType_Content && BrowserTabsRemote()) { nsContentUtils::DispatchChromeEvent(mContent->OwnerDoc(), mContent, NS_LITERAL_STRING("mozshowdropdown"), true, false); diff --git a/toolkit/components/satchel/nsFormAutoComplete.js b/toolkit/components/satchel/nsFormAutoComplete.js index 9fc3447ceb6c..5c454ac2a6e0 100644 --- a/toolkit/components/satchel/nsFormAutoComplete.js +++ b/toolkit/components/satchel/nsFormAutoComplete.js @@ -488,9 +488,8 @@ FormAutoCompleteResult.prototype = { }; -if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT && - Services.prefs.prefHasUserValue("browser.tabs.remote") && - Services.prefs.getBoolPref("browser.tabs.remote")) { +let remote = Services.appinfo.browserTabsRemote; +if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT && remote) { // Register the stub FormAutoComplete module in the child which will // forward messages to the parent through the process message manager. let component = [FormAutoCompleteChild]; diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index f5dd1d72496a..d4f9f5cbdb5d 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -789,6 +789,16 @@ nsXULAppInfo::GetProcessType(uint32_t* aResult) return NS_OK; } +static bool gBrowserTabsRemote = false; +static bool gBrowserTabsRemoteInitialized = false; + +NS_IMETHODIMP +nsXULAppInfo::GetBrowserTabsRemote(bool* aResult) +{ + *aResult = BrowserTabsRemote(); + return NS_OK; +} + NS_IMETHODIMP nsXULAppInfo::EnsureContentProcess() { @@ -4368,6 +4378,17 @@ XRE_GetProcessType() return mozilla::startup::sChildProcessType; } +bool +mozilla::BrowserTabsRemote() +{ + if (!gBrowserTabsRemoteInitialized) { + gBrowserTabsRemote = Preferences::GetBool("browser.tabs.remote", false); + gBrowserTabsRemoteInitialized = true; + } + + return gBrowserTabsRemote; +} + void SetupErrorHandling(const char* progname) { diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index 8640e221a71e..1916ced3277d 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -935,7 +935,7 @@ CheckForBasicBackends(nsTArray& aHints) for (size_t i = 0; i < aHints.Length(); ++i) { if (aHints[i] == LAYERS_BASIC && !Preferences::GetBool("layers.offmainthreadcomposition.force-basic", false) && - !Preferences::GetBool("browser.tabs.remote", false)) { + !BrowserTabsRemote()) { // basic compositor is not stable enough for regular use aHints[i] = LAYERS_NONE; } diff --git a/xpcom/system/nsIXULRuntime.idl b/xpcom/system/nsIXULRuntime.idl index 07466fc070f6..0b8cb5005768 100644 --- a/xpcom/system/nsIXULRuntime.idl +++ b/xpcom/system/nsIXULRuntime.idl @@ -4,6 +4,15 @@ #include "nsISupports.idl" +%{C++ + +namespace mozilla { +// Simple C++ getter for nsIXULRuntime::browserTabsRemote +bool BrowserTabsRemote(); +} + +%} + /** * Provides information about the XUL runtime. * @status UNSTABLE - This interface is not frozen and will probably change in @@ -11,7 +20,7 @@ * stable/frozen, please contact Benjamin Smedberg. */ -[scriptable, uuid(cb0b8eda-4c83-4d0e-a63c-d3b65714bc85)] +[scriptable, uuid(33bd1630-611e-11e3-949a-0800200c9a66)] interface nsIXULRuntime : nsISupports { /** @@ -67,6 +76,12 @@ interface nsIXULRuntime : nsISupports */ readonly attribute unsigned long processType; + /** + * If true, browser tabs may be opened in a different process from the main + * browser UI. + */ + readonly attribute boolean browserTabsRemote; + /** * Signal the apprunner to invalidate caches on the next restart. * This will cause components to be autoregistered and all From 2ca767aa8888d9c4fd1c6bd82b025deebf101386 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Thu, 12 Dec 2013 14:17:37 -0800 Subject: [PATCH 076/459] Bug 929012 - Annotate on crash reports that content processes are turned on (r=bsmedberg) --- toolkit/xre/nsAppRunner.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index d4f9f5cbdb5d..c64b4cc17ba2 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -3874,6 +3874,11 @@ XREMain::XRE_mainRun() mDirProvider.DoStartup(); #ifdef MOZ_CRASHREPORTER + if (BrowserTabsRemote()) { + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"), + NS_LITERAL_CSTRING("1")); + } + nsCString userAgentLocale; if (NS_SUCCEEDED(Preferences::GetCString("general.useragent.locale", &userAgentLocale))) { CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("useragent_locale"), userAgentLocale); From fb15e657d0193d23e0936023a0483344fe15d8d8 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Thu, 12 Dec 2013 14:17:41 -0800 Subject: [PATCH 077/459] Bug 924366 - Animated GIFs shouldn't loop before they have finished decoding (r=seth) --- image/src/FrameAnimator.cpp | 58 ++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/image/src/FrameAnimator.cpp b/image/src/FrameAnimator.cpp index bb1f720ed27d..2d20bb6efbcc 100644 --- a/image/src/FrameAnimator.cpp +++ b/image/src/FrameAnimator.cpp @@ -88,45 +88,45 @@ FrameAnimator::AdvanceFrame(TimeStamp aTime) // If we're done decoding, we know we've got everything we're going to get. // If we aren't, we only display fully-downloaded frames; everything else // gets delayed. - bool needToWait = !mDoneDecoding && - mFrameBlender.RawGetFrame(nextFrameIndex) && - !mFrameBlender.RawGetFrame(nextFrameIndex)->ImageComplete(); + bool canDisplay = mDoneDecoding || + (mFrameBlender.RawGetFrame(nextFrameIndex) && + mFrameBlender.RawGetFrame(nextFrameIndex)->ImageComplete()); - if (needToWait) { + if (!canDisplay) { // Uh oh, the frame we want to show is currently being decoded (partial) // Wait until the next refresh driver tick and try again return ret; - } else { - // If we're done decoding the next frame, go ahead and display it now and - // reinit with the next frame's delay time. - if (mFrameBlender.GetNumFrames() == nextFrameIndex) { - // End of an animation loop... + } - // If we are not looping forever, initialize the loop counter - if (mLoopCounter < 0 && mFrameBlender.GetLoopCount() >= 0) { - mLoopCounter = mFrameBlender.GetLoopCount(); - } + // If we're done decoding the next frame, go ahead and display it now and + // reinit with the next frame's delay time. + if (mFrameBlender.GetNumFrames() == nextFrameIndex) { + // End of an animation loop... - // If animation mode is "loop once", or we're at end of loop counter, it's time to stop animating - if (mAnimationMode == imgIContainer::kLoopOnceAnimMode || mLoopCounter == 0) { - ret.animationFinished = true; - } - - nextFrameIndex = 0; - - if (mLoopCounter > 0) { - mLoopCounter--; - } - - // If we're done, exit early. - if (ret.animationFinished) { - return ret; - } + // If we are not looping forever, initialize the loop counter + if (mLoopCounter < 0 && mFrameBlender.GetLoopCount() >= 0) { + mLoopCounter = mFrameBlender.GetLoopCount(); } - timeout = mFrameBlender.GetTimeoutForFrame(nextFrameIndex); + // If animation mode is "loop once", or we're at end of loop counter, it's time to stop animating + if (mAnimationMode == imgIContainer::kLoopOnceAnimMode || mLoopCounter == 0) { + ret.animationFinished = true; + } + + nextFrameIndex = 0; + + if (mLoopCounter > 0) { + mLoopCounter--; + } + + // If we're done, exit early. + if (ret.animationFinished) { + return ret; + } } + timeout = mFrameBlender.GetTimeoutForFrame(nextFrameIndex); + // Bad data if (timeout < 0) { ret.animationFinished = true; From 57737a44011ec8c4817b354994bd83ed75ceefe7 Mon Sep 17 00:00:00 2001 From: Cykesiopka Date: Thu, 12 Dec 2013 17:36:59 -0500 Subject: [PATCH 078/459] Bug 835150 - Remove fast-package make target and references to MOZ_FAST_PACKAGE. r=mshal --- CLOBBER | 5 +---- client.mk | 4 ++-- mobile/android/build.mk | 3 --- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/CLOBBER b/CLOBBER index 67876f8d1244..91acfa9390b2 100644 --- a/CLOBBER +++ b/CLOBBER @@ -18,7 +18,4 @@ # Modifying this file will now automatically clobber the buildbot machines \o/ # -Bug 934646 needs a clobber -- the icon resources previously copied -into $OBJDIR/mobile/android/base/res will conflict with those in -$BRANDING_DIRECTORY/res. - +Bug 887836 - webidl changes require a Windows clobber. diff --git a/client.mk b/client.mk index 561a294b1187..49c0a2ae82d6 100644 --- a/client.mk +++ b/client.mk @@ -175,7 +175,7 @@ CONFIGURES := $(TOPSRCDIR)/configure CONFIGURES += $(TOPSRCDIR)/js/src/configure # Make targets that are going to be passed to the real build system -OBJDIR_TARGETS = install export libs clean realclean distclean maybe_clobber_profiledbuild upload sdk installer package fast-package package-compare stage-package source-package l10n-check +OBJDIR_TARGETS = install export libs clean realclean distclean maybe_clobber_profiledbuild upload sdk installer package package-compare stage-package source-package l10n-check ####################################################################### # Rules @@ -224,7 +224,7 @@ build_all: build clobber clobber_all: clean # helper target for mobile -build_and_deploy: build fast-package install +build_and_deploy: build package install # Do everything from scratch everything: clean build diff --git a/mobile/android/build.mk b/mobile/android/build.mk index e4069388a349..d983063c3951 100644 --- a/mobile/android/build.mk +++ b/mobile/android/build.mk @@ -10,9 +10,6 @@ installer: package: @$(MAKE) -C mobile/android/installer -fast-package: - @$(MAKE) package MOZ_FAST_PACKAGE=1 - ifeq ($(OS_TARGET),Android) ifneq ($(MOZ_ANDROID_INSTALL_TARGET),) ANDROID_SERIAL = $(MOZ_ANDROID_INSTALL_TARGET) From 98d5e0c51a0f1d3923edc0e4d28ab1ff001f2d0c Mon Sep 17 00:00:00 2001 From: Douglas Crosher Date: Thu, 5 Dec 2013 10:44:40 +1100 Subject: [PATCH 079/459] Bug 906964 - ARM: Revert the reservation of some head-room in the constant pools as it caused or tickled bug 944972. r=mjrosenb --- js/src/jit/arm/Assembler-arm.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index b535a1fe9825..362d7d183470 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1299,17 +1299,12 @@ class Assembler void initWithAllocator() { m_buffer.initWithAllocator(); - // Note that the sizes for the double pools are set to 1020 rather than 1024 to - // work around a rare edge case that would otherwise bail out - which is not - // possible for Asm.js code and causes a compilation failure. See the comment at - // the fail_bail call within IonAssemberBufferWithConstantPools.h: finishPool(). - // Set up the backwards double region - new (&pools_[2]) Pool (1020, 8, 4, 8, 8, m_buffer.LifoAlloc_, true); + new (&pools_[2]) Pool (1024, 8, 4, 8, 8, m_buffer.LifoAlloc_, true); // Set up the backwards 32 bit region new (&pools_[3]) Pool (4096, 4, 4, 8, 4, m_buffer.LifoAlloc_, true, true); // Set up the forwards double region - new (doublePool) Pool (1020, 8, 4, 8, 8, m_buffer.LifoAlloc_, false, false, &pools_[2]); + new (doublePool) Pool (1024, 8, 4, 8, 8, m_buffer.LifoAlloc_, false, false, &pools_[2]); // Set up the forwards 32 bit region new (int32Pool) Pool (4096, 4, 4, 8, 4, m_buffer.LifoAlloc_, false, true, &pools_[3]); for (int i = 0; i < 4; i++) { From 47521f776ec7b3751079afdab6b27784e3b47f98 Mon Sep 17 00:00:00 2001 From: Jan-Ivar Bruaroey Date: Wed, 11 Dec 2013 23:42:12 -0500 Subject: [PATCH 080/459] Bug 908695 - Collect pipelines on main, dispatch to STS for stats. r=jesup --- .../src/mediapipeline/MediaPipeline.cpp | 1 + .../src/peerconnection/PeerConnectionImpl.cpp | 133 ++++++++++++------ .../src/peerconnection/PeerConnectionImpl.h | 26 ++-- .../peerconnection/PeerConnectionMedia.cpp | 67 --------- .../src/peerconnection/PeerConnectionMedia.h | 11 +- .../signaling/test/signaling_unittests.cpp | 4 +- 6 files changed, 119 insertions(+), 123 deletions(-) diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index 400a77519e8c..0cf31c4da5af 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -50,6 +50,7 @@ namespace mozilla { static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp"; MediaPipeline::~MediaPipeline() { + ASSERT_ON_THREAD(main_thread_); MOZ_ASSERT(!stream_); // Check that we have shut down already. MOZ_MTLOG(ML_INFO, "Destroying MediaPipeline: " << description_); } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index a360d1b43aa7..fac55f2b0617 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -1191,24 +1191,68 @@ PeerConnectionImpl::GetTimeSinceEpoch(DOMHighResTimeStamp *result) { *result = perf->Now() + perf->Timing()->NavigationStart(); return NS_OK; } + +class RTCStatsReportInternalConstruct : public RTCStatsReportInternal { +public: + RTCStatsReportInternalConstruct(const nsString &pcid, DOMHighResTimeStamp now) { + mPcid = pcid; + mInboundRTPStreamStats.Construct(); + mOutboundRTPStreamStats.Construct(); + mMediaStreamTrackStats.Construct(); + mMediaStreamStats.Construct(); + mTransportStats.Construct(); + mIceComponentStats.Construct(); + mIceCandidatePairStats.Construct(); + mIceCandidateStats.Construct(); + mCodecStats.Construct(); + } +}; + +// Specialized helper - push map[key] if specified or all map values onto array + +static void +PushBackSelect(std::vector>& aDst, + const std::map> & aSrc, + TrackID aKey = 0) { + auto begin = aKey ? aSrc.find(aKey) : aSrc.begin(), it = begin; + for (auto end = (aKey && begin != aSrc.end())? ++begin : aSrc.end(); + it != end; ++it) { + aDst.push_back(it->second); + } +} #endif NS_IMETHODIMP -PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector, - bool internalStats) { +PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector, bool internalStats) { PC_AUTO_ENTER_API_CALL(true); #ifdef MOZILLA_INTERNAL_API - uint32_t track = aSelector ? aSelector->GetTrackID() : 0; + MOZ_ASSERT(mMedia); + + // Gather up pipelines from mMedia and dispatch them to STS for inspection + + nsAutoPtr>> pipelines( + new std::vector>()); + TrackID trackId = aSelector ? aSelector->GetTrackID() : 0; + + for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) { + PushBackSelect(*pipelines, mMedia->GetLocalStream(i)->GetPipelines(), trackId); + } + for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) { + PushBackSelect(*pipelines, mMedia->GetRemoteStream(i)->GetPipelines(), trackId); + } + DOMHighResTimeStamp now; nsresult rv = GetTimeSinceEpoch(&now); NS_ENSURE_SUCCESS(rv, rv); + nsRefPtr pc(this); RUN_ON_THREAD(mSTSThread, WrapRunnable(pc, &PeerConnectionImpl::GetStats_s, - track, + trackId, internalStats, + pipelines, now), NS_DISPATCH_NORMAL); #endif @@ -1798,44 +1842,51 @@ PeerConnectionImpl::IceGatheringStateChange_m(PCImplIceGatheringState aState) } #ifdef MOZILLA_INTERNAL_API -class RTCStatsReportInternalConstruct : public RTCStatsReportInternal { -public: - RTCStatsReportInternalConstruct(const nsString &pcid, DOMHighResTimeStamp now) { - mPcid = pcid; - mInboundRTPStreamStats.Construct(); - mOutboundRTPStreamStats.Construct(); - mMediaStreamTrackStats.Construct(); - mMediaStreamStats.Construct(); - mTransportStats.Construct(); - mIceComponentStats.Construct(); - mIceCandidatePairStats.Construct(); - mIceCandidateStats.Construct(); - mCodecStats.Construct(); - } -}; - -nsresult PeerConnectionImpl::GetStatsImpl_s( +nsresult +PeerConnectionImpl::GetStatsImpl_s( TrackID trackId, bool internalStats, + nsAutoPtr>> pipelines, DOMHighResTimeStamp now, RTCStatsReportInternal *report) { + + // Gather stats from pipelines provided (can't touch mMedia + stream on STS) + + for (auto it = pipelines->begin(); it != pipelines->end(); ++it) { + const MediaPipeline& mp = **it; + nsString idstr = (mp.Conduit()->type() == MediaSessionConduit::AUDIO) ? + NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_"); + idstr.AppendInt(mp.trackid()); + + switch (mp.direction()) { + case MediaPipeline::TRANSMIT: { + RTCOutboundRTPStreamStats s; + s.mTimestamp.Construct(now); + s.mId.Construct(NS_LITERAL_STRING("outbound_rtp_") + idstr); + s.mType.Construct(RTCStatsType::Outboundrtp); + // TODO: Get SSRC + // int channel = mp.Conduit()->GetChannel(); + s.mSsrc.Construct(NS_LITERAL_STRING("123457")); + s.mPacketsSent.Construct(mp.rtp_packets_sent()); + s.mBytesSent.Construct(mp.rtp_bytes_sent()); + report->mOutboundRTPStreamStats.Value().AppendElement(s); + break; + } + case MediaPipeline::RECEIVE: { + RTCInboundRTPStreamStats s; + s.mTimestamp.Construct(now); + s.mId.Construct(NS_LITERAL_STRING("inbound_rtp_") + idstr); + s.mType.Construct(RTCStatsType::Inboundrtp); + s.mSsrc.Construct(NS_LITERAL_STRING("123457")); + s.mPacketsReceived.Construct(mp.rtp_packets_received()); + s.mBytesReceived.Construct(mp.rtp_bytes_received()); + report->mInboundRTPStreamStats.Value().AppendElement(s); + break; + } + } + } + if (mMedia) { - nsresult rv; - - // Gather stats from media pipeline (can't touch stream itself on STS) - - for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) { - rv = mMedia->GetLocalStream(i)->GetPipelineStats(now, trackId, - &report->mInboundRTPStreamStats.Value(), - &report->mOutboundRTPStreamStats.Value()); - NS_ENSURE_SUCCESS(rv, rv); - } - for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) { - rv = mMedia->GetRemoteStream(i)->GetPipelineStats(now, trackId, - &report->mInboundRTPStreamStats.Value(), - &report->mOutboundRTPStreamStats.Value()); - NS_ENSURE_SUCCESS(rv, rv); - } // Gather stats from ICE @@ -1902,27 +1953,29 @@ nsresult PeerConnectionImpl::GetStatsImpl_s( void PeerConnectionImpl::GetStats_s( TrackID trackId, bool internalStats, + nsAutoPtr>> pipelines, DOMHighResTimeStamp now) { nsAutoPtr report(new RTCStatsReportInternalConstruct( NS_ConvertASCIItoUTF16(mHandle.c_str()), now)); - nsresult rv = report ? GetStatsImpl_s(trackId, internalStats, now, report) + nsresult rv = report ? GetStatsImpl_s(trackId, internalStats, pipelines, now, + report) : NS_ERROR_UNEXPECTED; nsRefPtr pc(this); RUN_ON_THREAD(mThread, WrapRunnable(pc, &PeerConnectionImpl::OnStatsReport_m, - trackId, rv, + pipelines, // return for release on main thread report), NS_DISPATCH_NORMAL); } void PeerConnectionImpl::OnStatsReport_m( - TrackID trackId, nsresult result, + nsAutoPtr>> pipelines, //returned for release nsAutoPtr report) { nsRefPtr pco = do_QueryObjectReferent(mPCObserver); if (pco) { diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index 2b7186cb4996..334598f130db 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -55,6 +55,7 @@ class NrIceCtx; class NrIceMediaStream; class NrIceStunServer; class NrIceTurnServer; +class MediaPipeline; #ifdef USE_FAKE_MEDIA_STREAMS typedef Fake_DOMMediaStream DOMMediaStream; @@ -529,19 +530,24 @@ private: #ifdef MOZILLA_INTERNAL_API // Fills in an RTCStatsReportInternal. Must be run on STS. - void GetStats_s(mozilla::TrackID trackId, - bool internalStats, - DOMHighResTimeStamp now); + void GetStats_s( + mozilla::TrackID trackId, + bool internalStats, + nsAutoPtr>> pipelines, + DOMHighResTimeStamp now); - nsresult GetStatsImpl_s(mozilla::TrackID trackId, - bool internalStats, - DOMHighResTimeStamp now, - mozilla::dom::RTCStatsReportInternal *report); + nsresult GetStatsImpl_s( + mozilla::TrackID trackId, + bool internalStats, + nsAutoPtr>> pipelines, + DOMHighResTimeStamp now, + mozilla::dom::RTCStatsReportInternal *report); // Sends an RTCStatsReport to JS. Must run on main thread. - void OnStatsReport_m(mozilla::TrackID trackId, - nsresult result, - nsAutoPtr report); + void OnStatsReport_m( + nsresult result, + nsAutoPtr>> pipelines, + nsAutoPtr report); // Fetches logs matching pattern from RLogRingBuffer. Must be run on STS. void GetLogging_s(const std::string& pattern); diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index a3702677eb4a..0e9bc9b06188 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -452,73 +452,6 @@ PeerConnectionMedia::IceStreamReady(NrIceMediaStream *aStream) CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str()); } -// This method exists for the unittests. -// It allows visibility into the pipelines and flows. -// It returns nullptr if no pipeline exists for this track number. -mozilla::RefPtr -SourceStreamInfo::GetPipeline(int aTrack) { - std::map >::iterator it = - mPipelines.find(aTrack); - - if (it == mPipelines.end()) { - return nullptr; - } - - return it->second; -} - -// This methods gathers statistics for the getStats API. -// aTrack == 0 means gather stats for all tracks. - -nsresult -SourceStreamInfo::GetPipelineStats(DOMHighResTimeStamp now, int aTrack, - Sequence *inbound, - Sequence *outbound) -{ -#ifdef MOZILLA_INTERNAL_API - ASSERT_ON_THREAD(mParent->GetSTSThread()); - // walk through all the MediaPipelines and gather stats - for (std::map >::iterator it = mPipelines.begin(); - it != mPipelines.end(); - ++it) { - if (!aTrack || aTrack == it->first) { - const MediaPipeline &mp = *it->second; - nsString idstr = (mp.Conduit()->type() == MediaSessionConduit::AUDIO) ? - NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_"); - idstr.AppendInt(mp.trackid()); - - switch (mp.direction()) { - case MediaPipeline::TRANSMIT: { - RTCOutboundRTPStreamStats s; - s.mTimestamp.Construct(now); - s.mId.Construct(NS_LITERAL_STRING("outbound_rtp_") + idstr); - s.mType.Construct(RTCStatsType::Outboundrtp); - // TODO: Get SSRC - // int channel = mp.Conduit()->GetChannel(); - s.mSsrc.Construct(NS_LITERAL_STRING("123457")); - s.mPacketsSent.Construct(mp.rtp_packets_sent()); - s.mBytesSent.Construct(mp.rtp_bytes_sent()); - outbound->AppendElement(s); - break; - } - case MediaPipeline::RECEIVE: { - RTCInboundRTPStreamStats s; - s.mTimestamp.Construct(now); - s.mId.Construct(NS_LITERAL_STRING("inbound_rtp_") + idstr); - s.mType.Construct(RTCStatsType::Inboundrtp); - s.mSsrc.Construct(NS_LITERAL_STRING("123457")); - s.mPacketsReceived.Construct(mp.rtp_packets_received()); - s.mBytesReceived.Construct(mp.rtp_bytes_received()); - inbound->AppendElement(s); - break; - } - } - } - } -#endif - return NS_OK; -} - void LocalSourceStreamInfo::StorePipeline(int aTrack, mozilla::RefPtr aPipeline) diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h index ac73618d00e6..0b95de101f83 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -184,12 +184,13 @@ public: MOZ_ASSERT(mMediaStream); } - mozilla::RefPtr GetPipeline(int aTrack); - nsresult GetPipelineStats(DOMHighResTimeStamp now, int aTrack, - mozilla::dom::Sequence *inbound, - mozilla::dom::Sequence *outbound); + // This method exists for stats and the unittests. + // It allows visibility into the pipelines and flows. + const std::map>& + GetPipelines() const { return mPipelines; } + protected: - std::map > mPipelines; + std::map> mPipelines; nsRefPtr mMediaStream; PeerConnectionMedia *mParent; }; diff --git a/media/webrtc/signaling/test/signaling_unittests.cpp b/media/webrtc/signaling/test/signaling_unittests.cpp index 53f3cd9acd7c..52aee19422e2 100644 --- a/media/webrtc/signaling/test/signaling_unittests.cpp +++ b/media/webrtc/signaling/test/signaling_unittests.cpp @@ -1124,7 +1124,9 @@ void CreateAnswer(sipcc::MediaConstraints& constraints, std::string offer, return nullptr; } - return streamInfo->GetPipeline(track); + const auto &pipelines = streamInfo->GetPipelines(); + auto it = pipelines.find(track); + return (it == pipelines.end())? nullptr : it->second; } void CheckMediaPipeline(int stream, int track, uint32_t flags, From cf1bd82337a2119145a5c36dea636d9d614a0a5c Mon Sep 17 00:00:00 2001 From: Suhas Nandakumar Date: Mon, 11 Nov 2013 02:39:54 -0800 Subject: [PATCH 081/459] Bug 934667 - Add preferences to enable loopback devices. r=jesup --- dom/media/MediaManager.cpp | 58 ++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index fcbe21e34047..426fa77e89c8 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -662,25 +662,36 @@ template static SourceSet * GetSources(MediaEngine *engine, const MediaTrackConstraintsInternal &aConstraints, - void (MediaEngine::* aEnumerate)(nsTArray >*)) + void (MediaEngine::* aEnumerate)(nsTArray >*), + char* media_device_name = nullptr) { const SourceType * const type = nullptr; - + nsString deviceName; // First collect sources SourceSet candidateSet; { nsTArray > sources; (engine->*aEnumerate)(&sources); - /** * We're allowing multiple tabs to access the same camera for parity * with Chrome. See bug 811757 for some of the issues surrounding * this decision. To disallow, we'd filter by IsAvailable() as we used * to. */ - for (uint32_t len = sources.Length(), i = 0; i < len; i++) { - candidateSet.AppendElement(new MediaDevice(sources[i])); +#ifdef DEBUG + sources[i]->GetName(deviceName); + if (media_device_name && strlen(media_device_name) > 0) { + if (deviceName.EqualsASCII(media_device_name)) { + candidateSet.AppendElement(new MediaDevice(sources[i])); + break; + } + } else { +#endif + candidateSet.AppendElement(new MediaDevice(sources[i])); +#ifdef DEBUG + } +#endif } } @@ -1003,24 +1014,29 @@ public: const MediaStreamConstraintsInternal& aConstraints, already_AddRefed aSuccess, already_AddRefed aError, - uint64_t aWindowId) + uint64_t aWindowId, char* aAudioLoopbackDev, char* aVideoLoopbackDev) : mConstraints(aConstraints) , mSuccess(aSuccess) , mError(aError) , mManager(MediaManager::GetInstance()) - , mWindowId(aWindowId) {} + , mWindowId(aWindowId) + , mLoopbackAudioDevice(aAudioLoopbackDev) + , mLoopbackVideoDevice(aVideoLoopbackDev) {} NS_IMETHOD Run() { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); + MediaEngine *backend = mManager->GetBackend(mWindowId); ScopedDeletePtr final (GetSources(backend, mConstraints.mVideom, - &MediaEngine::EnumerateVideoDevices)); + &MediaEngine::EnumerateVideoDevices, + mLoopbackVideoDevice)); { ScopedDeletePtr s (GetSources(backend, mConstraints.mAudiom, - &MediaEngine::EnumerateAudioDevices)); + &MediaEngine::EnumerateAudioDevices, + mLoopbackAudioDevice)); final->MoveElementsFrom(*s); } NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mSuccess, mError, @@ -1034,6 +1050,11 @@ private: already_AddRefed mError; nsRefPtr mManager; uint64_t mWindowId; + // Audio & Video loopback devices to be used based on + // the preference settings. This is currently used for + // automated media tests only. + char* mLoopbackAudioDevice; + char* mLoopbackVideoDevice; }; MediaManager::MediaManager() @@ -1392,13 +1413,28 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow, nsCOMPtr onSuccess(aOnSuccess); nsCOMPtr onError(aOnError); + char* loopbackAudioDevice = nullptr; + char* loopbackVideoDevice = nullptr; + nsresult rv; +#ifdef DEBUG + // Check if the preference for using loopback devices is enabled. + nsCOMPtr prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr branch = do_QueryInterface(prefs); + if (branch) { + branch->GetCharPref("media.audio_loopback_dev", &loopbackAudioDevice); + branch->GetCharPref("media.video_loopback_dev", &loopbackVideoDevice); + } + } +#endif nsCOMPtr gUMDRunnable = new GetUserMediaDevicesRunnable( - aConstraints, onSuccess.forget(), onError.forget(), aWindow->WindowID() + aConstraints, onSuccess.forget(), onError.forget(), aWindow->WindowID(), + loopbackAudioDevice, loopbackVideoDevice ); nsCOMPtr deviceThread; - nsresult rv = NS_NewThread(getter_AddRefs(deviceThread)); + rv = NS_NewThread(getter_AddRefs(deviceThread)); NS_ENSURE_SUCCESS(rv, rv); From ae39aa6b052692e1c4a711781341f83cec46af93 Mon Sep 17 00:00:00 2001 From: Mike Connor Date: Fri, 13 Dec 2013 13:38:29 -0500 Subject: [PATCH 082/459] bug 936198 - update small Yahoo! icon, including on MetroFx, r=gavin --HG-- extra : rebase_source : 770893767db1836b13e5cf4bd0904b402ce0d15b --- browser/locales/en-US/searchplugins/yahoo.xml | 2 +- browser/locales/en-US/searchplugins/yahoometrofx.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/locales/en-US/searchplugins/yahoo.xml b/browser/locales/en-US/searchplugins/yahoo.xml index 353938f6de62..d80b70e96c00 100644 --- a/browser/locales/en-US/searchplugins/yahoo.xml +++ b/browser/locales/en-US/searchplugins/yahoo.xml @@ -6,7 +6,7 @@ Yahoo Yahoo Search UTF-8 -data:image/x-icon;base64,AAABAAIAEBAAAAAAAAANAQAAJgAAACAgAAAAAAAA/AMAADMBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAADUSURBVDjLY2AYPmBPzZn/65L2/W8SmoeClwXt+g+SQ1ZbxTTrPwzDNaNrRMYghciGoBgA03xv37P/MABiT9Jb9x9ZIbIhMP48p03/GUBOhNkE8gIIgBSCFPz++QeMX11697+MqQtsKLIBYAPRnXtu3i2wJpDBIBqkCKQ5iykPTIMMgLkOqwEgDLIR5hKQRhiGBRrI6fAARPYCDIM0ggCyZhCGeQHkVRibAeZfGAYFILLfYa5AjwmMNIBuG7rT0aMRHAPYElKf0jK4RlwJCaQG7oWhDwAW40nUCGVbdgAAAABJRU5ErkJggolQTkcNChoKAAAADUlIRFIAAAAgAAAAIAgGAAAAc3p69AAAA8NJREFUWMPtVmtsDFEU3j+IR7s7d4SISEMi6pUQ/CIRjyAVhBDxChJtvRptbXdn1iMtjbfW7s7caQmNRxVRVLWVVCtCEQklS6mttI3Eo+GPCvHKce5sdzqdndn10489ycnMnLn33O9+59xzrs0Wl7jE5X+X7Q4pyc3R+QJPRZHQPA+Rc5mKnJwuEt8Yp+1w/1g+xAR/sofQXzj/ucjTUoGXdwicvEKwKzOtJzmUNTih1sMrEE1ForQzYC5eHm3lS3AoE03nE9oWOZijGxHl+1gLW4A5n2Ur6Gv06UqgoywAnOi5a0LLQ44oCKhuImvKvpk9Jgiefs1J9I7ssSk7HWEC9qmbKHO1QYjmUrcTBXKIBE7ih+3Ep6qbger6J8Zm4rdzgDIo7Ntp9w83GVPksh1MCCEk8nJmxCfkJ5VAU9Ub+Nz2BdofvYNgfTs01bTCi+pWeFAcUEFkcV7I5I5pyr4ZSEHHEDLxKAwgiy8YGsmUvCREOyelsOwMU7978HE4tagCrm67A3opT70NxyZdhG1cIRRNvwJV7ga4nF4H5RvroNrTALV7HsLR8aXqJsKLuDl5msawAUCGzdeni3rlSk9qqLqrVMdBKBhbpgEIXAui7QCkoZ6YXQHPLge1f50d3yBwvQUOJZ/FUEk6X7LXFIA++/GjySyOLPE2OQ5BSUqlttBN4T6sTdwL6+z7oFpoUG2VOXchG0OQ4Tiq5okhIZ+YAUC7pM/85+bZHErENPt+qMy6p4E4vaAGGsua1XcWCgbSieNE8xMRNAMg8NLibgZ4+soqm1lSZWNybXUcgUBFiwbi+5cfcGD4GdiMdhcyJVqeBvrMDACrrt0M8LTS+kwruIAE6Rh374RLGoALq2phvT1f3bkQ/TgWGQGg7a2h5NKFuNBLKycsDBswDIW6hCyZdwNWJ+5R2YlaE+zSrAgGCK2OrPs8XWtBIewaeBz2J5+GKqE7D+56G6E45SocHn3OsjqiPaBfgxUm1tSMVTJqKBjFvikX4WPTJ+ho/gzNt9rhNRamV7Vt0Hb/HZSurFFPixmAbCIP03qB3T/ZwynjevQHrIJptrRepr1ArzsHFkPekJNqrMNlOSdK4iHFfzCs0w2bC+obFmvHrPRj0ZoTyQShmWah0Dcny8ZE6A0xQeL1/pbZcnsbfBX+00UELxwuHPwQEf+M0Xha8YJykl1OzPxkEF8iMlCPx72zqzou/ScArFZ7HPIMVk5x4mN00oG0fwgp/Yi2Fnyewf+pmf0Kh0TfjH+qh6NbRI76jLkQl7jE5S+qIPrIdKZBVAAAAABJRU5ErkJggg== +data:image/x-icon;base64,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 diff --git a/browser/locales/en-US/searchplugins/yahoometrofx.xml b/browser/locales/en-US/searchplugins/yahoometrofx.xml index 24eb8039bf56..f0d6fac71352 100644 --- a/browser/locales/en-US/searchplugins/yahoometrofx.xml +++ b/browser/locales/en-US/searchplugins/yahoometrofx.xml @@ -6,7 +6,7 @@ Yahoo Yahoo Search UTF-8 -data:image/x-icon;base64,AAABAAIAEBAAAAAAAAANAQAAJgAAACAgAAAAAAAA/AMAADMBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAADUSURBVDjLY2AYPmBPzZn/65L2/W8SmoeClwXt+g+SQ1ZbxTTrPwzDNaNrRMYghciGoBgA03xv37P/MABiT9Jb9x9ZIbIhMP48p03/GUBOhNkE8gIIgBSCFPz++QeMX11697+MqQtsKLIBYAPRnXtu3i2wJpDBIBqkCKQ5iykPTIMMgLkOqwEgDLIR5hKQRhiGBRrI6fAARPYCDIM0ggCyZhCGeQHkVRibAeZfGAYFILLfYa5AjwmMNIBuG7rT0aMRHAPYElKf0jK4RlwJCaQG7oWhDwAW40nUCGVbdgAAAABJRU5ErkJggolQTkcNChoKAAAADUlIRFIAAAAgAAAAIAgGAAAAc3p69AAAA8NJREFUWMPtVmtsDFEU3j+IR7s7d4SISEMi6pUQ/CIRjyAVhBDxChJtvRptbXdn1iMtjbfW7s7caQmNRxVRVLWVVCtCEQklS6mttI3Eo+GPCvHKce5sdzqdndn10489ycnMnLn33O9+59xzrs0Wl7jE5X+X7Q4pyc3R+QJPRZHQPA+Rc5mKnJwuEt8Yp+1w/1g+xAR/sofQXzj/ucjTUoGXdwicvEKwKzOtJzmUNTih1sMrEE1ForQzYC5eHm3lS3AoE03nE9oWOZijGxHl+1gLW4A5n2Ur6Gv06UqgoywAnOi5a0LLQ44oCKhuImvKvpk9Jgiefs1J9I7ssSk7HWEC9qmbKHO1QYjmUrcTBXKIBE7ih+3Ep6qbger6J8Zm4rdzgDIo7Ntp9w83GVPksh1MCCEk8nJmxCfkJ5VAU9Ub+Nz2BdofvYNgfTs01bTCi+pWeFAcUEFkcV7I5I5pyr4ZSEHHEDLxKAwgiy8YGsmUvCREOyelsOwMU7978HE4tagCrm67A3opT70NxyZdhG1cIRRNvwJV7ga4nF4H5RvroNrTALV7HsLR8aXqJsKLuDl5msawAUCGzdeni3rlSk9qqLqrVMdBKBhbpgEIXAui7QCkoZ6YXQHPLge1f50d3yBwvQUOJZ/FUEk6X7LXFIA++/GjySyOLPE2OQ5BSUqlttBN4T6sTdwL6+z7oFpoUG2VOXchG0OQ4Tiq5okhIZ+YAUC7pM/85+bZHErENPt+qMy6p4E4vaAGGsua1XcWCgbSieNE8xMRNAMg8NLibgZ4+soqm1lSZWNybXUcgUBFiwbi+5cfcGD4GdiMdhcyJVqeBvrMDACrrt0M8LTS+kwruIAE6Rh374RLGoALq2phvT1f3bkQ/TgWGQGg7a2h5NKFuNBLKycsDBswDIW6hCyZdwNWJ+5R2YlaE+zSrAgGCK2OrPs8XWtBIewaeBz2J5+GKqE7D+56G6E45SocHn3OsjqiPaBfgxUm1tSMVTJqKBjFvikX4WPTJ+ho/gzNt9rhNRamV7Vt0Hb/HZSurFFPixmAbCIP03qB3T/ZwynjevQHrIJptrRepr1ArzsHFkPekJNqrMNlOSdK4iHFfzCs0w2bC+obFmvHrPRj0ZoTyQShmWah0Dcny8ZE6A0xQeL1/pbZcnsbfBX+00UELxwuHPwQEf+M0Xha8YJykl1OzPxkEF8iMlCPx72zqzou/ScArFZ7HPIMVk5x4mN00oG0fwgp/Yi2Fnyewf+pmf0Kh0TfjH+qh6NbRI76jLkQl7jE5S+qIPrIdKZBVAAAAABJRU5ErkJggg== +data:image/x-icon;base64,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 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEoAAABKCAIAAACTslUmAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw%2FeHBhY2tldCBiZWdpbj0i77u%2FIiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8%2BIDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDOUM2QjI5NUY0MkExMUUyQjUzMEMyNEUyNjY3NEQyNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDOUM2QjI5NkY0MkExMUUyQjUzMEMyNEUyNjY3NEQyNyI%2BIDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM5QzZCMjkzRjQyQTExRTJCNTMwQzI0RTI2Njc0RDI3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkM5QzZCMjk0RjQyQTExRTJCNTMwQzI0RTI2Njc0RDI3Ii8%2BIDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY%2BIDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8%2BaVyByAAABG1JREFUeNrsmmtMU2cYgFtKS%2B%2F0gqVQadVUJIgUjCaMLXMbg%2BEtKgiCZMmybFli%2FEHiH7c%2F27JLMpct%2FFiyiyYTIxiic8FFjMapW4MDWWSU4gUUSnX0tPTGSq%2FQdu8oq66nHkhT0868X86P97u8b8%2BT73sv30npR4rbaM9uy6A90w3xEA%2FxEA%2FxEA%2FxEA%2FxEA%2FxEA%2FxEA%2FxEA%2FxEA%2FxEA%2FxEA%2FxEA%2FxSC3zqVpn8Vjql9eoKpWywhyOhMPJZodCYZ%2FT57a6iRGLoXdyXGsIzYfi6j5%2FsKKottBpdDomnQ7jDAgTvZPpggdgFe9sLm%2FWsLjM2CkuU5gvyCuVlzeXum2e%2FqMDg526UDAWksFkSFaL4Yl0HUbnsa3taYGXWyzb1bY9WyFcciVPyn3l8JbiHUXdref%2FMrkoVpqGiLTwvZWbFE3te5fDFm3yktyWjkaxUkSFN5wGeNkrhXVf7SQfyCUbP5df%2F80uCkWTzpz60PLaB1VZgqw579zZA%2Bf87sA%2FLpSZEVwIHlk8Vvl%2BTWG1%2Bkm6YpUIwsnVI1ry1Lw%2FaLkznWI8RVme6jklCEw2s%2BbDKtt9%2B2DnkOG6EUaq3t2iadzAYDFAHv9lYvqeLarFFrJ5OVwIsCCXNWn6vhvwOn0xloEtOBdMMV7R9nWLEp0GjgRPwSbFyeYu%2B4Tjxvc3S%2BrWR%2FBc5lndab3z4QwtvBBdVvBebK1cfJssxtpX1bozetLJJBJ7pWT6Xr5GHjMCB3XnF9uAykW4tG29kUHYxrcvvKGqUEJ62H%2By4cC1t0p2Fz8yUionW57SmVKPJ8gVkAdl63JeOvQCCIOndFP%2FBvern2shs7d07FOU58fGGDmfbIQYNqcejyvmxB3f2FJWsFkRDoUvvn85GAj%2B%2FMk1Y5%2Bx6fhevowXx4go1ojH7nU%2BmEk9HgTM%2BBN02taPa5jsTOuYrb2%2Bc7L%2FQcOxOo6IvUwjpkRPZpLxKMoOyIfgciDYxu21H1VzJZzlGyH0lrTA%2B%2FMPE3XijgiigmyKZVOk4mtqKD12b%2FTSGMUsnR6tlZ%2F4o1BY37tyPyJ7HV7I5uCxpkTjSpLznuE3I6E3QwEZM%2B53%2Bd1Wj%2FnWYtlhvPFQKBfA%2FUiYFxtpR7pvQ1aMyAPHb%2F5%2BYpAv44N6wq%2FEqF5Rm0RCYpjYsGd9BuM%2F%2B9P15tlfv%2BydHrVGuncujA6d1kOxtqpS%2BfgyuBx1t%2FZEQgs9gw4Nti4wG0ij66zlrrXnvUs7PqulM%2BjRwYZvd4fDYUjxFIqA8ePBnzx2T6Sbo5a%2B3tUElQ3cZc8fvpgwZPLve7A5%2FtnAtk9rouGRxWdRqwDDuUM9ltuPimaBnA%2B1jnSNRKISz3vn0%2Btby4TW0F7fMfzDCPkOHrtpnrm%2BowMnGk89zrZQAC2GWShuljRCFc%2Be6p%2BNBXJBYY16daVSqpZGAwl4FJy66btWuDqMXRn3zfjiKio25kFRDh54%2Fev%2BNMXDD4GIh3iIh3iIh3iIh3iIh3iIh3iIh3iIh3iI939pfwswANago8DqAlG2AAAAAElFTkSuQmCC From 640e4550f05c5178d3ad1958f83425d1e1bedf39 Mon Sep 17 00:00:00 2001 From: Mike Connor Date: Fri, 13 Dec 2013 13:42:30 -0500 Subject: [PATCH 083/459] bug 947275 - updated Bing branding for desktop and Metro, r=gavin --HG-- extra : rebase_source : 375fe6e5cc5c7cc13326a35c1572c69dbec58bd1 --- browser/locales/en-US/searchplugins/bing.xml | 2 +- browser/locales/en-US/searchplugins/bingmetrofx.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/browser/locales/en-US/searchplugins/bing.xml b/browser/locales/en-US/searchplugins/bing.xml index ef02aff5a785..0705329654f9 100644 --- a/browser/locales/en-US/searchplugins/bing.xml +++ b/browser/locales/en-US/searchplugins/bing.xml @@ -6,7 +6,7 @@ Bing Bing. Search by Microsoft. UTF-8 - data:image/x-icon;base64,AAABAAIAEBAAAAAAAAD/AAAAJgAAACAgAAAAAAAA+AMAACUBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAADGSURBVDjLY/i/TPQ/JZiBOgZsd/z/f08AhCbLgJdH/4MBiKaaAWtUIK4i4DJUA95d/v//xsz//788+o8VPN4GMRCnATAAMuByF8IFx3NR1dxbjscAkCTI+dicDDIIBkAuxWoALs0wDLIABH5+QDIA5DeY0wmFPMhrMAA34Gw1QhAtkFAwyHWwAIZahkiJoBiAOQ1kILpmkMHIaqBRy4BiOihgYACkCBQ2IIwcrSBDkNIFZl7YaARxAShcYAaAMCxaaZOZKMAAOzkOfhyf540AAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAIAAAACAIBgAAAHN6evQAAAO/SURBVFjDxZRJTxRBFMfrJiC7enKJ28nEGL+AXo2J+gFcrkr0C4gLKjCAoILKIgKyjSZG40FFMephHERcLm4XVzTRi7I4DHRPL8/3qqq7q6d7ZIgRO/mFzlT1+/3fq6ZZ8lzB9smq3JGxQwtgPhmvyHk1XpO3kZF8/EgOjB2eX8hJbkY3/4WjOTBRkQuMbuaVCsHEsVwOoxTzhpROHs8TnMjDAMe8hX/KcQFJOZVIFQaYPEE//mMqBb9QyKlGInmQiFCASpkG0WPVoF7m5xio63OmSvCreqEgIkjUILVEPjCeRhIWQF2fCwmCZI5QSqeIunyYOilg7iZEf5QWYDQG6nrW1OQLaj08aQFM1RdAskHApmhRkopHAgHU9ayp84RJop6Q0lMFME2cRs4UYoA674HQAHIt2bgUtNt7+R76PYzUUAS0gb2QbF8npA35rjTpSKV4urEQZhopQL0YCUEFfAG+xEC7s4//netFz+iDZZA8XSiEUsrFTUUwc1bAnHRE6nEk6+K012U4AtbX8JD25Cjo17e6wplzRaAR55FmCnAG5RIqlOnihQb3wXTLMnf/tAp11roc9HtlfG/6lbpf5ko5LcWgI2xGngVhPKkJ7/hNP2hYXN3LaZLjVEZKaO0rwHwbDdQx7uzkUr2VKIFUWwkGOItFJMZIMID54SaoewRFvpH6xup2WQzWx1v+YtoE6G1CnLqAtGMA9yHEfBoMoPesB3WPdr5Y0OyhN4txujgdRjcEG7q3i4uNi6UcphYyn9YGHlDXOS0enkzSVsw71J0OkfTLelEHRkepoBMDqMnN+MHgBMJEUpZSueBJeYe8y5AAT8rBRDGniwLIxDx12MiGyr11pTNV5iLHysEOreHyYL2rG1G8CMxLApZe0PqU9uLoE2Bc2+TvTOlQFTpQd9aNzfxZ37/y6G0utYhuAfN1QHSvBPvHy0AI6kYIJYqQztJwxkrykfKg/OcrsC6vdsVWj4D5Cjn0rgL7Wzz4QUiMgv26FayBbWD2r+JnyOnHwgPbwX7TyvcEPmLf42BdWe1KrZ7FYPUKmJM+DCu2P7Rg1lfiC9jxA5641xPbRB8FwBeCMDNB5/VgN9jvLmcXhqTvr4D9cI9P6Ir7/DDnbcyEe2YOeI72XRz3YDo7cMxrgl2GSDn9AmZiUZWAsOcPhHWYSahIVRh/IWYjrKvZZBmlSwTRJQAISxdk7CobWYYuXXHUA5wARlfJkD1XyawyD1Bk6Rhdpc+Z1lG4Fm+G/1bkEhX8SczlnaXP8Juz5TddEmZvDz4eOQAAAABJRU5ErkJggg== + data:image/x-icon;base64,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 diff --git a/browser/locales/en-US/searchplugins/bingmetrofx.xml b/browser/locales/en-US/searchplugins/bingmetrofx.xml index d5f7eddee839..a29b4a7ca820 100644 --- a/browser/locales/en-US/searchplugins/bingmetrofx.xml +++ b/browser/locales/en-US/searchplugins/bingmetrofx.xml @@ -6,8 +6,8 @@ Bing Bing. Search by Microsoft. UTF-8 - data:image/x-icon;base64,AAABAAIAEBAAAAAAAAD/AAAAJgAAACAgAAAAAAAA+AMAACUBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAADGSURBVDjLY/i/TPQ/JZiBOgZsd/z/f08AhCbLgJdH/4MBiKaaAWtUIK4i4DJUA95d/v//xsz//788+o8VPN4GMRCnATAAMuByF8IFx3NR1dxbjscAkCTI+dicDDIIBkAuxWoALs0wDLIABH5+QDIA5DeY0wmFPMhrMAA34Gw1QhAtkFAwyHWwAIZahkiJoBiAOQ1kILpmkMHIaqBRy4BiOihgYACkCBQ2IIwcrSBDkNIFZl7YaARxAShcYAaAMCxaaZOZKMAAOzkOfhyf540AAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAIAAAACAIBgAAAHN6evQAAAO/SURBVFjDxZRJTxRBFMfrJiC7enKJ28nEGL+AXo2J+gFcrkr0C4gLKjCAoILKIgKyjSZG40FFMephHERcLm4XVzTRi7I4DHRPL8/3qqq7q6d7ZIgRO/mFzlT1+/3fq6ZZ8lzB9smq3JGxQwtgPhmvyHk1XpO3kZF8/EgOjB2eX8hJbkY3/4WjOTBRkQuMbuaVCsHEsVwOoxTzhpROHs8TnMjDAMe8hX/KcQFJOZVIFQaYPEE//mMqBb9QyKlGInmQiFCASpkG0WPVoF7m5xio63OmSvCreqEgIkjUILVEPjCeRhIWQF2fCwmCZI5QSqeIunyYOilg7iZEf5QWYDQG6nrW1OQLaj08aQFM1RdAskHApmhRkopHAgHU9ayp84RJop6Q0lMFME2cRs4UYoA674HQAHIt2bgUtNt7+R76PYzUUAS0gb2QbF8npA35rjTpSKV4urEQZhopQL0YCUEFfAG+xEC7s4//netFz+iDZZA8XSiEUsrFTUUwc1bAnHRE6nEk6+K012U4AtbX8JD25Cjo17e6wplzRaAR55FmCnAG5RIqlOnihQb3wXTLMnf/tAp11roc9HtlfG/6lbpf5ko5LcWgI2xGngVhPKkJ7/hNP2hYXN3LaZLjVEZKaO0rwHwbDdQx7uzkUr2VKIFUWwkGOItFJMZIMID54SaoewRFvpH6xup2WQzWx1v+YtoE6G1CnLqAtGMA9yHEfBoMoPesB3WPdr5Y0OyhN4txujgdRjcEG7q3i4uNi6UcphYyn9YGHlDXOS0enkzSVsw71J0OkfTLelEHRkepoBMDqMnN+MHgBMJEUpZSueBJeYe8y5AAT8rBRDGniwLIxDx12MiGyr11pTNV5iLHysEOreHyYL2rG1G8CMxLApZe0PqU9uLoE2Bc2+TvTOlQFTpQd9aNzfxZ37/y6G0utYhuAfN1QHSvBPvHy0AI6kYIJYqQztJwxkrykfKg/OcrsC6vdsVWj4D5Cjn0rgL7Wzz4QUiMgv26FayBbWD2r+JnyOnHwgPbwX7TyvcEPmLf42BdWe1KrZ7FYPUKmJM+DCu2P7Rg1lfiC9jxA5641xPbRB8FwBeCMDNB5/VgN9jvLmcXhqTvr4D9cI9P6Ir7/DDnbcyEe2YOeI72XRz3YDo7cMxrgl2GSDn9AmZiUZWAsOcPhHWYSahIVRh/IWYjrKvZZBmlSwTRJQAISxdk7CobWYYuXXHUA5wARlfJkD1XyawyD1Bk6Rhdpc+Z1lG4Fm+G/1bkEhX8SczlnaXP8Juz5TddEmZvDz4eOQAAAABJRU5ErkJggg== - data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEoAAABKCAIAAACTslUmAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw%2FeHBhY2tldCBiZWdpbj0i77u%2FIiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8%2BIDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDOUM2QjI5MUY0MkExMUUyQjUzMEMyNEUyNjY3NEQyNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDOUM2QjI5MkY0MkExMUUyQjUzMEMyNEUyNjY3NEQyNyI%2BIDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM5QzZCMjhGRjQyQTExRTJCNTMwQzI0RTI2Njc0RDI3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkM5QzZCMjkwRjQyQTExRTJCNTMwQzI0RTI2Njc0RDI3Ii8%2BIDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY%2BIDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8%2BWCTAWgAAA21JREFUeNrsm11IU2EYx3d2vvblttK0iSusFpFtIlkYKajYRTeRXfiBdBXVhVYXIQVdRxHRRUQ3EWSRQVJa3XRRVpSGReVXgVo5ZlNjqftw29nOV29eiOdk6j5yR3lexnjZzvOc97f3fZ%2Fn%2FxzeYb8uO1Srt6lVq7oBHuABHuABHuABHuABHuABHuABHuABHuABHuABHuABHuClrhFJ9JV%2Bume2H%2F3%2BOtDaALMHeIAHoWW5GkYb1Lp0jNKjvhidFoITYjS4ovEwMqeAsO4iLHZyvR3TmmVfi4yP8wxx7o8o3nJjvSsGD5Fo8qvovAO42brQZRoTaS1EL23RMX5yOPL5MdP7ADErFw9T45qCWt3eeoxOi8kQX5urKzml3X0k3HUj%2FOGOSuATGkYSjw7MTesLNMHn5n1ukfGqcAqjjbg5R23InPdKtFADTxqFwPgKCC1orJH%2BR9Gv7UJoUh6%2B07Ko3BLacYjIypMMzuIw1Tb57x%2FlvS7lzh4%2F5Qq2X2SdHYt6oGzl%2BtJGtTFbYu51%2BZoPi2GvEvMe63rna65bCtsfpTrU7r1dxf74INmN5g2GinNKTOu8ZzDQdkJk%2FEs3ESOBQNtJFD8ls7p1n2zdKgJv%2Btl5kWVitRIj08HnF%2BRawF6pLDw0A9xod7xLukvwj879hLTuVBieZzChSCs1V6dZUo3Hs1LfZILSVJouuRTjybITYdmBxEu8lQxOpG%2BROh9JMR7rfCtxbcikbBXxuaK37Zcpb9bZmWI8prdFJhH15WeQIonVD0p0KLnLlj3T9zDVi3PSyXy6J%2FGuzzDVNOEZthhUYtZ2Y%2FVN2dQhbY2UauojZ%2BjNVW6sT3IDo8VUd1e75zhGahaJJZReV9xgrLklU9hI94Q6r6dec87WeMbKa6hy%2FTtZRwaest9ecmP9Qnhq7hZFQYjaXIqkCUbq5OnB3e1vrUe2SsGbKc41%2BrKztP0g6s6vS9gQqhswQoOqQYyg%2F6VeUFEbenFJ5CKKqPfku8ji0Jc1ovd4cvrPL6FXV9iR94n%2B0P%2F5JDxGbSqm86upjUUqfPEsL%2FJRdriD6WmZSQNiEm6%2FPAf9MY2RtBYS2QXEOhsq59TaNZgmbWb9BYTQhOAf5zwDSKCiKJLcp2YY%2FI8B8AAP8AAP8AAP8AAP8AAP8AAP8ABPYe23AAMAXYk%2FgLbQd%2FsAAAAASUVORK5CYII%3D + data:image/x-icon;base64,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 + data:image/png;base64,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 From 0909f112c05f50af996753907d4caa9a5619bff0 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Thu, 12 Dec 2013 13:10:54 -0800 Subject: [PATCH 084/459] Bug 932982 - Trace type constraints and allow preserving jitcode in GCs without also marking all type information, r=billm, r=jandem --- CLOBBER | 5 +- js/public/MemoryMetrics.h | 1 - js/src/gc/Marking.cpp | 46 +---- js/src/gc/RootMarking.cpp | 5 - js/src/gc/Statistics.cpp | 1 - js/src/gc/Statistics.h | 1 - js/src/gc/Zone.cpp | 31 +-- js/src/gc/Zone.h | 2 - js/src/jit/CodeGenerator.cpp | 6 +- js/src/jit/Ion.cpp | 17 +- js/src/jit/Ion.h | 2 +- js/src/jit/IonBuilder.cpp | 6 +- js/src/jit/IonCode.h | 3 + js/src/jit/MIR.cpp | 2 +- js/src/jscompartment.cpp | 11 +- js/src/jscompartment.h | 3 - js/src/jsgc.cpp | 14 +- js/src/jsgc.h | 7 +- js/src/jsinfer.cpp | 329 +++++++++++++++--------------- js/src/jsinfer.h | 122 ++++------- js/src/jsinferinlines.h | 78 ++----- js/src/vm/MemoryMetrics.cpp | 1 - js/xpconnect/src/XPCJSRuntime.cpp | 4 - 23 files changed, 260 insertions(+), 437 deletions(-) diff --git a/CLOBBER b/CLOBBER index f61ddd42af5e..8e6beba9f279 100644 --- a/CLOBBER +++ b/CLOBBER @@ -21,6 +21,5 @@ # Are you updating CLOBBER because you think it's needed for your WebIDL # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 928195 rewrote WebIDL build system integration from the ground up. This -will hopefully be the last required clobber due to WebIDLs poorly interacting -with the build system. + +Bug 932982 apparently requires a clobber or else we get B2G mochitest-2 failures. diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index 95ac91b9f6d5..f974259467d5 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -393,7 +393,6 @@ struct CompartmentStats macro(Other, NotLiveGCThing, baselineStubsOptimized) \ macro(Other, NotLiveGCThing, ionData) \ macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \ - macro(Other, NotLiveGCThing, typeInferencePendingArrays) \ macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \ macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \ macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \ diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index b0c8a6555988..d0e3bde29e8a 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1122,14 +1122,11 @@ gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape) static void ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) { - /* Don't mark properties for singletons. They'll be purged by the GC. */ - if (!type->singleton) { - unsigned count = type->getPropertyCount(); - for (unsigned i = 0; i < count; i++) { - types::Property *prop = type->getProperty(i); - if (prop && JSID_IS_STRING(prop->id)) - PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); - } + unsigned count = type->getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + types::Property *prop = type->getProperty(i); + if (prop && JSID_IS_STRING(prop->id)) + PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); } if (TaggedProto(type->proto).isObject()) @@ -1350,7 +1347,7 @@ GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp) } void -GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr) +GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr) { if (tag == TypeTag) { ScanTypeObject(this, reinterpret_cast(addr)); @@ -1364,35 +1361,6 @@ GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t ad pushObject(obj); } else if (tag == IonCodeTag) { MarkChildren(this, reinterpret_cast(addr)); - } else if (tag == ArenaTag) { - ArenaHeader *aheader = reinterpret_cast(addr); - AllocKind thingKind = aheader->getAllocKind(); - size_t thingSize = Arena::thingSize(thingKind); - - for ( ; aheader; aheader = aheader->next) { - Arena *arena = aheader->getArena(); - FreeSpan firstSpan(aheader->getFirstFreeSpan()); - const FreeSpan *span = &firstSpan; - - for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) { - JS_ASSERT(thing <= arena->thingsEnd()); - if (thing == span->first) { - if (!span->hasNext()) - break; - thing = span->last; - span = span->nextSpan(); - } else { - JSObject *object = reinterpret_cast(thing); - if (object->hasSingletonType() && object->markIfUnmarked(getMarkColor())) - pushObject(object); - budget.step(); - } - } - if (budget.isOverBudget()) { - pushArenaList(aheader); - return; - } - } } } @@ -1430,7 +1398,7 @@ GCMarker::processMarkStackTop(SliceBudget &budget) goto scan_obj; } - processMarkStackOther(budget, tag, addr); + processMarkStackOther(tag, addr); return; scan_value_array: diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 8ae3e85fd559..541ad84d996c 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -726,11 +726,6 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting()) continue; - if (IS_GC_MARKING_TRACER(trc) && zone->isPreservingCode()) { - gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_TYPES); - zone->markTypes(trc); - } - /* Do not discard scripts with counts while profiling. */ if (rt->profilingScripts && !rt->isHeapMinorCollecting()) { for (CellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) { diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 7744b61d53f5..be76eee734a1 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -276,7 +276,6 @@ static const PhaseInfo phases[] = { { PHASE_PURGE, "Purge", PHASE_NO_PARENT }, { PHASE_MARK, "Mark", PHASE_NO_PARENT }, { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MARK }, - { PHASE_MARK_TYPES, "Mark Types", PHASE_MARK_ROOTS }, { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK }, { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT }, { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP }, diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 3240fd238f53..1029fd52fe8e 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -28,7 +28,6 @@ enum Phase { PHASE_PURGE, PHASE_MARK, PHASE_MARK_ROOTS, - PHASE_MARK_TYPES, PHASE_MARK_DELAYED, PHASE_SWEEP, PHASE_SWEEP_MARK, diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index ad0c2fae7fde..539faabbd7c8 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -80,35 +80,6 @@ Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon) needsBarrier_ = needs; } -void -Zone::markTypes(JSTracer *trc) -{ - /* - * Mark all scripts, type objects and singleton JS objects in the - * compartment. These can be referred to directly by type sets, which we - * cannot modify while code which depends on these type sets is active. - */ - JS_ASSERT(isPreservingCode()); - - for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { - JSScript *script = i.get(); - MarkScriptRoot(trc, &script, "mark_types_script"); - JS_ASSERT(script == i.get()); - } - - for (size_t thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) { - ArenaHeader *aheader = allocator.arenas.getFirstArena(static_cast(thingKind)); - if (aheader) - trc->runtime->gcMarker.pushArenaList(aheader); - } - - for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { - types::TypeObject *type = i.get(); - MarkTypeObjectRoot(trc, &type, "mark_types_scan"); - JS_ASSERT(type == i.get()); - } -} - void Zone::resetGCMallocBytes() { @@ -144,7 +115,7 @@ Zone::sweep(FreeOp *fop, bool releaseTypes) if (active) releaseTypes = false; - if (!isPreservingCode()) { + { gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); types.sweep(fop, releaseTypes); } diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index ebad06d0b6c9..a2fe6a22a022 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -310,8 +310,6 @@ struct Zone : public JS::shadow::Zone, js_ReportAllocationOverflow(nullptr); } - void markTypes(JSTracer *trc); - js::types::TypeZone types; void sweep(js::FreeOp *fop, bool releaseTypes); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 63efbd13937e..09fa15507436 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -5825,7 +5825,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) safepoints_.size(), callTargets.length(), patchableBackedges_.length()); if (!ionScript) { - recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); + recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); return false; } @@ -5850,7 +5850,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) // Use js_free instead of IonScript::Destroy: the cache list and // backedge list are still uninitialized. js_free(ionScript); - recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); + recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); return false; } @@ -5871,7 +5871,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) /* resetUses */ false, /* cancelOffThread*/ false)) { js_free(ionScript); - recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); + recompileInfo.compilerOutput(cx->zone()->types)->invalidate(); return false; } } diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 3ca40db2aad5..c103cc61bd38 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2481,7 +2481,7 @@ jit::InvalidateAll(FreeOp *fop, Zone *zone) void -jit::Invalidate(types::TypeCompartment &types, FreeOp *fop, +jit::Invalidate(types::TypeZone &types, FreeOp *fop, const Vector &invalid, bool resetUses, bool cancelOffThread) { @@ -2564,7 +2564,7 @@ void jit::Invalidate(JSContext *cx, const Vector &invalid, bool resetUses, bool cancelOffThread) { - jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses, + jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses, cancelOffThread); } @@ -2611,14 +2611,13 @@ FinishInvalidationOf(FreeOp *fop, JSScript *script, IonScript *ionScript, bool p else script->setIonScript(nullptr); - // If this script has Ion code on the stack, invalidation() will return - // true. In this case we have to wait until destroying it. - if (!ionScript->invalidated()) { - types::TypeCompartment &types = script->compartment()->types; - ionScript->recompileInfo().compilerOutput(types)->invalidate(); + types::TypeZone &types = script->zone()->types; + ionScript->recompileInfo().compilerOutput(types)->invalidate(); + // If this script has Ion code on the stack, invalidated() will return + // true. In this case we have to wait until destroying it. + if (!ionScript->invalidated()) jit::IonScript::Destroy(fop, ionScript); - } } void @@ -2637,8 +2636,6 @@ jit::FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp) // Free optimized baseline stubs. if (comp->jitCompartment()) comp->jitCompartment()->optimizedStubSpace()->free(); - - comp->types.clearCompilerOutputs(fop); } void diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index bf495ccf0e43..ac9a0562714a 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -364,7 +364,7 @@ IonExecStatus IonCannon(JSContext *cx, RunState &state); IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args); // Walk the stack and invalidate active Ion frames for the invalid scripts. -void Invalidate(types::TypeCompartment &types, FreeOp *fop, +void Invalidate(types::TypeZone &types, FreeOp *fop, const Vector &invalid, bool resetUses = true, bool cancelOffThread = true); void Invalidate(JSContext *cx, const Vector &invalid, bool resetUses = true, diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index ef0a2e6b7986..2c210df8eb92 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6296,7 +6296,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc types::HeapTypeSetKey property = staticType->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || - property.configured(constraints(), staticType)) + property.configured(constraints())) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. @@ -6386,7 +6386,7 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name) types::HeapTypeSetKey property = staticType->property(id); if (!property.maybeTypes() || !property.maybeTypes()->definiteProperty() || - property.configured(constraints(), staticType)) + property.configured(constraints())) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. @@ -7836,7 +7836,7 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name, *property = type->property(id); return property->maybeTypes() && property->maybeTypes()->definiteProperty() && - !property->configured(constraints(), type); + !property->configured(constraints()); } bool diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index 8167d1fa80e9..4acc08d50993 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -526,6 +526,9 @@ struct IonScript const types::RecompileInfo& recompileInfo() const { return recompileInfo_; } + types::RecompileInfo& recompileInfoRef() { + return recompileInfo_; + } uint32_t incrOsrPcMismatchCounter() { return ++osrPcMismatchCounter_; } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index aad9234a367f..882b468f6a7b 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -3025,7 +3025,7 @@ jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, // Check if the property has been reconfigured or is a getter. types::HeapTypeSetKey property = object->property(NameToId(name)); - if (property.configured(constraints, object)) + if (property.configured(constraints)) return false; } } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 2d1fbff3ca19..84f911528a8e 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -48,7 +48,6 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options = #endif global_(nullptr), enterCompartmentDepth(0), - lastCodeRelease(0), data(nullptr), objectMetadataCallback(nullptr), lastAnimationTime(0), @@ -545,13 +544,6 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes) WeakMapBase::sweepCompartment(this); } - if (zone()->isPreservingCode()) { - gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); - types.sweepShapes(fop); - } else { - JS_ASSERT(!types.constrainedOutputs); - } - NativeIterator *ni = enumerators->next(); while (ni != enumerators) { JSObject *iterObj = ni->iterObj(); @@ -856,7 +848,6 @@ JSCompartment::clearTraps(FreeOp *fop) void JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *tiPendingArrays, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, @@ -868,7 +859,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *baselineStubsOptimized) { *compartmentObject += mallocSizeOf(this); - types.addSizeOfExcludingThis(mallocSizeOf, tiPendingArrays, tiAllocationSiteTables, + types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, tiArrayTypeTables, tiObjectTypeTables); *shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf) + initialShapes.sizeOfExcludingThis(mallocSizeOf) diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 15a93afe1280..f44c2b09e390 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -187,8 +187,6 @@ struct JSCompartment */ void adoptWorkerAllocator(js::Allocator *workerAllocator); - - int64_t lastCodeRelease; bool activeAnalysis; /* Type information about the scripts and objects in this compartment. */ @@ -221,7 +219,6 @@ struct JSCompartment public: void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *tiPendingArrays, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 909dff6c5c63..80d2b3fafd45 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2857,13 +2857,9 @@ ShouldPreserveJITCode(JSCompartment *comp, int64_t currentTime) if (rt->alwaysPreserveCode) return true; - if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime && - comp->lastCodeRelease + (PRMJ_USEC_PER_SEC * 300) >= currentTime) - { + if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime) return true; - } - comp->lastCodeRelease = currentTime; return false; } @@ -3942,12 +3938,12 @@ BeginSweepingZoneGroup(JSRuntime *rt) bool releaseTypes = ReleaseObservedTypes(rt); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex); - c->sweep(&fop, releaseTypes); + c->sweep(&fop, releaseTypes && !c->zone()->isPreservingCode()); } for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex); - zone->sweep(&fop, releaseTypes); + zone->sweep(&fop, releaseTypes && !zone->isPreservingCode()); } } @@ -5304,10 +5300,6 @@ js::ReleaseAllJITCode(FreeOp *fop) jit::FinishDiscardBaselineScript(fop, script); } } - - /* Sweep now invalidated compiler outputs from each compartment. */ - for (CompartmentsIter comp(fop->runtime(), SkipAtoms); !comp.done(); comp.next()) - comp->types.clearCompilerOutputs(fop); #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 2e5cbe360930..9159f31ec024 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1091,7 +1091,6 @@ struct GCMarker : public JSTracer { ObjectTag, TypeTag, XmlTag, - ArenaTag, SavedValueArrayTag, IonCodeTag, LastTag = IonCodeTag @@ -1119,10 +1118,6 @@ struct GCMarker : public JSTracer { pushTaggedPtr(ObjectTag, obj); } - void pushArenaList(gc::ArenaHeader *firstArena) { - pushTaggedPtr(ArenaTag, firstArena); - } - void pushType(types::TypeObject *type) { pushTaggedPtr(TypeTag, type); } @@ -1229,7 +1224,7 @@ struct GCMarker : public JSTracer { bool restoreValueArray(JSObject *obj, void **vpp, void **endp); void saveValueRanges(); inline void processMarkStackTop(SliceBudget &budget); - void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr); + void processMarkStackOther(uintptr_t tag, uintptr_t addr); void appendGrayRoot(void *thing, JSGCTraceKind kind); diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 0c1fe0167a3c..51c19af54640 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -243,14 +243,6 @@ types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &val if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) return true; - /* - * If we called in here while resolving a type constraint, we may be in the - * middle of resolving a standard class and the type sets will not be updated - * until the outer TypeSet::add finishes. - */ - if (cx->compartment()->types.pendingCount) - return true; - Type type = GetValueType(value); AutoEnterAnalysis enter(cx); @@ -742,12 +734,12 @@ class TypeCompilerConstraint : public TypeConstraint void newType(JSContext *cx, TypeSet *source, Type type) { if (data.invalidateOnNewType(type)) - cx->compartment()->types.addPendingRecompile(cx, compilation); + cx->zone()->types.addPendingRecompile(cx, compilation); } void newPropertyState(JSContext *cx, TypeSet *source) { if (data.invalidateOnNewPropertyState(source)) - cx->compartment()->types.addPendingRecompile(cx, compilation); + cx->zone()->types.addPendingRecompile(cx, compilation); } void newObjectState(JSContext *cx, TypeObject *object) { @@ -755,7 +747,16 @@ class TypeCompilerConstraint : public TypeConstraint // will be sent on changes to its state, so always invalidate any // associated compilations. if (object->unknownProperties() || data.invalidateOnNewObjectState(object)) - cx->compartment()->types.addPendingRecompile(cx, compilation); + cx->zone()->types.addPendingRecompile(cx, compilation); + } + + TypeConstraint *sweep(TypeZone &zone) { + if (data.shouldSweep() || compilation.shouldSweep(zone)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_ >(compilation, data); + if (!res) + zone.setPendingNukeTypes(); + return res; } }; @@ -908,13 +909,21 @@ class TypeConstraintFreezeStack : public TypeConstraint const char *kind() { return "freezeStack"; } - void newType(JSContext *cx, TypeSet *source, Type type) - { + void newType(JSContext *cx, TypeSet *source, Type type) { /* * Unlike TypeConstraintFreeze, triggering this constraint once does * not disable it on future changes to the type set. */ - cx->compartment()->types.addPendingRecompile(cx, script_); + cx->zone()->types.addPendingRecompile(cx, script_); + } + + TypeConstraint *sweep(TypeZone &zone) { + if (IsScriptAboutToBeFinalized(&script_)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_(script_); + if (!res) + zone.setPendingNukeTypes(); + return res; } }; @@ -929,15 +938,22 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu CompilerOutput co(script, executionMode); - TypeCompartment &types = cx->compartment()->types; - if (!types.constrainedOutputs) { - types.constrainedOutputs = cx->new_< Vector >(cx); - if (!types.constrainedOutputs) + TypeZone &types = cx->zone()->types; + if (!types.compilerOutputs) { + types.compilerOutputs = cx->new_< Vector >(cx); + if (!types.compilerOutputs) return false; } - uint32_t index = types.constrainedOutputs->length(); - if (!types.constrainedOutputs->append(co)) +#ifdef DEBUG + for (size_t i = 0; i < types.compilerOutputs->length(); i++) { + const CompilerOutput &co = (*types.compilerOutputs)[i]; + JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode); + } +#endif + + uint32_t index = types.compilerOutputs->length(); + if (!types.compilerOutputs->append(co)) return false; *precompileInfo = RecompileInfo(index); @@ -979,8 +995,8 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu array[i].add(cx, cx->typeLifoAlloc().new_(entry.script), false); } - if (!succeeded || types.constrainedOutputs->back().pendingInvalidation()) { - types.constrainedOutputs->back().invalidate(); + if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) { + types.compilerOutputs->back().invalidate(); script->resetUseCount(); return false; } @@ -1009,6 +1025,8 @@ class ConstraintDataFreeze ? property.maybeTypes()->isSubset(expected) : property.maybeTypes()->empty(); } + + bool shouldSweep() { return false; } }; } /* anonymous namespace */ @@ -1195,6 +1213,8 @@ class ConstraintDataFreezeObjectFlags { return !invalidateOnNewObjectState(property.object()->maybeType()); } + + bool shouldSweep() { return false; } }; } /* anonymous namespace */ @@ -1289,6 +1309,8 @@ class ConstraintDataFreezeObjectForInlinedCall { return true; } + + bool shouldSweep() { return false; } }; // Constraint which triggers recompilation when the template object for a @@ -1315,6 +1337,11 @@ class ConstraintDataFreezeObjectForNewScriptTemplate { return !invalidateOnNewObjectState(property.object()->maybeType()); } + + bool shouldSweep() { + // Note: |templateObject| is only used for equality testing. + return false; + } }; // Constraint which triggers recompilation when the underlying data pointer for @@ -1341,6 +1368,11 @@ class ConstraintDataFreezeObjectForTypedArrayBuffer { return !invalidateOnNewObjectState(property.object()->maybeType()); } + + bool shouldSweep() { + // Note: |viewData| is only used for equality testing. + return false; + } }; } /* anonymous namespace */ @@ -1413,10 +1445,7 @@ namespace { class ConstraintDataFreezeConfiguredProperty { public: - TypeObjectKey *object; - - ConstraintDataFreezeConfiguredProperty(TypeObjectKey *object) - : object(object) + ConstraintDataFreezeConfiguredProperty() {} const char *kind() { return "freezeConfiguredProperty"; } @@ -1430,31 +1459,16 @@ class ConstraintDataFreezeConfiguredProperty bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) { - // Everywhere compiled code depends on definite properties associated - // with a type object's newScript, we need to make sure there are - // constraints in place which will mark those properties as configured - // should the definite properties be invalidated. - TypeObject *type = object->isSingleObject() - ? object->asSingleObject()->type() - : object->asTypeObject(); - if (type->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { - type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; - if (type->hasNewScript()) { - CheckNewScriptProperties(cx, type, type->newScript()->fun); - } else { - JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED); - type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; - } - } - return !property.maybeTypes()->configuredProperty(); } + + bool shouldSweep() { return false; } }; } /* anonymous namespace */ bool -HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type) +HeapTypeSetKey::configured(CompilerConstraintList *constraints) { if (maybeTypes() && maybeTypes()->configuredProperty()) return true; @@ -1463,7 +1477,7 @@ HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *t typedef CompilerConstraintInstance T; constraints->add(alloc->new_(alloc, *this, - ConstraintDataFreezeConfiguredProperty(type))); + ConstraintDataFreezeConfiguredProperty())); return false; } @@ -1986,7 +2000,7 @@ PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj) if (type->unknownProperties()) return true; HeapTypeSetKey index = type->property(JSID_VOID); - if (index.configured(constraints, type) || index.isOwnProperty(constraints)) + if (index.configured(constraints) || index.isOwnProperty(constraints)) return true; obj = obj->getProto(); } while (obj); @@ -2024,27 +2038,8 @@ types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, return PrototypeHasIndexedProperty(constraints, proto); } -bool -TypeCompartment::growPendingArray(JSContext *cx) -{ - unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2); - PendingWork *newArray = js_pod_calloc(newCapacity); - if (!newArray) { - cx->compartment()->types.setPendingNukeTypes(cx); - return false; - } - - PodCopy(newArray, pendingArray, pendingCount); - js_free(pendingArray); - - pendingArray = newArray; - pendingCapacity = newCapacity; - - return true; -} - void -TypeCompartment::processPendingRecompiles(FreeOp *fop) +TypeZone::processPendingRecompiles(FreeOp *fop) { if (!pendingRecompiles) return; @@ -2095,11 +2090,9 @@ TypeZone::nukeTypes(FreeOp *fop) */ JS_ASSERT(pendingNukeTypes); - for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) { - if (comp->types.pendingRecompiles) { - fop->free_(comp->types.pendingRecompiles); - comp->types.pendingRecompiles = nullptr; - } + if (pendingRecompiles) { + fop->free_(pendingRecompiles); + pendingRecompiles = nullptr; } inferenceEnabled = false; @@ -2119,7 +2112,7 @@ TypeZone::nukeTypes(FreeOp *fop) } void -TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) +TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info) { CompilerOutput *co = info.compilerOutput(cx); if (!co || !co->isValid() || co->pendingInvalidation()) @@ -2145,7 +2138,7 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) } void -TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script) +TypeZone::addPendingRecompile(JSContext *cx, JSScript *script) { JS_ASSERT(script); @@ -3146,6 +3139,15 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint } void newType(JSContext *cx, TypeSet *source, Type type) {} + + TypeConstraint *sweep(TypeZone &zone) { + if (IsTypeObjectAboutToBeFinalized(&object)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_(object); + if (!res) + zone.setPendingNukeTypes(); + return res; + } }; bool @@ -3192,6 +3194,15 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint if (source->baseFlags() || source->getObjectCount() > 1) object->clearAddendum(cx); } + + TypeConstraint *sweep(TypeZone &zone) { + if (IsTypeObjectAboutToBeFinalized(&object)) + return nullptr; + TypeConstraint *res = zone.typeLifoAlloc.new_(object); + if (!res) + zone.setPendingNukeTypes(); + return res; + } }; void @@ -3941,8 +3952,19 @@ ConstraintTypeSet::sweep(Zone *zone) } } - /* All constraints are wiped out on each GC. */ + /* + * Type constraints only hold weak references. Copy constraints referring + * to data that is still live into the zone's new arena. + */ + TypeConstraint *constraint = constraintList; constraintList = nullptr; + while (constraint) { + if (TypeConstraint *copy = constraint->sweep(zone->types)) { + copy->next = constraintList; + constraintList = copy; + } + constraint = constraint->next; + } } inline void @@ -3962,18 +3984,6 @@ TypeObject::clearProperties() inline void TypeObject::sweep(FreeOp *fop) { - if (singleton) { - JS_ASSERT(!hasNewScript()); - - /* - * All properties can be discarded. We will regenerate them as needed - * as code gets reanalyzed. - */ - clearProperties(); - - return; - } - if (!isMarked()) { if (addendum) fop->free_(addendum); @@ -3996,6 +4006,14 @@ TypeObject::sweep(FreeOp *fop) for (unsigned i = 0; i < oldCapacity; i++) { Property *prop = oldArray[i]; if (prop) { + if (singleton && !prop->types.constraintList) { + /* + * Don't copy over properties of singleton objects which + * don't have associated constraints. The contents of these + * type sets will be regenerated as necessary. + */ + continue; + } Property *newProp = typeLifoAlloc.new_(*prop); if (newProp) { Property **pentry = @@ -4015,12 +4033,17 @@ TypeObject::sweep(FreeOp *fop) setBasePropertyCount(propertyCount); } else if (propertyCount == 1) { Property *prop = (Property *) propertySet; - Property *newProp = typeLifoAlloc.new_(*prop); - if (newProp) { - propertySet = (Property **) newProp; - newProp->types.sweep(zone()); + if (singleton && !prop->types.constraintList) { + // Skip, as above. + clearProperties(); } else { - zone()->types.setPendingNukeTypes(); + Property *newProp = typeLifoAlloc.new_(*prop); + if (newProp) { + propertySet = (Property **) newProp; + newProp->types.sweep(zone()); + } else { + zone()->types.setPendingNukeTypes(); + } } } @@ -4028,14 +4051,6 @@ TypeObject::sweep(FreeOp *fop) for (unsigned i = 0; i < basePropertyCount(); i++) JS_ASSERT(propertySet[i]); } - - /* - * The GC will clear out the constraints ensuring the correctness of the - * newScript information, these constraints will need to be regenerated - * the next time we compile code which depends on this info. - */ - if (hasNewScript()) - flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE; } void @@ -4119,52 +4134,6 @@ TypeCompartment::sweep(FreeOp *fop) e.rekeyFront(key); } } - - /* - * The pending array is reset on GC, it can grow large (75+ KB) and is easy - * to reallocate if the compartment becomes active again. - */ - if (pendingArray) - fop->free_(pendingArray); - - pendingArray = nullptr; - pendingCapacity = 0; -} - -void -TypeCompartment::sweepShapes(FreeOp *fop) -{ - /* - * Sweep any weak shape references that may be finalized even if a GC is - * preserving type information. - */ - if (objectTypeTable) { - for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) { - const ObjectTableKey &key = e.front().key(); - ObjectTableEntry &entry = e.front().value(); - - if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) { - fop->free_(key.properties); - fop->free_(entry.types); - e.removeFront(); - } - } - } -} - -void -TypeCompartment::clearCompilerOutputs(FreeOp *fop) -{ - if (constrainedOutputs) { - fop->delete_(constrainedOutputs); - constrainedOutputs = nullptr; - } - - if (pendingRecompiles) { - JS_ASSERT(pendingRecompiles->length() == 0); - fop->delete_(pendingRecompiles); - pendingRecompiles = nullptr; - } } void @@ -4193,7 +4162,6 @@ JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table) TypeCompartment::~TypeCompartment() { - js_free(pendingArray); js_delete(arrayTypeTable); js_delete(objectTypeTable); js_delete(allocationSiteTable); @@ -4212,12 +4180,6 @@ TypeScript::Sweep(FreeOp *fop, JSScript *script) /* Remove constraints and references to dead objects from the persistent type sets. */ for (unsigned i = 0; i < num; i++) typeArray[i].sweep(compartment->zone()); - - /* - * Freeze constraints on stack type sets need to be regenerated the next - * time the script is analyzed. - */ - script->clearHasFreezeConstraints(); } void @@ -4234,20 +4196,10 @@ Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePoo void TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *pendingArrays, size_t *allocationSiteTables, size_t *arrayTypeTables, size_t *objectTypeTables) { - /* Pending arrays are cleared on GC along with the analysis pool. */ - *pendingArrays += mallocSizeOf(pendingArray); - - /* - * TypeCompartment::pendingRecompiles is non-nullptr only while inference - * code is running. - */ - JS_ASSERT(!pendingRecompiles); - if (allocationSiteTable) *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); @@ -4289,6 +4241,8 @@ TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const TypeZone::TypeZone(Zone *zone) : zone_(zone), typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), + compilerOutputs(nullptr), + pendingRecompiles(nullptr), pendingNukeTypes(false), inferenceEnabled(false) { @@ -4296,6 +4250,8 @@ TypeZone::TypeZone(Zone *zone) TypeZone::~TypeZone() { + js_delete(compilerOutputs); + js_delete(pendingRecompiles); } void @@ -4312,11 +4268,21 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize()); oldAlloc.steal(&typeLifoAlloc); - /* - * Sweep analysis information and everything depending on it from the - * compartment, including all remaining mjit code if inference is - * enabled in the compartment. - */ + /* Sweep and find compressed indexes for each compiler output. */ + size_t newCompilerOutputCount = 0; + if (compilerOutputs) { + for (size_t i = 0; i < compilerOutputs->length(); i++) { + CompilerOutput &output = (*compilerOutputs)[i]; + if (output.isValid()) { + JSScript *script = output.script(); + if (IsScriptAboutToBeFinalized(&script)) + output.invalidate(); + else + output.setSweepIndex(newCompilerOutputCount++); + } + } + } + if (inferenceEnabled) { gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI); @@ -4328,6 +4294,21 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) if (releaseTypes) { script->types->destroy(); script->types = nullptr; + + /* + * Freeze constraints on stack type sets need to be + * regenerated the next time the script is analyzed. + */ + script->clearHasFreezeConstraints(); + + JS_ASSERT(!script->hasIonScript()); + JS_ASSERT(!script->hasParallelIonScript()); + } else { + /* Update the recompile indexes in any IonScripts still on the script. */ + if (script->hasIonScript()) + script->ionScript()->recompileInfoRef().shouldSweep(*this); + if (script->hasParallelIonScript()) + script->parallelIonScript()->recompileInfoRef().shouldSweep(*this); } } } @@ -4347,6 +4328,20 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes) comp->types.sweep(fop); } + if (compilerOutputs) { + size_t sweepIndex = 0; + for (size_t i = 0; i < compilerOutputs->length(); i++) { + CompilerOutput output = (*compilerOutputs)[i]; + if (output.isValid()) { + JS_ASSERT(sweepIndex == output.sweepIndex()); + output.setSweepIndex(0); + (*compilerOutputs)[sweepIndex++] = output; + } + } + JS_ASSERT(sweepIndex == newCompilerOutputCount); + JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount)); + } + { gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS); for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) { diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 690631dfa7ad..38c3be127e82 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -177,7 +177,7 @@ namespace analyze { namespace types { -class TypeCompartment; +class TypeZone; class TypeSet; class TypeObjectKey; @@ -278,28 +278,10 @@ inline Type GetValueType(const Value &val); * Type information about the values observed within scripts and about the * contents of the heap is accumulated as the program executes. Compilation * accumulates constraints relating type information on the heap with the - * compilations that should be invalidated when those types change. This data - * is periodically cleared to reduce memory usage. - * - * GCs may clear both analysis information and jitcode. Sometimes GCs will - * preserve all information and code, and will not collect any scripts, type - * objects or singleton JS objects. - * - * The following data is cleared by all non-preserving GCs: - * - * - The ScriptAnalysis for each analyzed script and data from each analysis - * pass performed. - * - * - Property type sets for singleton JS objects. - * - * - Type constraints and dead references in all type sets. - * - * The following data is occasionally cleared by non-preserving GCs: - * - * - TypeScripts and their type sets are occasionally destroyed, per a timer. - * - * - When a JSScript or TypeObject is swept, type information for its contents - * is destroyed. + * compilations that should be invalidated when those types change. Type + * information and constraints are allocated in the zone's typeLifoAlloc, + * and on GC all data referring to live things is copied into a new allocator. + * Thus, type set and constraints only hold weak references. */ /* @@ -334,6 +316,12 @@ public: * state. */ virtual void newObjectState(JSContext *cx, TypeObject *object) {} + + /* + * If the data this constraint refers to is still live, copy it into the + * zone's new allocator. Type constraints only hold weak references. + */ + virtual TypeConstraint *sweep(TypeZone &zone) = 0; }; /* Flags and other state stored in TypeSet::flags */ @@ -394,13 +382,6 @@ enum { /* If set, addendum information should not be installed on this object. */ OBJECT_FLAG_ADDENDUM_CLEARED = 0x2, - /* - * If set, type constraints covering the correctness of the newScript - * definite properties need to be regenerated before compiling any jitcode - * which depends on this information. - */ - OBJECT_FLAG_NEW_SCRIPT_REGENERATE = 0x4, - /* * Whether we have ensured all type sets in the compartment contain * ANYOBJECT instead of this object. @@ -869,12 +850,6 @@ struct TypeTypedObject : public TypeObjectAddendum * information is sensitive to changes in the property's type. Future changes * to the property (whether those uncovered by analysis or those occurring * in the VM) will treat these properties like those of any other type object. - * - * When a GC occurs, we wipe out all analysis information for all the - * compartment's scripts, so can destroy all properties on singleton type - * objects at the same time. If there is no reference on the stack to the - * type object itself, the type object is also destroyed, and the JS object - * reverts to having a lazy type. */ /* Type information about an object accessed by a script. */ @@ -1316,7 +1291,7 @@ class HeapTypeSetKey void freeze(CompilerConstraintList *constraints); JSValueType knownTypeTag(CompilerConstraintList *constraints); - bool configured(CompilerConstraintList *constraints, TypeObjectKey *type); + bool configured(CompilerConstraintList *constraints); bool isOwnProperty(CompilerConstraintList *constraints); bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other); JSObject *singleton(CompilerConstraintList *constraints); @@ -1339,6 +1314,10 @@ class CompilerOutput // Whether this compilation is about to be invalidated. bool pendingInvalidation_ : 1; + // During sweeping, the list of compiler outputs is compacted and invalidated + // outputs are removed. This gives the new index for a valid compiler output. + uint32_t sweepIndex_ : 29; + public: CompilerOutput() : script_(nullptr), mode_(SequentialExecution), pendingInvalidation_(false) @@ -1366,6 +1345,15 @@ class CompilerOutput bool pendingInvalidation() { return pendingInvalidation_; } + + void setSweepIndex(uint32_t index) { + if (index >= 1 << 29) + MOZ_CRASH(); + sweepIndex_ = index; + } + uint32_t sweepIndex() { + return sweepIndex_; + } }; class RecompileInfo @@ -1380,8 +1368,9 @@ class RecompileInfo bool operator == (const RecompileInfo &o) const { return outputIndex == o.outputIndex; } - CompilerOutput *compilerOutput(TypeCompartment &types) const; + CompilerOutput *compilerOutput(TypeZone &types) const; CompilerOutput *compilerOutput(JSContext *cx) const; + bool shouldSweep(TypeZone &types); }; /* Type information for a compartment. */ @@ -1389,32 +1378,9 @@ struct TypeCompartment { /* Constraint solving worklist structures. */ - /* - * Worklist of types which need to be propagated to constraints. We use a - * worklist to avoid blowing the native stack. - */ - struct PendingWork - { - TypeConstraint *constraint; - ConstraintTypeSet *source; - Type type; - }; - PendingWork *pendingArray; - unsigned pendingCount; - unsigned pendingCapacity; - - /* Whether we are currently resolving the pending worklist. */ - bool resolving; - /* Number of scripts in this compartment. */ unsigned scriptCount; - /* Valid & Invalid script referenced by type constraints. */ - Vector *constrainedOutputs; - - /* Pending recompilations to perform before execution of JIT code can resume. */ - Vector *pendingRecompiles; - /* Table for referencing types of objects keyed to an allocation site. */ AllocationSiteTable *allocationSiteTable; @@ -1438,14 +1404,6 @@ struct TypeCompartment inline JSCompartment *compartment(); - /* Add a type to register with a list of constraints. */ - inline void addPending(JSContext *cx, TypeConstraint *constraint, - ConstraintTypeSet *source, Type type); - bool growPendingArray(JSContext *cx); - - /* Resolve pending type registrations, excluding delayed ones. */ - inline void resolvePending(JSContext *cx); - /* Prints results of this compartment if spew is enabled or force is set. */ void print(JSContext *cx, bool force); @@ -1461,26 +1419,16 @@ struct TypeCompartment /* Get or make an object for an allocation site, and add to the allocation site table. */ TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key); - void processPendingRecompiles(FreeOp *fop); - /* Mark all types as needing destruction once inference has 'finished'. */ void setPendingNukeTypes(ExclusiveContext *cx); - /* Mark a script as needing recompilation once inference has finished. */ - void addPendingRecompile(JSContext *cx, const RecompileInfo &info); - void addPendingRecompile(JSContext *cx, JSScript *script); - /* Mark any type set containing obj as having a generic object type. */ void markSetsUnknown(JSContext *cx, TypeObject *obj); void sweep(FreeOp *fop); - void sweepShapes(FreeOp *fop); - void clearCompilerOutputs(FreeOp *fop); - void finalizeObjects(); void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *pendingArrays, size_t *allocationSiteTables, size_t *arrayTypeTables, size_t *objectTypeTables); @@ -1496,6 +1444,16 @@ struct TypeZone static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024; js::LifoAlloc typeLifoAlloc; + /* + * All Ion compilations that have occured in this zone, for indexing via + * RecompileInfo. This includes both valid and invalid compilations, though + * invalidated compilations are swept on GC. + */ + Vector *compilerOutputs; + + /* Pending recompilations to perform before execution of JIT code can resume. */ + Vector *pendingRecompiles; + /* * Bit set if all current types must be marked as unknown, and all scripts * recompiled. Caused by OOM failure within inference operations. @@ -1516,6 +1474,12 @@ struct TypeZone /* Mark all types as needing destruction once inference has 'finished'. */ void setPendingNukeTypes(); + /* Mark a script as needing recompilation once inference has finished. */ + void addPendingRecompile(JSContext *cx, const RecompileInfo &info); + void addPendingRecompile(JSContext *cx, JSScript *script); + + void processPendingRecompiles(FreeOp *fop); + void nukeTypes(FreeOp *fop); }; diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 0166b368d659..faad1f66ff62 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -103,17 +103,29 @@ CompilerOutput::ion() const } inline CompilerOutput* -RecompileInfo::compilerOutput(TypeCompartment &types) const +RecompileInfo::compilerOutput(TypeZone &types) const { - if (!types.constrainedOutputs || outputIndex >= types.constrainedOutputs->length()) + if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length()) return nullptr; - return &(*types.constrainedOutputs)[outputIndex]; + return &(*types.compilerOutputs)[outputIndex]; } inline CompilerOutput* RecompileInfo::compilerOutput(JSContext *cx) const { - return compilerOutput(cx->compartment()->types); + return compilerOutput(cx->zone()->types); +} + +inline bool +RecompileInfo::shouldSweep(TypeZone &types) +{ + CompilerOutput *output = compilerOutput(types); + if (!output || !output->isValid()) + return true; + + // Update this info for the output's new index in the zone's compiler outputs. + outputIndex = output->sweepIndex(); + return false; } ///////////////////////////////////////////////////////////////////// @@ -287,14 +299,13 @@ struct AutoEnterAnalysis * If there are no more type inference activations on the stack, * process any triggered recompilations. Note that we should not be * invoking any scripted code while type inference is running. - * :TODO: assert this. */ if (!compartment->activeAnalysis) { - TypeCompartment *types = &compartment->types; - if (compartment->zone()->types.pendingNukeTypes) - compartment->zone()->types.nukeTypes(freeOp); - else if (types->pendingRecompiles) - types->processPendingRecompiles(freeOp); + TypeZone &types = compartment->zone()->types; + if (types.pendingNukeTypes) + types.nukeTypes(freeOp); + else if (types.pendingRecompiles) + types.processPendingRecompiles(freeOp); } } @@ -848,50 +859,6 @@ TypeCompartment::compartment() return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); } -inline void -TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, - ConstraintTypeSet *source, Type type) -{ - JS_ASSERT(this == &cx->compartment()->types); - JS_ASSERT(!cx->runtime()->isHeapBusy()); - - InferSpew(ISpewOps, "pending: %sC%p%s %s", - InferSpewColor(constraint), constraint, InferSpewColorReset(), - TypeString(type)); - - if ((pendingCount == pendingCapacity) && !growPendingArray(cx)) - return; - - PendingWork &pending = pendingArray[pendingCount++]; - pending.constraint = constraint; - pending.source = source; - pending.type = type; -} - -inline void -TypeCompartment::resolvePending(JSContext *cx) -{ - JS_ASSERT(this == &cx->compartment()->types); - - if (resolving) { - /* There is an active call further up resolving the worklist. */ - return; - } - - resolving = true; - - /* Handle all pending type registrations. */ - while (pendingCount) { - const PendingWork &pending = pendingArray[--pendingCount]; - InferSpew(ISpewOps, "resolve: %sC%p%s %s", - InferSpewColor(pending.constraint), pending.constraint, - InferSpewColorReset(), TypeString(pending.type)); - pending.constraint->newType(cx, pending.source, pending.type); - } - - resolving = false; -} - ///////////////////////////////////////////////////////////////////// // TypeSet ///////////////////////////////////////////////////////////////////// @@ -1215,10 +1182,9 @@ ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type) if (JSContext *cx = cxArg->maybeJSContext()) { TypeConstraint *constraint = constraintList; while (constraint) { - cx->compartment()->types.addPending(cx, constraint, this, type); + constraint->newType(cx, this, type); constraint = constraint->next; } - cx->compartment()->types.resolvePending(cx); } else { JS_ASSERT(!constraintList); } diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index 3ba7d433c6f8..ee09906df5bd 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -215,7 +215,6 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment) // Measure the compartment object itself, and things hanging off it. compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_, - &cStats.typeInferencePendingArrays, &cStats.typeInferenceAllocationSiteTables, &cStats.typeInferenceArrayTypeTables, &cStats.typeInferenceObjectTypeTables, diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 2f33a01747fa..8a8d8391a7f5 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2087,10 +2087,6 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats, cStats.typeInferenceTypeScripts, "Memory used by type sets associated with scripts."); - ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/pending-arrays"), - cStats.typeInferencePendingArrays, - "Memory used for solving constraints during type inference."); - ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"), cStats.typeInferenceAllocationSiteTables, "Memory indexing type objects associated with allocation sites."); From dc08516cffdd9ca07df051fda58e3b808c7d35e5 Mon Sep 17 00:00:00 2001 From: Benoit Girard Date: Wed, 11 Dec 2013 23:16:03 -0500 Subject: [PATCH 085/459] Bug 941095 - Part 1: Support SetPermitSubpixelAA with Quartz. r=bas --HG-- extra : rebase_source : 69171a8a17a34c822b34f48babecf18ffa08bf56 --- gfx/2d/DrawTargetCG.cpp | 8 ++++++++ gfx/2d/DrawTargetCG.h | 1 + 2 files changed, 9 insertions(+) diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp index 98f5ce75d7c6..d8f703e83d9e 100644 --- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -1338,6 +1338,7 @@ DrawTargetCG::Init(BackendType aType, // XXX: Create input parameter to control interpolation and // use the default for content. CGContextSetInterpolationQuality(mCg, kCGInterpolationLow); + CGContextSetShouldSmoothFonts(mCg, GetPermitSubpixelAA()); if (aType == BACKEND_COREGRAPHICS_ACCELERATED) { @@ -1377,6 +1378,7 @@ DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize) mSize = aSize; mCg = cgContext; + CGContextSetShouldSmoothFonts(mCg, GetPermitSubpixelAA()); CGContextRetain(mCg); assert(mCg); @@ -1530,6 +1532,12 @@ DrawTargetCG::MarkChanged() } } +void +DrawTargetCG::SetPermitSubpixelAA(bool aPermitSubpixelAA) { + DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA); + CGContextSetShouldSmoothFonts(mCg, aPermitSubpixelAA); +} + CGContextRef BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT) { diff --git a/gfx/2d/DrawTargetCG.h b/gfx/2d/DrawTargetCG.h index 3a972852d06d..a46acdc14ff4 100644 --- a/gfx/2d/DrawTargetCG.h +++ b/gfx/2d/DrawTargetCG.h @@ -154,6 +154,7 @@ public: virtual IntSize GetSize() { return mSize; } + virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) MOZ_OVERRIDE; /* This is for creating good compatible surfaces */ virtual TemporaryRef CreateSourceSurfaceFromData(unsigned char *aData, From 892081b54a585f676ec6fcf71872f7fb436d9d3f Mon Sep 17 00:00:00 2001 From: Benoit Girard Date: Fri, 13 Dec 2013 13:53:18 -0500 Subject: [PATCH 086/459] Bug 941095 - Part 2: Disable subpixelaa + component alpha. r=mattwoodrow --HG-- extra : rebase_source : 7e3a0d3710fb29b47615cc0254135d3b44637ed8 --- gfx/layers/Layers.cpp | 14 +++++++++----- gfx/layers/Layers.h | 8 +++++++- layout/base/FrameLayerBuilder.cpp | 17 +++++++++++------ layout/reftests/transform-3d/reftest.list | 4 ++-- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 5fffb03188e6..af3a4f3bfb3e 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -1494,10 +1494,12 @@ PrintInfo(nsACString& aTo, LayerComposite* aLayerComposite) void SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget) { + bool permitSubpixelAA = !(aLayer->GetContentFlags() & Layer::CONTENT_DISABLE_SUBPIXEL_AA); if (!aTarget->IsCairo()) { RefPtr dt = aTarget->GetDrawTarget(); if (dt->GetFormat() != FORMAT_B8G8R8A8) { + dt->SetPermitSubpixelAA(permitSubpixelAA); return; } @@ -1507,20 +1509,22 @@ SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget) transformedBounds.RoundOut(); IntRect intTransformedBounds; transformedBounds.ToIntRect(&intTransformedBounds); - dt->SetPermitSubpixelAA(!(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) || - dt->GetOpaqueRect().Contains(intTransformedBounds)); + permitSubpixelAA &= !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) || + dt->GetOpaqueRect().Contains(intTransformedBounds); + dt->SetPermitSubpixelAA(permitSubpixelAA); } else { nsRefPtr surface = aTarget->CurrentSurface(); if (surface->GetContentType() != GFX_CONTENT_COLOR_ALPHA) { // Destination doesn't have alpha channel; no need to set any special flags + surface->SetSubpixelAntialiasingEnabled(permitSubpixelAA); return; } const nsIntRect& bounds = aLayer->GetVisibleRegion().GetBounds(); - surface->SetSubpixelAntialiasingEnabled( - !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) || + permitSubpixelAA &= !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) || surface->GetOpaqueRect().Contains( - aTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)))); + aTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height))); + surface->SetSubpixelAntialiasingEnabled(permitSubpixelAA); } } diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index b560e5fce2dc..38721dd66fff 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -684,7 +684,13 @@ public: * transaction where there is no possibility of redrawing the content, so the * implementation should be ready for that. */ - CONTENT_MAY_CHANGE_TRANSFORM = 0x08 + CONTENT_MAY_CHANGE_TRANSFORM = 0x08, + + /** + * Disable subpixel AA for this layer. This is used if the display isn't suited + * for subpixel AA like hidpi or rotated content. + */ + CONTENT_DISABLE_SUBPIXEL_AA = 0x10 }; /** * CONSTRUCTION PHASE ONLY diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 3c4d96df5782..2f0853c9be8e 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1798,13 +1798,18 @@ ContainerState::PopThebesLayerData() // mask layer for image and color layers SetupMaskLayer(layer, data->mItemClip); } - uint32_t flags; + + uint32_t flags = 0; + nsIWidget* widget = mContainerReferenceFrame->PresContext()->GetRootWidget(); + // Disable subpixelAA on hidpi + bool hidpi = widget && widget->GetDefaultScale().scale >= 2; + if (hidpi) { + flags |= Layer::CONTENT_DISABLE_SUBPIXEL_AA; + } if (isOpaque && !data->mForceTransparentSurface) { - flags = Layer::CONTENT_OPAQUE; - } else if (data->mNeedComponentAlpha) { - flags = Layer::CONTENT_COMPONENT_ALPHA; - } else { - flags = 0; + flags |= Layer::CONTENT_OPAQUE; + } else if (data->mNeedComponentAlpha && !hidpi) { + flags |= Layer::CONTENT_COMPONENT_ALPHA; } layer->SetContentFlags(flags); diff --git a/layout/reftests/transform-3d/reftest.list b/layout/reftests/transform-3d/reftest.list index 2d81127ca9cb..447369b3adc9 100644 --- a/layout/reftests/transform-3d/reftest.list +++ b/layout/reftests/transform-3d/reftest.list @@ -22,8 +22,8 @@ fails-if(Android&&AndroidVersion!=17) == rotatex-perspective-3a.html rotatex-per skip-if(B2G) == preserve3d-4a.html green-rect.html fuzzy-if(Android&&AndroidVersion>=15,4,300) == preserve3d-5a.html preserve3d-5-ref.html == scale3d-z.html scalez-1-ref.html -fuzzy-if(winWidget,102,580) fuzzy-if(d2d,143,681) fuzzy-if(OSX==10.8,145,752) == scale3d-all.html scale3d-1-ref.html # subpixel AA -fuzzy-if(winWidget,102,580) fuzzy-if(d2d,143,681) fuzzy-if(OSX==10.8,145,752) == scale3d-all-separate.html scale3d-1-ref.html # subpixel AA +fuzzy-if(winWidget,102,580) fuzzy-if(d2d,143,681) fuzzy-if(OSX==10.8,224,924) fuzzy-if(OSX==10.9,224,924) == scale3d-all.html scale3d-1-ref.html # subpixel AA +fuzzy-if(winWidget,102,580) fuzzy-if(d2d,143,681) fuzzy-if(OSX==10.8,224,924) fuzzy-if(OSX==10.9,224,924) == scale3d-all-separate.html scale3d-1-ref.html # subpixel AA == scale3d-xz.html scale3d-1-ref.html == translatez-1a.html translatez-1-ref.html != translatez-1b.html translatez-1-ref.html From fd192955fd7bedb3949c26359bdec39f8206a91d Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 13 Dec 2013 19:37:34 +0000 Subject: [PATCH 087/459] Bug 936500 - Always build layers for nsDisplayScrollLayer. r=tn APZC information is associated with layers, and having those layers suddenly disappear because they became occluded, or their displayports are off-screen has undesired effects. --- layout/base/nsDisplayList.cpp | 10 ++++++++++ layout/base/nsDisplayList.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 6f9a05370177..b2999c012802 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -3498,6 +3498,16 @@ nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder, return layer.forget(); } +bool +nsDisplayScrollLayer::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) +{ + if (nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), nullptr)) { + return true; + } + + return nsDisplayWrapList::ShouldBuildLayerEvenIfInvisible(aBuilder); +} + bool nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 2b28efd826cf..77a5fab0311f 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -2713,6 +2713,8 @@ public: LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE; + virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE; + virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, const nsRect& aAllowVisibleRegionExpansion) MOZ_OVERRIDE; From 8fa042c6ad8fb43538a286acc9a4ed0551cb2360 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 13 Dec 2013 19:37:42 +0000 Subject: [PATCH 088/459] Bug 936500 - Don't copy Axis when creating a FlingAnimation. r=kats FlingAnimation was, likely accidentally, creating copies of the axes it was animating and operating on those, which lead to problems when displayports were calculated based on the original copies which housed old values. --- gfx/layers/ipc/AsyncPanZoomController.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index a1a034ffa564..c2fefad6d5e5 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -280,7 +280,7 @@ GetFrameTime() { class FlingAnimation: public AsyncPanZoomAnimation { public: - FlingAnimation(AxisX aX, AxisY aY) + FlingAnimation(AxisX& aX, AxisY& aY) : AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gFlingRepaintInterval)) , mX(aX) , mY(aY) @@ -295,8 +295,8 @@ public: const TimeDuration& aDelta); private: - AxisX mX; - AxisY mY; + AxisX& mX; + AxisY& mY; }; class ZoomAnimation: public AsyncPanZoomAnimation { From 774e6ed76a32f43117dde1293fe29d36947800a5 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Thu, 12 Dec 2013 15:30:53 -0800 Subject: [PATCH 089/459] Bug 949283 - Add a post-barrier to missingScopes for the JSObject* in ScopeKeyIter; r=jonco --- js/src/vm/ScopeObject.cpp | 32 ++++++++++++++++++++++++++++++++ js/src/vm/ScopeObject.h | 16 +++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 5e61bf7f5675..d80c61eb46db 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -1570,6 +1570,37 @@ DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map, #endif } +#ifdef JSGC_GENERATIONAL +class DebugScopes::MissingScopesRef : public BufferableRef +{ + MissingScopeMap *map; + ScopeIterKey key; + + public: + MissingScopesRef(MissingScopeMap *m, const ScopeIterKey &k) : map(m), key(k) {} + + void mark(JSTracer *trc) { + ScopeIterKey prior = key; + MissingScopeMap::Ptr p = map->lookup(key); + if (!p) + return; + JS_SET_TRACING_LOCATION(trc, &const_cast(p->key()).enclosingScope()); + Mark(trc, &key.enclosingScope(), "MissingScopesRef"); + map->rekeyIfMoved(prior, key); + } +}; +#endif + +/* static */ JS_ALWAYS_INLINE void +DebugScopes::missingScopesPostWriteBarrier(JSRuntime *rt, MissingScopeMap *map, + const ScopeIterKey &key) +{ +#ifdef JSGC_GENERATIONAL + if (key.enclosingScope() && IsInsideNursery(rt, key.enclosingScope())) + rt->gcStoreBuffer.putGeneric(MissingScopesRef(map, key)); +#endif +} + /* static */ JS_ALWAYS_INLINE void DebugScopes::liveScopesPostWriteBarrier(JSRuntime *rt, LiveScopeMap *map, ScopeObject *key) { @@ -1763,6 +1794,7 @@ DebugScopes::addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject js_ReportOutOfMemory(cx); return false; } + missingScopesPostWriteBarrier(cx->runtime(), &scopes->missingScopes, si); JS_ASSERT(!scopes->liveScopes.has(&debugScope.scope())); if (!scopes->liveScopes.put(&debugScope.scope(), si)) { diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 4dafb301719a..e6a5f4c4f68e 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -597,11 +597,22 @@ class ScopeIterKey StaticBlockObject *block() const { return block_; } ScopeIter::Type type() const { return type_; } bool hasScopeObject() const { return hasScopeObject_; } + JSObject *enclosingScope() const { return cur_; } + JSObject *&enclosingScope() { return cur_; } /* For use as hash policy */ typedef ScopeIterKey Lookup; static HashNumber hash(ScopeIterKey si); static bool match(ScopeIterKey si1, ScopeIterKey si2); + bool operator!=(const ScopeIterKey &other) const { + return frame_ != other.frame_ || + cur_ != other.cur_ || + block_ != other.block_ || + type_ != other.type_; + } + static void rekey(ScopeIterKey &k, const ScopeIterKey& newKey) { + k = newKey; + } }; class ScopeIterVal @@ -613,7 +624,7 @@ class ScopeIterVal RelocatablePtr block_; ScopeIter::Type type_; bool hasScopeObject_; - + static void staticAsserts(); public: @@ -704,6 +715,9 @@ class DebugScopes ScopeIterKey, RuntimeAllocPolicy> MissingScopeMap; MissingScopeMap missingScopes; + class MissingScopesRef; + static JS_ALWAYS_INLINE void missingScopesPostWriteBarrier(JSRuntime *rt, MissingScopeMap *map, + const ScopeIterKey &key); /* * The map from scope objects of live frames to the live frame. This map From 3635aa6f6f5868cf21e59e39c83c23cda2525b56 Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Fri, 6 Dec 2013 16:37:52 -0500 Subject: [PATCH 090/459] Bug 863872 - If we have overdue TelemetryPings, try to send them all. r=nfroyd, feedback=irving. --- .../components/telemetry/TelemetryFile.jsm | 75 ++++- toolkit/components/telemetry/TelemetryPing.js | 26 +- .../tests/unit/test_TelemetryPing.js | 4 +- .../tests/unit/test_TelemetrySendOldPings.js | 265 ++++++++++++++++++ .../telemetry/tests/unit/xpcshell.ini | 2 + 5 files changed, 353 insertions(+), 19 deletions(-) create mode 100644 toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js diff --git a/toolkit/components/telemetry/TelemetryFile.jsm b/toolkit/components/telemetry/TelemetryFile.jsm index 7a21d5c2fd4e..8db9eb815b58 100644 --- a/toolkit/components/telemetry/TelemetryFile.jsm +++ b/toolkit/components/telemetry/TelemetryFile.jsm @@ -22,8 +22,13 @@ const PR_EXCL = 0x80; const RW_OWNER = parseInt("0600", 8); const RWX_OWNER = parseInt("0700", 8); -// Delete ping files that have been lying around for longer than this. -const MAX_PING_FILE_AGE = 7 * 24 * 60 * 60 * 1000; // 1 week +// Files that have been lying around for longer than MAX_PING_FILE_AGE are +// deleted without being loaded. +const MAX_PING_FILE_AGE = 14 * 24 * 60 * 60 * 1000; // 2 weeks + +// Files that are older than OVERDUE_PING_FILE_AGE, but younger than +// MAX_PING_FILE_AGE indicate that we need to send all of our pings ASAP. +const OVERDUE_PING_FILE_AGE = 7 * 24 * 60 * 60 * 1000; // 1 week // The number of outstanding saved pings that we have issued loading // requests for. @@ -32,6 +37,14 @@ let pingsLoaded = 0; // The number of those requests that have actually completed. let pingLoadsCompleted = 0; +// The number of pings that we have destroyed due to being older +// than MAX_PING_FILE_AGE. +let pingsDiscarded = 0; + +// The number of pings that are older than OVERDUE_PING_FILE_AGE +// but younger than MAX_PING_FILE_AGE. +let pingsOverdue = 0; + // If |true|, send notifications "telemetry-test-save-complete" // and "telemetry-test-load-complete" once save/load is complete. let shouldNotifyUponSave = false; @@ -41,6 +54,14 @@ let pendingPings = []; this.TelemetryFile = { + get MAX_PING_FILE_AGE() { + return MAX_PING_FILE_AGE; + }, + + get OVERDUE_PING_FILE_AGE() { + return OVERDUE_PING_FILE_AGE; + }, + /** * Save a single ping to a file. * @@ -142,7 +163,7 @@ this.TelemetryFile = { * ping. It is passed |true| in case of success, |false| in case of * format error. */ - loadSavedPings: function(sync, onLoad = null) { + loadSavedPings: function(sync, onLoad = null, onDone = null) { let directory = ensurePingDirectory(); let entries = directory.directoryEntries .QueryInterface(Ci.nsIDirectoryEnumerator); @@ -150,7 +171,7 @@ this.TelemetryFile = { pingLoadsCompleted = 0; try { while (entries.hasMoreElements()) { - this.loadHistograms(entries.nextFile, sync, onLoad); + this.loadHistograms(entries.nextFile, sync, onLoad, onDone); } } finally { entries.close(); @@ -169,20 +190,26 @@ this.TelemetryFile = { * ping. It is passed |true| in case of success, |false| in case of * format error. */ - loadHistograms: function loadHistograms(file, sync, onLoad = null) { - let now = new Date(); + loadHistograms: function loadHistograms(file, sync, onLoad = null, onDone = null) { + let now = Date.now(); if (now - file.lastModifiedTime > MAX_PING_FILE_AGE) { // We haven't had much luck in sending this file; delete it. file.remove(true); + pingsDiscarded++; return; } + // This file is a bit stale, and overdue for sending. + if (now - file.lastModifiedTime > OVERDUE_PING_FILE_AGE) { + pingsOverdue++; + } + pingsLoaded++; if (sync) { let stream = Cc["@mozilla.org/network/file-input-stream;1"] .createInstance(Ci.nsIFileInputStream); stream.init(file, -1, -1, 0); - addToPendingPings(file, stream, onLoad); + addToPendingPings(file, stream, onLoad, onDone); } else { let channel = NetUtil.newChannel(file); channel.contentType = "application/json"; @@ -191,7 +218,7 @@ this.TelemetryFile = { if (!Components.isSuccessCode(result)) { return; } - addToPendingPings(file, stream, onLoad); + addToPendingPings(file, stream, onLoad, onDone); }).bind(this)); } }, @@ -203,6 +230,22 @@ this.TelemetryFile = { return pingsLoaded; }, + /** + * The number of pings loaded that are older than OVERDUE_PING_FILE_AGE + * but younger than MAX_PING_FILE_AGE. + */ + get pingsOverdue() { + return pingsOverdue; + }, + + /** + * The number of pings that we just tossed out for being older than + * MAX_PING_FILE_AGE. + */ + get pingsDiscarded() { + return pingsDiscarded; + }, + /** * Iterate destructively through the pending pings. * @@ -249,7 +292,7 @@ function ensurePingDirectory() { return directory; }; -function addToPendingPings(file, stream, onLoad) { +function addToPendingPings(file, stream, onLoad, onDone) { let success = false; try { @@ -263,19 +306,25 @@ function addToPendingPings(file, stream, onLoad) { } pingLoadsCompleted++; pendingPings.push(ping); - if (shouldNotifyUponSave && - pingLoadsCompleted == pingsLoaded) { - Services.obs.notifyObservers(null, "telemetry-test-load-complete", null); - } success = true; } catch (e) { // An error reading the file, or an error parsing the contents. stream.close(); // close is idempotent. file.remove(true); // FIXME: Should be false, isn't it? } + if (onLoad) { onLoad(success); } + + if (pingLoadsCompleted == pingsLoaded) { + if (onDone) { + onDone(); + } + if (shouldNotifyUponSave) { + Services.obs.notifyObservers(null, "telemetry-test-load-complete", null); + } + } }; function finishTelemetrySave(ok, stream) { diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index b96bbde06be2..ed7cc2f26f01 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -39,6 +39,8 @@ const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID"; const TELEMETRY_INTERVAL = 60000; // Delay before intializing telemetry (ms) const TELEMETRY_DELAY = 60000; +// Delay before initializing telemetry if we're testing (ms) +const TELEMETRY_TEST_DELAY = 100; // Seconds of idle time before pinging. // On idle-daily a gather-telemetry notification is fired, during it probes can @@ -218,6 +220,9 @@ TelemetryPing.prototype = { ret.savedPings = TelemetryFile.pingsLoaded; } + ret.pingsOverdue = TelemetryFile.pingsOverdue; + ret.pingsDiscarded = TelemetryFile.pingsDiscarded; + return ret; }, @@ -599,7 +604,9 @@ TelemetryPing.prototype = { popPayloads: function popPayloads(reason) { function payloadIter() { - yield this.getSessionPayloadAndSlug(reason); + if (reason != "overdue-flush") { + yield this.getSessionPayloadAndSlug(reason); + } let iterator = TelemetryFile.popPendingPings(reason); for (let data of iterator) { yield data; @@ -761,7 +768,7 @@ TelemetryPing.prototype = { /** * Initializes telemetry within a timer. If there is no PREF_SERVER set, don't turn on telemetry. */ - setup: function setup() { + setup: function setup(aTesting) { // Initialize some probes that are kept in their own modules this._thirdPartyCookies = new ThirdPartyCookieProbe(); this._thirdPartyCookies.init(); @@ -824,7 +831,17 @@ TelemetryPing.prototype = { { let success_histogram = Telemetry.getHistogramById("READ_SAVED_PING_SUCCESS"); success_histogram.add(success); - })); + }), () => + { + // If we have any TelemetryPings lying around, we'll be aggressive + // and try to send them all off ASAP. + if (TelemetryFile.pingsOverdue > 0) { + // It doesn't really matter what we pass to this.send as a reason, + // since it's never sent to the server. All that this.send does with + // the reason is check to make sure it's not a test-ping. + this.send("overdue-flush", this._server); + } + }); this.attachObservers(); this.gatherMemory(); @@ -832,7 +849,8 @@ TelemetryPing.prototype = { }); delete this._timer; } - this._timer.initWithCallback(timerCallback.bind(this), TELEMETRY_DELAY, + this._timer.initWithCallback(timerCallback.bind(this), + aTesting ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY, Ci.nsITimer.TYPE_ONE_SHOT); }, diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index c859b779c947..06d9292719cd 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -142,7 +142,7 @@ function decodeRequestPayload(request) { let observer = { buffer: "", onStreamComplete: function(loader, context, status, length, result) { - this.buffer = String.fromCharCode.apply(this, result); + this.buffer = String.fromCharCode.apply(this, result); } }; @@ -363,7 +363,7 @@ function runOldPingFileTest() { do_check_true(histogramsFile.exists()); let mtime = histogramsFile.lastModifiedTime; - histogramsFile.lastModifiedTime = mtime - 8 * 24 * 60 * 60 * 1000; // 8 days. + histogramsFile.lastModifiedTime = mtime - 14 * 24 * 60 * 60 * 1000; // 14 days. TelemetryPing.testLoadHistograms(histogramsFile, true); do_check_false(histogramsFile.exists()); } diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js new file mode 100644 index 000000000000..0ba9707d8f53 --- /dev/null +++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js @@ -0,0 +1,265 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + +/** + * This test case populates the profile with some fake stored + * pings, and checks that: + * + * 1) Pings that are considered "expired" are deleted and never sent. + * 2) Pings that are considered "overdue" trigger a send of all + * overdue and recent pings. + */ + +Components.utils.import("resource://gre/modules/Services.jsm"); + +// Get the TelemetryPing definitions directly so we can test it without going through xpcom. +// That gives us Cc, Ci, Cr and Cu, as well as a number of consts like PREF_ENABLED, +// and PREF_SERVER. +Services.scriptloader.loadSubScript("resource://gre/components/TelemetryPing.js"); + +Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/TelemetryFile.jsm"); + +// We increment TelemetryFile's MAX_PING_FILE_AGE and +// OVERDUE_PING_FILE_AGE by 1ms so that our test pings exceed +// those points in time. +const EXPIRED_PING_FILE_AGE = TelemetryFile.MAX_PING_FILE_AGE + 1; +const OVERDUE_PING_FILE_AGE = TelemetryFile.OVERDUE_PING_FILE_AGE + 1; + +const PING_SAVE_FOLDER = "saved-telemetry-pings"; +const PING_TIMEOUT_LENGTH = 1500; +const EXPIRED_PINGS = 5; +const OVERDUE_PINGS = 6; +const RECENT_PINGS = 4; + +const TOTAL_EXPECTED_PINGS = OVERDUE_PINGS + RECENT_PINGS; + +let gHttpServer = new HttpServer(); +let gCreatedPings = 0; +let gSeenPings = 0; + +/** + * Creates some TelemetryPings for the current session and + * saves them to disk. Each ping gets a unique ID slug based on + * an incrementor. + * + * @param aNum the number of pings to create. + * @param aAge the age in milliseconds to offset from now. A value + * of 10 would make the ping 10ms older than now, for + * example. + * @returns an Array with the created pings. + */ +function createSavedPings(aNum, aAge) { + // Create a TelemetryPing service that we can generate payloads from. + // Luckily, the TelemetryPing constructor does nothing that we need to + // clean up. + let pingService = new TelemetryPing(); + let pings = []; + let age = Date.now() - aAge; + for (let i = 0; i < aNum; ++i) { + let payload = pingService.getPayload(); + let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload }; + TelemetryFile.savePing(ping); + if (aAge) { + // savePing writes to the file synchronously, so we're good to + // modify the lastModifedTime now. + let file = getSaveFileForPing(ping); + file.lastModifiedTime = age; + } + gCreatedPings++; + pings.push(ping); + } + return pings; +} + +/** + * Deletes locally saved pings in aPings if they + * exist. + * + * @param aPings an Array of pings to delete. + */ +function clearPings(aPings) { + for (let ping of aPings) { + let file = getSaveFileForPing(ping); + if (file.exists()) { + file.remove(false); + } + } +} + +/** + * Returns a handle for the file that aPing should be + * stored in locally. + * + * @returns nsILocalFile + */ +function getSaveFileForPing(aPing) { + let file = Services.dirsvc.get("ProfD", Ci.nsILocalFile).clone(); + file.append(PING_SAVE_FOLDER); + file.append(aPing.slug); + return file; +} + +/** + * Wait for PING_TIMEOUT_LENGTH ms, and make sure we didn't receive + * TelemetryPings in that time. + * + * @returns Promise + */ +function assertReceivedNoPings() { + let deferred = Promise.defer(); + + do_timeout(PING_TIMEOUT_LENGTH, function() { + if (gSeenPings > 0) { + deferred.reject(); + } else { + deferred.resolve(); + } + }); + + return deferred.promise; +} + +/** + * Returns a Promise that rejects if the number of TelemetryPings + * received by the HttpServer is not equal to aExpectedNum. + * + * @param aExpectedNum the number of pings we expect to receive. + * @returns Promise + */ +function assertReceivedPings(aExpectedNum) { + let deferred = Promise.defer(); + + do_timeout(PING_TIMEOUT_LENGTH, function() { + if (gSeenPings == aExpectedNum) { + deferred.resolve(); + } else { + deferred.reject("Saw " + gSeenPings + " TelemetryPings, " + + "but expected " + aExpectedNum); + } + }) + + return deferred.promise; +} + +/** + * Throws if any pings in aPings is saved locally. + * + * @param aPings an Array of pings to check. + */ +function assertNotSaved(aPings) { + let saved = 0; + for (let ping of aPings) { + let file = getSaveFileForPing(ping); + if (file.exists()) { + saved++; + } + } + if (saved > 0) { + do_throw("Found " + saved + " unexpected saved pings."); + } +} + +/** + * Our handler function for the HttpServer that simply + * increments the gSeenPings global when it successfully + * receives and decodes a TelemetryPing payload. + * + * @param aRequest the HTTP request sent from HttpServer. + */ +function pingHandler(aRequest) { + gSeenPings++; +} + +/** + * Returns a Promise that resolves when gHttpServer has been + * successfully shut down. + * + * @returns Promise + */ +function stopHttpServer() { + let deferred = Promise.defer(); + gHttpServer.stop(function() { + deferred.resolve(); + }) + return deferred.promise; +} + +/** + * Teardown a TelemetryPing instance and clear out any pending + * pings to put as back in the starting state. + */ +function resetTelemetry(aPingService) { + aPingService.uninstall(); + // Quick and dirty way to clear TelemetryFile's pendingPings + // collection, and put it back in its initial state. + let gen = TelemetryFile.popPendingPings(); + for (let item of gen) {}; +} + +/** + * Creates and returns a TelemetryPing instance in "testing" + * mode. + */ +function startTelemetry() { + let service = new TelemetryPing(); + service.setup(true); + return service; +} + +function run_test() { + gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler); + gHttpServer.start(-1); + do_get_profile(); + Services.prefs.setBoolPref(PREF_ENABLED, true); + Services.prefs.setCharPref(PREF_SERVER, + "http://localhost:" + gHttpServer.identity.primaryPort); + run_next_test(); +} + +/** + * Test that pings that are considered too old are just chucked out + * immediately and never sent. + */ +add_task(function test_expired_pings_are_deleted() { + let expiredPings = createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE); + let pingService = startTelemetry(); + yield assertReceivedNoPings(); + assertNotSaved(expiredPings); + resetTelemetry(pingService); +}) + +/** + * Test that really recent pings are not sent on Telemetry initialization. + */ +add_task(function test_recent_pings_not_sent() { + let recentPings = createSavedPings(RECENT_PINGS); + let pingService = startTelemetry(); + yield assertReceivedNoPings(); + resetTelemetry(pingService); + clearPings(recentPings); +}); + +/** + * Create some recent, expired and overdue pings. The overdue pings should + * trigger a send of all recent and overdue pings, but the expired pings + * should just be deleted. + */ +add_task(function test_overdue_pings_trigger_send() { + let recentPings = createSavedPings(RECENT_PINGS); + let expiredPings = createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE); + let overduePings = createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE); + + let pingService = startTelemetry(); + yield assertReceivedPings(TOTAL_EXPECTED_PINGS); + + assertNotSaved(recentPings); + assertNotSaved(expiredPings); + assertNotSaved(overduePings); + resetTelemetry(pingService); +}) + +add_task(function teardown() { + yield stopHttpServer(); +}); diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.ini b/toolkit/components/telemetry/tests/unit/xpcshell.ini index 339219fedcdc..5a0d1b138cd3 100644 --- a/toolkit/components/telemetry/tests/unit/xpcshell.ini +++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini @@ -12,3 +12,5 @@ tail = [test_TelemetryStopwatch.js] [test_TelemetryPingBuildID.js] [test_ThirdPartyCookieProbe.js] +[test_TelemetrySendOldPings.js] +skip-if = debug == true From a6f4a5beea4fbcd862b272dddbb2896fbe4f0e2c Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Fri, 13 Dec 2013 12:01:30 -0800 Subject: [PATCH 091/459] Bug 926012 - Part 1: Clean up __proto__ setting semantics on native objects. (r=Waldo) --- js/src/js.msg | 1 + js/src/jsapi.cpp | 11 ++++++++++- js/src/jsobj.cpp | 28 ++++++++-------------------- js/src/jsobj.h | 5 ++++- js/src/jsobjinlines.h | 29 +++++++++++++++++++++++++++++ js/src/vm/GlobalObject.cpp | 17 +++++++---------- 6 files changed, 59 insertions(+), 32 deletions(-) diff --git a/js/src/js.msg b/js/src/js.msg index 072f28a8a3e6..75e0dce894c4 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -433,3 +433,4 @@ MSG_DEF(JSMSG_NO_EXPORT_NAME, 378, 0, JSEXN_SYNTAXERR, "missing export MSG_DEF(JSMSG_DECLARATION_AFTER_EXPORT, 379, 0, JSEXN_SYNTAXERR, "missing declaration after 'export' keyword") MSG_DEF(JSMSG_INVALID_PROTOTYPE, 380, 0, JSEXN_TYPEERR, "prototype field is not an object") MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_TO_UNSIZED, 381, 0, JSEXN_TYPEERR, "cannot create a handle to an unsized type") +MSG_DEF(JSMSG_SETPROTOTYPEOF_FAIL, 382, 0, JSEXN_TYPEERR, "[[SetPrototypeOf]] failed") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 882c65b08b8e..a731c49ca07e 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2374,7 +2374,16 @@ JS_SetPrototype(JSContext *cx, JS::Handle obj, JS::Handle CHECK_REQUEST(cx); assertSameCompartment(cx, obj, proto); - return SetClassAndProto(cx, obj, obj->getClass(), proto, false); + bool succeeded; + if (!JSObject::setProto(cx, obj, proto, &succeeded)) + return false; + + if (!succeeded) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SETPROTOTYPEOF_FAIL); + return false; + } + + return true; } JS_PUBLIC_API(JSObject *) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index b362833bff1a..9ed490c295ed 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1888,9 +1888,10 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *aArg, JSObject *bArg, const Class *bClass = b->getClass(); Rooted aProto(cx, a->getTaggedProto()); Rooted bProto(cx, b->getTaggedProto()); - if (!SetClassAndProto(cx, a, bClass, bProto, false)) + bool success; + if (!SetClassAndProto(cx, a, bClass, bProto, &success) || !success) return false; - if (!SetClassAndProto(cx, b, aClass, aProto, false)) + if (!SetClassAndProto(cx, b, aClass, aProto, &success) || !success) return false; if (a->tenuredSizeOfThis() == b->tenuredSizeOfThis()) @@ -2898,10 +2899,9 @@ static const ClassInitializerOp lazy_prototype_init[JSProto_LIMIT] = { bool js::SetClassAndProto(JSContext *cx, HandleObject obj, - const Class *clasp, Handle proto, bool checkForCycles) + const Class *clasp, Handle proto, + bool *succeeded) { - JS_ASSERT_IF(!checkForCycles, obj.get() != proto.raw()); - /* * Regenerate shapes for all of the scopes along the old prototype chain, * in case any entries were filled by looking up through obj. Stop when a @@ -2921,6 +2921,7 @@ js::SetClassAndProto(JSContext *cx, HandleObject obj, * * :XXX: bug 707717 make this code less brittle. */ + *succeeded = false; RootedObject oldproto(cx, obj); while (oldproto && oldproto->isNative()) { if (oldproto->hasSingletonType()) { @@ -2933,21 +2934,6 @@ js::SetClassAndProto(JSContext *cx, HandleObject obj, oldproto = oldproto->getProto(); } - if (checkForCycles) { - JS_ASSERT(!proto.isLazy()); - RootedObject obj2(cx); - for (obj2 = proto.toObjectOrNull(); obj2; ) { - if (obj2 == obj) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CYCLIC_VALUE, - js_proto_str); - return false; - } - - if (!JSObject::getProto(cx, obj2, &obj2)) - return false; - } - } - if (obj->hasSingletonType()) { /* * Just splice the prototype, but mark the properties as unknown for @@ -2956,6 +2942,7 @@ js::SetClassAndProto(JSContext *cx, HandleObject obj, if (!obj->splicePrototype(cx, clasp, proto)) return false; MarkTypeObjectUnknownProperties(cx, obj->type()); + *succeeded = true; return true; } @@ -2981,6 +2968,7 @@ js::SetClassAndProto(JSContext *cx, HandleObject obj, MarkTypeObjectUnknownProperties(cx, type, true); obj->setType(type); + *succeeded = true; return true; } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index f3fbbf9ef59b..1a003445390c 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -478,6 +478,9 @@ class JSObject : public js::ObjectImpl } static inline bool getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject protop); + // Returns false on error, success of operation in outparam. + static inline bool setProto(JSContext *cx, JS::HandleObject obj, + JS::HandleObject proto, bool *succeeded); // uninlinedSetType() is the same as setType(), but not inlined. inline void setType(js::types::TypeObject *newType); @@ -1600,7 +1603,7 @@ GetClassPrototypePure(GlobalObject *global, JSProtoKey protoKey); extern bool SetClassAndProto(JSContext *cx, HandleObject obj, - const Class *clasp, Handle proto, bool checkForCycles); + const Class *clasp, Handle proto, bool *succeeded); extern JSObject * NonNullObject(JSContext *cx, const Value &v); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 5704017a5eb4..492d5d244515 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -21,6 +21,8 @@ #include "jsgcinlines.h" #include "jsinferinlines.h" +#include "vm/ObjectImpl-inl.h" + /* static */ inline bool JSObject::setGenericAttributes(JSContext *cx, js::HandleObject obj, js::HandleId id, unsigned *attrsp) @@ -408,6 +410,33 @@ JSObject::getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject } } +/* static */ inline bool +JSObject::setProto(JSContext *cx, JS::HandleObject obj, JS::HandleObject proto, bool *succeeded) +{ + /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */ + bool extensible; + if (!JSObject::isExtensible(cx, obj, &extensible)) + return false; + if (!extensible) { + *succeeded = false; + return true; + } + + /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */ + js::RootedObject obj2(cx); + for (obj2 = proto; obj2; ) { + if (obj2 == obj) { + *succeeded = false; + return true; + } + + if (!JSObject::getProto(cx, obj2, &obj2)) + return false; + } + + return SetClassAndProto(cx, obj, obj->getClass(), proto, succeeded); +} + inline bool JSObject::isVarObj() { if (is()) diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 51948545ca95..a37680794248 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -137,15 +137,6 @@ ProtoSetterImpl(JSContext *cx, CallArgs args) Rooted obj(cx, &args.thisv().toObject()); - /* ES5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */ - bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) - return false; - if (!extensible) { - obj->reportNotExtensible(cx); - return false; - } - /* * Disallow mutating the [[Prototype]] of a proxy that wasn't simply * wrapping some other object. Also disallow it on ArrayBuffer objects, @@ -173,9 +164,15 @@ ProtoSetterImpl(JSContext *cx, CallArgs args) if (!CheckAccess(cx, obj, nid, JSAccessMode(JSACC_PROTO | JSACC_WRITE), &v, &dummy)) return false; - if (!SetClassAndProto(cx, obj, obj->getClass(), newProto, true)) + bool success; + if (!JSObject::setProto(cx, obj, newProto, &success)) return false; + if (!success) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SETPROTOTYPEOF_FAIL); + return false; + } + args.rval().setUndefined(); return true; } From 7f87debdf912a60f08b77e629c09d59581cebf86 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Fri, 13 Dec 2013 12:01:30 -0800 Subject: [PATCH 092/459] Bug 926012 - Part 2: Allow __proto__ sets on proxies. (r=Waldo) --- js/src/jsobjinlines.h | 6 ++++ js/src/jsproxy.cpp | 31 +++++++++++++---- js/src/jsproxy.h | 18 ++++++++++ .../ecma_5/extensions/proxy-__proto__.js | 15 ++------ js/src/vm/GlobalObject.cpp | 34 +++++-------------- 5 files changed, 61 insertions(+), 43 deletions(-) diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 492d5d244515..efa47d470594 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -413,6 +413,12 @@ JSObject::getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject /* static */ inline bool JSObject::setProto(JSContext *cx, JS::HandleObject obj, JS::HandleObject proto, bool *succeeded) { + /* Proxies live in their own little world. */ + if (obj->getTaggedProto().isLazy()) { + JS_ASSERT(obj->is()); + return js::Proxy::setPrototypeOf(cx, obj, proto, succeeded); + } + /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */ bool extensible; if (!JSObject::isExtensible(cx, obj, &extensible)) diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 52efb84f3d8c..670bad717d76 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -345,9 +345,17 @@ BaseProxyHandler::weakmapKeyDelegate(JSObject *proxy) bool BaseProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) { - // The default implementation here just uses proto of the proxy object. - protop.set(proxy->getTaggedProto().toObjectOrNull()); - return true; + MOZ_ASSUME_UNREACHABLE("Must override getPrototypeOf with lazy prototype."); +} + +bool +BaseProxyHandler::setPrototypeOf(JSContext *cx, HandleObject, HandleObject, bool *) +{ + // Disallow sets of protos on proxies with lazy protos, but no hook. + // This keeps us away from the footgun of having the first proto set opt + // you out of having dynamic protos altogether. + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SETPROTOTYPEOF_FAIL); + return false; } bool @@ -2305,7 +2313,7 @@ ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton; #define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \ JS_BEGIN_MACRO \ RootedObject proto(cx); \ - if (!handler->getPrototypeOf(cx, proxy, &proto)) \ + if (!JSObject::getProto(cx, proxy, &proto)) \ return false; \ if (!proto) \ return true; \ @@ -2543,7 +2551,9 @@ Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id return false; if (!hasOwn) { RootedObject proto(cx); - if (!handler->getPrototypeOf(cx, proxy, &proto)) + // Proxies might still use the normal prototype mechanism, rather than + // a hook, so query the engine proper + if (!JSObject::getProto(cx, proxy, &proto)) return false; if (proto) { Rooted desc(cx); @@ -2725,14 +2735,23 @@ Proxy::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandl return proxy->as().handler()->defaultValue(cx, proxy, hint, vp); } +JSObject * const Proxy::LazyProto = reinterpret_cast(0x1); + bool Proxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject proto) { + JS_ASSERT(proxy->getTaggedProto().isLazy()); JS_CHECK_RECURSION(cx, return false); return proxy->as().handler()->getPrototypeOf(cx, proxy, proto); } -JSObject * const Proxy::LazyProto = reinterpret_cast(0x1); +bool +Proxy::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) +{ + JS_ASSERT(proxy->getTaggedProto().isLazy()); + JS_CHECK_RECURSION(cx, return false); + return proxy->as().handler()->setPrototypeOf(cx, proxy, proto, bp); +} /* static */ bool Proxy::watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable) diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 0c6b4d4e2b5e..1888b660cead 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -47,6 +47,22 @@ class JS_FRIEND_API(Wrapper); * BaseProxyHandler provides implementations of the derived traps in terms of * the (pure virtual) fundamental traps. * + * In addition to the normal traps, there are two models for proxy prototype + * chains. First, proxies may opt to use the standard prototype mechanism used + * throughout the engine. To do so, simply pass a prototype to NewProxyObject() + * at creation time. All prototype accesses will then "just work" to treat the + * proxy as a "normal" object. Alternatively, if instead the proxy wishes to + * implement more complicated prototype semantics (if, for example, it wants to + * delegate the prototype lookup to a wrapped object), it may pass Proxy::LazyProto + * as the prototype at create time and opt in to the trapped prototype system, + * which guarantees that their trap will be called on any and every prototype + * chain access of the object. + * + * This system is implemented with two traps: {get,set}PrototypeOf. The default + * implementation of setPrototypeOf throws a TypeError. Since it is not possible + * to create an object without a sense of prototype chain, handler implementors + * must provide a getPrototypeOf trap if opting in to the dynamic prototype system. + * * To minimize code duplication, a set of abstract proxy handler classes is * provided, from which other handlers may inherit. These abstract classes * are organized in the following hierarchy: @@ -167,6 +183,7 @@ class JS_FRIEND_API(BaseProxyHandler) virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp); virtual void finalize(JSFreeOp *fop, JSObject *proxy); virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); + virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); // These two hooks must be overridden, or not overridden, in tandem -- no // overriding just one! @@ -294,6 +311,7 @@ class Proxy static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g); static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp); static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); + static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable); static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id); diff --git a/js/src/tests/ecma_5/extensions/proxy-__proto__.js b/js/src/tests/ecma_5/extensions/proxy-__proto__.js index 9bfe3e3ad545..480d43048f8f 100644 --- a/js/src/tests/ecma_5/extensions/proxy-__proto__.js +++ b/js/src/tests/ecma_5/extensions/proxy-__proto__.js @@ -34,20 +34,11 @@ function testProxy(creator, args, proto) assertEq(protoGetter.call(pobj), proto); // Attempt [[Prototype]] mutation - try - { - protoSetter.call(pobj); - throw new Error("should throw trying to mutate a proxy's [[Prototype]]"); - } - catch (e) - { - assertEq(e instanceof TypeError, true, - "expected TypeError, instead got: " + e); - } + protoSetter.call(pobj, null); // Check [[Prototype]] after attempted mutation - assertEq(Object.getPrototypeOf(pobj), proto); - assertEq(protoGetter.call(pobj), proto); + assertEq(Object.getPrototypeOf(pobj), null); + assertEq(protoGetter.call(pobj), null); } // Proxy object with non-null [[Prototype]] diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index a37680794248..aff455ebc495 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -68,7 +68,7 @@ ThrowTypeError(JSContext *cx, unsigned argc, Value *vp) } static bool -TestProtoGetterThis(HandleValue v) +TestProtoThis(HandleValue v) { return !v.isNullOrUndefined(); } @@ -76,7 +76,7 @@ TestProtoGetterThis(HandleValue v) static bool ProtoGetterImpl(JSContext *cx, CallArgs args) { - JS_ASSERT(TestProtoGetterThis(args.thisv())); + JS_ASSERT(TestProtoThis(args.thisv())); HandleValue thisv = args.thisv(); if (thisv.isPrimitive() && !BoxNonStrictThis(cx, args)) @@ -97,31 +97,17 @@ static bool ProtoGetter(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, TestProtoGetterThis, ProtoGetterImpl, args); + return CallNonGenericMethod(cx, TestProtoThis, ProtoGetterImpl, args); } namespace js { size_t sSetProtoCalled = 0; } // namespace js -static bool -TestProtoSetterThis(HandleValue v) -{ - if (v.isNullOrUndefined()) - return false; - - /* These will work as if on a boxed primitive; dumb, but whatever. */ - if (!v.isObject()) - return true; - - /* Otherwise, only accept non-proxies. */ - return !v.toObject().is(); -} - static bool ProtoSetterImpl(JSContext *cx, CallArgs args) { - JS_ASSERT(TestProtoSetterThis(args.thisv())); + JS_ASSERT(TestProtoThis(args.thisv())); HandleValue thisv = args.thisv(); if (thisv.isPrimitive()) { @@ -138,15 +124,13 @@ ProtoSetterImpl(JSContext *cx, CallArgs args) Rooted obj(cx, &args.thisv().toObject()); /* - * Disallow mutating the [[Prototype]] of a proxy that wasn't simply - * wrapping some other object. Also disallow it on ArrayBuffer objects, - * which due to their complicated delegate-object shenanigans can't easily + * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which + * due to their complicated delegate-object shenanigans can't easily * have a mutable [[Prototype]]. */ - if (obj->is() || obj->is()) { + if (obj->is()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, - "Object", "__proto__ setter", - obj->is() ? "Proxy" : "ArrayBuffer"); + "Object", "__proto__ setter", "ArrayBuffer"); return false; } @@ -181,7 +165,7 @@ static bool ProtoSetter(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, TestProtoSetterThis, ProtoSetterImpl, args); + return CallNonGenericMethod(cx, TestProtoThis, ProtoSetterImpl, args); } JSObject * From 46a65bdd10d9cc24fa5a5df83208e4e3b0b45e79 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Fri, 13 Dec 2013 12:01:30 -0800 Subject: [PATCH 093/459] Bug 926012 - Part 3: Convert wrappers to using dynamic prototype hooks. (r=bholley) --- dom/base/nsGlobalWindow.cpp | 30 +++---------- js/src/jsapi-tests/testBug604087.cpp | 8 ++-- js/src/jsproxy.cpp | 13 ++++++ js/src/jsproxy.h | 2 + js/src/jswrapper.cpp | 32 +++++++++---- js/src/jswrapper.h | 11 +++-- js/src/shell/js.cpp | 39 +++++++++++++--- js/xpconnect/wrappers/WaiveXrayWrapper.cpp | 7 +++ js/xpconnect/wrappers/WaiveXrayWrapper.h | 3 ++ js/xpconnect/wrappers/WrapperFactory.cpp | 52 ++++++++++------------ js/xpconnect/wrappers/WrapperFactory.h | 1 + js/xpconnect/wrappers/XrayWrapper.cpp | 51 +++++++++++++++++++++ js/xpconnect/wrappers/XrayWrapper.h | 5 +++ 13 files changed, 178 insertions(+), 76 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index b8bf96e9298a..dbcc1f5078fe 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1011,11 +1011,7 @@ static JSObject* NewOuterWindowProxy(JSContext *cx, JS::Handle parent, bool isChrome) { JSAutoCompartment ac(cx, parent); - JS::Rooted proto(cx); - if (!js::GetObjectProto(cx, parent, &proto)) - return nullptr; - - JSObject *obj = js::Wrapper::New(cx, parent, proto, parent, + JSObject *obj = js::Wrapper::New(cx, parent, parent, isChrome ? &nsChromeOuterWindowProxy::singleton : &nsOuterWindowProxy::singleton); @@ -2103,26 +2099,11 @@ nsGlobalWindow::CreateOuterObject(nsGlobalWindow* aNewInner) js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this))); - return SetOuterObject(cx, outer); -} - -nsresult -nsGlobalWindow::SetOuterObject(JSContext* aCx, JS::Handle aOuterObject) -{ - JSAutoCompartment ac(aCx, aOuterObject); + JSAutoCompartment ac(cx, outer); // Inform the nsJSContext, which is the canonical holder of the outer. MOZ_ASSERT(IsOuterWindow()); - mContext->SetWindowProxy(aOuterObject); - - // Set up the prototype for the outer object. - JS::Rooted inner(aCx, JS_GetParent(aOuterObject)); - JS::Rooted proto(aCx); - if (!JS_GetPrototype(aCx, inner, &proto)) { - return NS_ERROR_FAILURE; - } - JS_SetPrototype(aCx, aOuterObject, proto); - + mContext->SetWindowProxy(outer); return NS_OK; } @@ -2457,8 +2438,9 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject); JS::Rooted obj(cx, mJSObject); - rv = SetOuterObject(cx, obj); - NS_ENSURE_SUCCESS(rv, rv); + + // Inform the nsJSContext, which is the canonical holder of the outer. + mContext->SetWindowProxy(obj); NS_ASSERTION(!JS_IsExceptionPending(cx), "We might overwrite a pending exception!"); diff --git a/js/src/jsapi-tests/testBug604087.cpp b/js/src/jsapi-tests/testBug604087.cpp index 63b4efb4581d..ebd791286685 100644 --- a/js/src/jsapi-tests/testBug604087.cpp +++ b/js/src/jsapi-tests/testBug604087.cpp @@ -60,13 +60,12 @@ static JSObject * Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj, JS::HandleObject proto, JS::HandleObject parent, unsigned flags) { - return js::Wrapper::New(cx, obj, proto, parent, &js::CrossCompartmentWrapper::singleton); + return js::Wrapper::New(cx, obj, parent, &js::CrossCompartmentWrapper::singleton); } BEGIN_TEST(testBug604087) { - JS::RootedObject outerObj(cx, js::Wrapper::New(cx, global, global->getProto(), global, - &OuterWrapper::singleton)); + JS::RootedObject outerObj(cx, js::Wrapper::New(cx, global, global, &OuterWrapper::singleton)); JS::RootedObject compartment2(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook)); JS::RootedObject compartment3(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook)); JS::RootedObject compartment4(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook)); @@ -87,8 +86,7 @@ BEGIN_TEST(testBug604087) JS::RootedObject next(cx); { JSAutoCompartment ac(cx, compartment2); - next = js::Wrapper::New(cx, compartment2, compartment2->getProto(), compartment2, - &OuterWrapper::singleton); + next = js::Wrapper::New(cx, compartment2, compartment2, &OuterWrapper::singleton); CHECK(next); } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 670bad717d76..b1d4dcdd6605 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -516,6 +516,19 @@ DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandle return true; } +bool +DirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) +{ + RootedObject target(cx, proxy->as().target()); + return JSObject::getProto(cx, target, protop); +} + +bool +DirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) +{ + RootedObject target(cx, proxy->as().target()); + return JSObject::setProto(cx, target, proto, bp); +} bool DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 1888b660cead..12cc9ceab9d1 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -252,6 +252,8 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler CallArgs args) MOZ_OVERRIDE; virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) MOZ_OVERRIDE; + virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); + virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index e28a33acb16b..7a2e1c2c1587 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -39,7 +39,7 @@ Wrapper::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHan } JSObject * -Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler) +Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler) { JS_ASSERT(parent); @@ -48,7 +48,7 @@ Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wr RootedValue priv(cx, ObjectValue(*obj)); ProxyOptions options; options.setCallable(obj->isCallable()); - return NewProxyObject(cx, handler, priv, proto, parent, options); + return NewProxyObject(cx, handler, priv, Proxy::LazyProto, parent, options); } JSObject * @@ -141,7 +141,8 @@ js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject { // Allow wrapping outer window proxies. JS_ASSERT(!obj->is() || obj->getClass()->ext.innerObject); - return Wrapper::New(cx, obj, wrappedProto, parent, &CrossCompartmentWrapper::singleton); + JS_ASSERT(wrappedProto == Proxy::LazyProto); + return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton); } ErrorCopier::~ErrorCopier() @@ -578,11 +579,6 @@ bool CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper, MutableHandleObject protop) { - if (!wrapper->getTaggedProto().isLazy()) { - protop.set(wrapper->getTaggedProto().toObjectOrNull()); - return true; - } - { RootedObject wrapped(cx, wrappedObject(wrapper)); AutoCompartment call(cx, wrapped); @@ -595,6 +591,17 @@ CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper, return cx->compartment()->wrap(cx, protop); } +bool +CrossCompartmentWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper, + HandleObject proto, bool *bp) +{ + RootedObject protoCopy(cx, proto); + PIERCE(cx, wrapper, + cx->compartment()->wrap(cx, &protoCopy), + Wrapper::setPrototypeOf(cx, wrapper, protoCopy, bp), + NOTHING); +} + CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u); /* Security wrappers. */ @@ -646,6 +653,15 @@ SecurityWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeIm return false; } +template +bool +SecurityWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper, + HandleObject proto, bool *bp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNWRAP_DENIED); + return false; +} + // For security wrappers, we run the DefaultValue algorithm on the wrapper // itself, which means that the existing security policy on operations like // toString() will take effect and do the right thing here. diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 5d68b8d17a69..08f57ef03c0a 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -49,8 +49,7 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler void setSafeToUnwrap(bool safe) { mSafeToUnwrap = safe; } bool isSafeToUnwrap() { return mSafeToUnwrap; } - static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, - JSObject *parent, Wrapper *handler); + static JSObject *New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler); static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler); @@ -120,7 +119,10 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, MutableHandleValue vp) MOZ_OVERRIDE; - virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); + virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, + MutableHandleObject protop) MOZ_OVERRIDE; + virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, + bool *bp) MOZ_OVERRIDE; static CrossCompartmentWrapper singleton; static CrossCompartmentWrapper singletonWithPrototype; @@ -155,6 +157,9 @@ class JS_FRIEND_API(SecurityWrapper) : public Base virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandle desc) MOZ_OVERRIDE; + virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, + bool *bp) MOZ_OVERRIDE; + virtual bool watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable) MOZ_OVERRIDE; virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id) MOZ_OVERRIDE; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 2d526784902c..96d3553a7ebc 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3743,6 +3743,34 @@ ThisFilename(JSContext *cx, unsigned argc, Value *vp) return true; } +/* + * Internal class for testing hasPrototype easily. + * Uses passed in prototype instead of target's. + */ +class WrapperWithProto : public Wrapper +{ + public: + explicit WrapperWithProto(unsigned flags) + : Wrapper(flags, true) + { } + + static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, + Wrapper *handler); +}; + +/* static */ JSObject * +WrapperWithProto::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, + Wrapper *handler) +{ + JS_ASSERT(parent); + AutoMarkInDeadZone amd(cx->zone()); + + RootedValue priv(cx, ObjectValue(*obj)); + ProxyOptions options; + options.setCallable(obj->isCallable()); + return NewProxyObject(cx, handler, priv, proto, parent, options); +} + static bool Wrap(JSContext *cx, unsigned argc, jsval *vp) { @@ -3753,10 +3781,7 @@ Wrap(JSContext *cx, unsigned argc, jsval *vp) } RootedObject obj(cx, JSVAL_TO_OBJECT(v)); - RootedObject proto(cx); - if (!JSObject::getProto(cx, obj, &proto)) - return false; - JSObject *wrapped = Wrapper::New(cx, obj, proto, &obj->global(), + JSObject *wrapped = Wrapper::New(cx, obj, &obj->global(), &Wrapper::singleton); if (!wrapped) return false; @@ -3779,9 +3804,9 @@ WrapWithProto(JSContext *cx, unsigned argc, jsval *vp) return false; } - JSObject *wrapped = Wrapper::New(cx, &obj.toObject(), proto.toObjectOrNull(), - &obj.toObject().global(), - &Wrapper::singletonWithPrototype); + JSObject *wrapped = WrapperWithProto::New(cx, &obj.toObject(), proto.toObjectOrNull(), + &obj.toObject().global(), + &Wrapper::singletonWithPrototype); if (!wrapped) return false; diff --git a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp index 3f5a068a4a1c..bc5d3b419e08 100644 --- a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp +++ b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp @@ -91,4 +91,11 @@ WaiveXrayWrapper::nativeCall(JSContext *cx, JS::IsAcceptableThis test, WrapperFactory::WaiveXrayAndWrap(cx, args.rval()); } +bool +WaiveXrayWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper, MutableHandleObject protop) +{ + return CrossCompartmentWrapper::getPrototypeOf(cx, wrapper, protop) && + (!protop || WrapperFactory::WaiveXrayAndWrap(cx, protop)); +} + } diff --git a/js/xpconnect/wrappers/WaiveXrayWrapper.h b/js/xpconnect/wrappers/WaiveXrayWrapper.h index ea0337e9ce76..985c891423cf 100644 --- a/js/xpconnect/wrappers/WaiveXrayWrapper.h +++ b/js/xpconnect/wrappers/WaiveXrayWrapper.h @@ -38,6 +38,9 @@ class WaiveXrayWrapper : public js::CrossCompartmentWrapper { virtual bool nativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl, JS::CallArgs args) MOZ_OVERRIDE; + virtual bool getPrototypeOf(JSContext *cx, JS::Handle wrapper, + JS::MutableHandle protop) MOZ_OVERRIDE; + static WaiveXrayWrapper singleton; }; diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index f2581ad9c70b..2c8c13eb8c9e 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -75,18 +75,8 @@ WrapperFactory::CreateXrayWaiver(JSContext *cx, HandleObject obj) MOZ_ASSERT(!GetXrayWaiver(obj)); XPCWrappedNativeScope *scope = GetObjectScope(obj); - // Get a waiver for the proto. - RootedObject proto(cx); - if (!js::GetObjectProto(cx, obj, &proto)) - return nullptr; - if (proto && !(proto = WaiveXray(cx, proto))) - return nullptr; - - // Create the waiver. JSAutoCompartment ac(cx, obj); - if (!JS_WrapObject(cx, &proto)) - return nullptr; - JSObject *waiver = Wrapper::New(cx, obj, proto, + JSObject *waiver = Wrapper::New(cx, obj, JS_GetGlobalForObject(cx, obj), &XrayWaiver); if (!waiver) @@ -409,10 +399,6 @@ WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, XrayType xrayType = GetXrayType(obj); bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG; - // By default we use the wrapped proto of the underlying object as the - // prototype for our wrapper, but we may select something different below. - RootedObject proxyProto(cx, wrappedProto); - Wrapper *wrapper; CompartmentPrivate *targetdata = EnsureCompartmentPrivate(target); @@ -498,10 +484,10 @@ WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); - if (existing && proxyProto == wrappedProto) + if (existing) return Wrapper::Renew(cx, existing, obj, wrapper); - return Wrapper::New(cx, obj, proxyProto, parent, wrapper); + return Wrapper::New(cx, obj, parent, wrapper); } JSObject * @@ -549,10 +535,22 @@ WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp) if (vp.isPrimitive()) return JS_WrapValue(cx, vp); - JSObject *obj = js::UncheckedUnwrap(&vp.toObject()); + RootedObject obj(cx, &vp.toObject()); + if (!WaiveXrayAndWrap(cx, &obj)) + return false; + + vp.setObject(*obj); + return true; +} + +bool +WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleObject argObj) +{ + MOZ_ASSERT(argObj); + RootedObject obj(cx, js::UncheckedUnwrap(argObj)); MOZ_ASSERT(!js::IsInnerObject(obj)); if (js::IsObjectInContextCompartment(obj, cx)) { - vp.setObject(*obj); + argObj.set(obj); return true; } @@ -570,24 +568,23 @@ WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp) if (!obj) return false; - vp.setObject(*obj); - return JS_WrapValue(cx, vp); + if (!JS_WrapObject(cx, &obj)) + return false; + argObj.set(obj); + return true; } JSObject * WrapperFactory::WrapSOWObject(JSContext *cx, JSObject *objArg) { RootedObject obj(cx, objArg); - RootedObject proto(cx); // If we're not allowing XBL scopes, that means we're running as a remote // XUL domain, in which we can't have SOWs. We should never be called in // that case. MOZ_ASSERT(xpc::AllowXBLScope(js::GetContextCompartment(cx))); - if (!JS_GetPrototype(cx, obj, &proto)) - return nullptr; JSObject *wrapperObj = - Wrapper::New(cx, obj, proto, JS_GetGlobalForObject(cx, obj), + Wrapper::New(cx, obj, JS_GetGlobalForObject(cx, obj), &FilteringWrapper::singleton); return wrapperObj; @@ -603,11 +600,8 @@ WrapperFactory::IsComponentsObject(JSObject *obj) JSObject * WrapperFactory::WrapComponentsObject(JSContext *cx, HandleObject obj) { - RootedObject proto(cx); - if (!JS_GetPrototype(cx, obj, &proto)) - return nullptr; JSObject *wrapperObj = - Wrapper::New(cx, obj, proto, JS_GetGlobalForObject(cx, obj), + Wrapper::New(cx, obj, JS_GetGlobalForObject(cx, obj), &FilteringWrapper::singleton); return wrapperObj; diff --git a/js/xpconnect/wrappers/WrapperFactory.h b/js/xpconnect/wrappers/WrapperFactory.h index cfdc28e99b3b..1aab6c7e674a 100644 --- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -64,6 +64,7 @@ class WrapperFactory { // Wrap wrapped object into a waiver wrapper and then re-wrap it. static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleValue vp); + static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleObject object); // Wrap a (same compartment) object in a SOW. static JSObject *WrapSOWObject(JSContext *cx, JSObject *obj); diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 7f2021227c28..2610670ab612 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -304,6 +304,7 @@ enum ExpandoSlots { JSSLOT_EXPANDO_NEXT = 0, JSSLOT_EXPANDO_ORIGIN, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL, + JSSLOT_EXPANDO_PROTOTYPE, JSSLOT_EXPANDO_COUNT }; @@ -1792,6 +1793,56 @@ XrayWrapper::defaultValue(JSContext *cx, HandleObject wrapper, return js::DefaultValue(cx, wrapper, hint, vp); } +template +bool +XrayWrapper::getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, + JS::MutableHandleObject protop) +{ + RootedObject target(cx, Traits::getTargetObject(wrapper)); + RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); + + // We want to keep the Xray's prototype distinct from that of content, but only + // if there's been a set. If there's not an expando, or the expando slot is |undefined|, + // hand back content's proto, appropriately wrapped. + // + // NB: Our baseclass's getPrototypeOf() will appropriately wrap its return value, so there is + // no need for us to. + + if (!expando) + return Base::getPrototypeOf(cx, wrapper, protop); + + RootedValue v(cx); + { + JSAutoCompartment ac(cx, expando); + v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE); + } + + if (v.isUndefined()) + return Base::getPrototypeOf(cx, wrapper, protop); + + protop.set(v.toObjectOrNull()); + return JS_WrapObject(cx, protop); +} + +template +bool +XrayWrapper::setPrototypeOf(JSContext *cx, JS::HandleObject wrapper, + JS::HandleObject proto, bool *bp) +{ + RootedObject target(cx, Traits::getTargetObject(wrapper)); + RootedObject expando(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, target)); + + // The expando lives in the target's compartment, so do our installation there. + JSAutoCompartment ac(cx, target); + + RootedValue v(cx, ObjectValue(*proto)); + if (!JS_WrapValue(cx, &v)) + return false; + JS_SetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE, v); + *bp = true; + return true; +} + /* * The Permissive / Security variants should be used depending on whether the diff --git a/js/xpconnect/wrappers/XrayWrapper.h b/js/xpconnect/wrappers/XrayWrapper.h index b5332c277d52..465a5c66804f 100644 --- a/js/xpconnect/wrappers/XrayWrapper.h +++ b/js/xpconnect/wrappers/XrayWrapper.h @@ -106,6 +106,11 @@ class XrayWrapper : public Base { JSType hint, JS::MutableHandleValue vp) MOZ_OVERRIDE; + virtual bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, + JS::MutableHandleObject protop) MOZ_OVERRIDE; + virtual bool setPrototypeOf(JSContext *cx, JS::HandleObject wrapper, + JS::HandleObject proto, bool *bp) MOZ_OVERRIDE; + static XrayWrapper singleton; private: From fad917b723a0dd9bf77f4e0ee3a9279311c9c5c9 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Fri, 13 Dec 2013 15:15:07 -0500 Subject: [PATCH 094/459] Bug 948406 - Append some zeros to EGL attrib lists to work around a bug in Android/B2G emulator - r=vladv --- gfx/gl/GLContextProviderEGL.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index ba6b9f2c46e2..570e7363f4ce 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -134,16 +134,26 @@ namespace gl { static bool CreateConfig(EGLConfig* aConfig); +// append three zeros at the end of attribs list to work around +// EGL implementation bugs that iterate until they find 0, instead of +// EGL_NONE. See bug 948406. +#define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \ + LOCAL_EGL_NONE, 0, 0, 0 + +static EGLint gTerminationAttribs[] = { + EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS +}; + static EGLint gContextAttribs[] = { LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2, - LOCAL_EGL_NONE + EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS }; static EGLint gContextAttribsRobustness[] = { LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2, //LOCAL_EGL_CONTEXT_ROBUST_ACCESS_EXT, LOCAL_EGL_TRUE, LOCAL_EGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_EXT, LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT, - LOCAL_EGL_NONE + EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS }; static int @@ -576,7 +586,9 @@ protected: pbattrs.AppendElement(bindToTextureFormat); } - pbattrs.AppendElement(LOCAL_EGL_NONE); + for (size_t i = 0; i < MOZ_ARRAY_LENGTH(gTerminationAttribs); i++) { + pbattrs.AppendElement(gTerminationAttribs[i]); + } surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, &pbattrs[0]); if (!surface) { @@ -609,7 +621,7 @@ GLContextEGL::ResizeOffscreen(const gfxIntSize& aNewSize) static const EGLint kEGLConfigAttribsOffscreenPBuffer[] = { LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_PBUFFER_BIT, LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, - LOCAL_EGL_NONE + EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS }; static const EGLint kEGLConfigAttribsRGB16[] = { @@ -619,7 +631,7 @@ static const EGLint kEGLConfigAttribsRGB16[] = { LOCAL_EGL_GREEN_SIZE, 6, LOCAL_EGL_BLUE_SIZE, 5, LOCAL_EGL_ALPHA_SIZE, 0, - LOCAL_EGL_NONE + EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS }; static const EGLint kEGLConfigAttribsRGB24[] = { @@ -629,7 +641,7 @@ static const EGLint kEGLConfigAttribsRGB24[] = { LOCAL_EGL_GREEN_SIZE, 8, LOCAL_EGL_BLUE_SIZE, 8, LOCAL_EGL_ALPHA_SIZE, 0, - LOCAL_EGL_NONE + EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS }; static const EGLint kEGLConfigAttribsRGBA32[] = { @@ -642,7 +654,7 @@ static const EGLint kEGLConfigAttribsRGBA32[] = { #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 LOCAL_EGL_FRAMEBUFFER_TARGET_ANDROID, LOCAL_EGL_TRUE, #endif - LOCAL_EGL_NONE + EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS }; static bool @@ -894,3 +906,4 @@ GLContextProviderEGL::Shutdown() } /* namespace gl */ } /* namespace mozilla */ +#undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS From e56e44cd695867961623f70591d37e4655af2d55 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:46 -0800 Subject: [PATCH 095/459] Bug 949668 - SpiderMonkey: Rename MoveResolver::Move to MoveOp. r=jandem --- js/src/jit/CodeGenerator.cpp | 4 +- js/src/jit/MoveResolver.cpp | 2 +- js/src/jit/MoveResolver.h | 286 +++++++++--------- js/src/jit/arm/CodeGenerator-arm.cpp | 2 - js/src/jit/arm/CodeGenerator-arm.h | 2 +- js/src/jit/arm/MacroAssembler-arm.cpp | 12 +- js/src/jit/arm/MacroAssembler-arm.h | 5 +- js/src/jit/arm/MoveEmitter-arm.cpp | 12 +- js/src/jit/arm/MoveEmitter-arm.h | 9 +- js/src/jit/arm/Trampoline-arm.cpp | 2 - .../jit/shared/CodeGenerator-x86-shared.cpp | 2 - js/src/jit/shared/CodeGenerator-x86-shared.h | 2 +- js/src/jit/shared/MoveEmitter-x86-shared.cpp | 16 +- js/src/jit/shared/MoveEmitter-x86-shared.h | 7 +- js/src/jit/x64/MacroAssembler-x64.cpp | 6 +- js/src/jit/x64/MacroAssembler-x64.h | 3 - js/src/jit/x64/Trampoline-x64.cpp | 2 - js/src/jit/x86/MacroAssembler-x86.cpp | 4 +- js/src/jit/x86/MacroAssembler-x86.h | 3 - js/src/jit/x86/Trampoline-x86.cpp | 2 - 20 files changed, 178 insertions(+), 205 deletions(-) diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 09fa15507436..ba965f48f66a 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1191,9 +1191,7 @@ CodeGenerator::visitMoveGroup(LMoveGroup *group) JS_ASSERT(!from->isConstant()); JS_ASSERT(from->isDouble() == to->isDouble()); - MoveResolver::Move::Kind kind = from->isDouble() - ? MoveResolver::Move::DOUBLE - : MoveResolver::Move::GENERAL; + MoveOp::Kind kind = from->isDouble() ? MoveOp::DOUBLE : MoveOp::GENERAL; if (!resolver.addMove(toMoveOperand(from), toMoveOperand(to), kind)) return false; diff --git a/js/src/jit/MoveResolver.cpp b/js/src/jit/MoveResolver.cpp index 522bd3b17470..4354f24d8ef5 100644 --- a/js/src/jit/MoveResolver.cpp +++ b/js/src/jit/MoveResolver.cpp @@ -21,7 +21,7 @@ MoveResolver::resetState() } bool -MoveResolver::addMove(const MoveOperand &from, const MoveOperand &to, Move::Kind kind) +MoveResolver::addMove(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind) { // Assert that we're not doing no-op moves. JS_ASSERT(!(from == to)); diff --git a/js/src/jit/MoveResolver.h b/js/src/jit/MoveResolver.h index 601113b8ac01..bbe6f1201a32 100644 --- a/js/src/jit/MoveResolver.h +++ b/js/src/jit/MoveResolver.h @@ -14,156 +14,156 @@ namespace js { namespace jit { +// This is similar to Operand, but carries more information. We're also not +// guaranteed that Operand looks like this on all ISAs. +class MoveOperand +{ + enum Kind { + REG, + FLOAT_REG, + ADDRESS, + FLOAT_ADDRESS, + EFFECTIVE_ADDRESS + }; + + Kind kind_; + uint32_t code_; + int32_t disp_; + + public: + enum AddressKind { + MEMORY = ADDRESS, + EFFECTIVE = EFFECTIVE_ADDRESS, + FLOAT = FLOAT_ADDRESS + }; + + MoveOperand() + { } + explicit MoveOperand(const Register ®) : kind_(REG), code_(reg.code()) + { } + explicit MoveOperand(const FloatRegister ®) : kind_(FLOAT_REG), code_(reg.code()) + { } + MoveOperand(const Register ®, int32_t disp, AddressKind addrKind = MEMORY) + : kind_((Kind) addrKind), + code_(reg.code()), + disp_(disp) + { + // With a zero offset, this is a plain reg-to-reg move. + if (disp == 0 && addrKind == EFFECTIVE) + kind_ = REG; + } + MoveOperand(const MoveOperand &other) + : kind_(other.kind_), + code_(other.code_), + disp_(other.disp_) + { } + bool isFloatReg() const { + return kind_ == FLOAT_REG; + } + bool isGeneralReg() const { + return kind_ == REG; + } + bool isDouble() const { + return kind_ == FLOAT_REG || kind_ == FLOAT_ADDRESS; + } + bool isMemory() const { + return kind_ == ADDRESS; + } + bool isFloatAddress() const { + return kind_ == FLOAT_ADDRESS; + } + bool isEffectiveAddress() const { + return kind_ == EFFECTIVE_ADDRESS; + } + Register reg() const { + JS_ASSERT(isGeneralReg()); + return Register::FromCode(code_); + } + FloatRegister floatReg() const { + JS_ASSERT(isFloatReg()); + return FloatRegister::FromCode(code_); + } + Register base() const { + JS_ASSERT(isMemory() || isEffectiveAddress() || isFloatAddress()); + return Register::FromCode(code_); + } + int32_t disp() const { + return disp_; + } + + bool operator ==(const MoveOperand &other) const { + if (kind_ != other.kind_) + return false; + if (code_ != other.code_) + return false; + if (isMemory() || isEffectiveAddress()) + return disp_ == other.disp_; + return true; + } + bool operator !=(const MoveOperand &other) const { + return !operator==(other); + } +}; + +// This represents a move operation. +class MoveOp +{ + protected: + MoveOperand from_; + MoveOperand to_; + bool cycle_; + + public: + enum Kind { + GENERAL, + DOUBLE + }; + + protected: + Kind kind_; + + public: + MoveOp() + { } + MoveOp(const MoveOp &other) + : from_(other.from_), + to_(other.to_), + cycle_(other.cycle_), + kind_(other.kind_) + { } + MoveOp(const MoveOperand &from, const MoveOperand &to, Kind kind, bool cycle = false) + : from_(from), + to_(to), + cycle_(cycle), + kind_(kind) + { } + + bool inCycle() const { + return cycle_; + } + const MoveOperand &from() const { + return from_; + } + const MoveOperand &to() const { + return to_; + } + Kind kind() const { + return kind_; + } +}; + class MoveResolver { - public: - // This is similar to Operand, but carries more information. We're also not - // guaranteed that Operand looks like this on all ISAs. - class MoveOperand - { - enum Kind { - REG, - FLOAT_REG, - ADDRESS, - FLOAT_ADDRESS, - EFFECTIVE_ADDRESS - }; - - Kind kind_; - uint32_t code_; - int32_t disp_; - - public: - enum AddressKind { - MEMORY = ADDRESS, - EFFECTIVE = EFFECTIVE_ADDRESS, - FLOAT = FLOAT_ADDRESS - }; - - MoveOperand() - { } - explicit MoveOperand(const Register ®) : kind_(REG), code_(reg.code()) - { } - explicit MoveOperand(const FloatRegister ®) : kind_(FLOAT_REG), code_(reg.code()) - { } - MoveOperand(const Register ®, int32_t disp, AddressKind addrKind = MEMORY) - : kind_((Kind) addrKind), - code_(reg.code()), - disp_(disp) - { - // With a zero offset, this is a plain reg-to-reg move. - if (disp == 0 && addrKind == EFFECTIVE) - kind_ = REG; - } - MoveOperand(const MoveOperand &other) - : kind_(other.kind_), - code_(other.code_), - disp_(other.disp_) - { } - bool isFloatReg() const { - return kind_ == FLOAT_REG; - } - bool isGeneralReg() const { - return kind_ == REG; - } - bool isDouble() const { - return kind_ == FLOAT_REG || kind_ == FLOAT_ADDRESS; - } - bool isMemory() const { - return kind_ == ADDRESS; - } - bool isFloatAddress() const { - return kind_ == FLOAT_ADDRESS; - } - bool isEffectiveAddress() const { - return kind_ == EFFECTIVE_ADDRESS; - } - Register reg() const { - JS_ASSERT(isGeneralReg()); - return Register::FromCode(code_); - } - FloatRegister floatReg() const { - JS_ASSERT(isFloatReg()); - return FloatRegister::FromCode(code_); - } - Register base() const { - JS_ASSERT(isMemory() || isEffectiveAddress() || isFloatAddress()); - return Register::FromCode(code_); - } - int32_t disp() const { - return disp_; - } - - bool operator ==(const MoveOperand &other) const { - if (kind_ != other.kind_) - return false; - if (code_ != other.code_) - return false; - if (isMemory() || isEffectiveAddress()) - return disp_ == other.disp_; - return true; - } - bool operator !=(const MoveOperand &other) const { - return !operator==(other); - } - }; - - class Move - { - protected: - MoveOperand from_; - MoveOperand to_; - bool cycle_; - - public: - enum Kind { - GENERAL, - DOUBLE - }; - - protected: - Kind kind_; - - public: - Move() - { } - Move(const Move &other) - : from_(other.from_), - to_(other.to_), - cycle_(other.cycle_), - kind_(other.kind_) - { } - Move(const MoveOperand &from, const MoveOperand &to, Kind kind, bool cycle = false) - : from_(from), - to_(to), - cycle_(cycle), - kind_(kind) - { } - - bool inCycle() const { - return cycle_; - } - const MoveOperand &from() const { - return from_; - } - const MoveOperand &to() const { - return to_; - } - Kind kind() const { - return kind_; - } - }; - private: struct PendingMove - : public Move, + : public MoveOp, public TempObject, public InlineListNode { PendingMove() { } PendingMove(const MoveOperand &from, const MoveOperand &to, Kind kind) - : Move(from, to, kind, false) + : MoveOp(from, to, kind, false) { } void setInCycle() { @@ -178,7 +178,7 @@ class MoveResolver private: // Moves that are definitely unblocked (constants to registers). These are // emitted last. - js::Vector orderedMoves_; + js::Vector orderedMoves_; bool hasCycles_; TempObjectPool movePool_; @@ -200,13 +200,13 @@ class MoveResolver // // After calling addMove() for each parallel move, resolve() performs the // cycle resolution algorithm. Calling addMove() again resets the resolver. - bool addMove(const MoveOperand &from, const MoveOperand &to, Move::Kind kind); + bool addMove(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind); bool resolve(); size_t numMoves() const { return orderedMoves_.length(); } - const Move &getMove(size_t i) const { + const MoveOp &getMove(size_t i) const { return orderedMoves_[i]; } bool hasCycles() const { diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index ae1984e96efc..c53edb5bf8c8 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1020,8 +1020,6 @@ CodeGeneratorARM::visitPowHalfD(LPowHalfD *ins) return true; } -typedef MoveResolver::MoveOperand MoveOperand; - MoveOperand CodeGeneratorARM::toMoveOperand(const LAllocation *a) const { diff --git a/js/src/jit/arm/CodeGenerator-arm.h b/js/src/jit/arm/CodeGenerator-arm.h index a916315a485b..08c90dfd8920 100644 --- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -43,7 +43,7 @@ class CodeGeneratorARM : public CodeGeneratorShared return ToOperand(def->output()); } - MoveResolver::MoveOperand toMoveOperand(const LAllocation *a) const; + MoveOperand toMoveOperand(const LAllocation *a) const; bool bailoutIf(Assembler::Condition condition, LSnapshot *snapshot); bool bailoutFrom(Label *label, LSnapshot *snapshot); diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index fe039c5ec771..512aad71c061 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3518,26 +3518,26 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from) FloatRegister fr; if (GetFloatArgReg(usedIntSlots_, usedFloatSlots_, &fr)) { if (!from.isFloatReg() || from.floatReg() != fr) { - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(fr), Move::DOUBLE); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(fr), MoveOp::DOUBLE); } // else nothing to do; the value is in the right register already } else { // If (and only if) the integer registers have started spilling, do we // need to take the double register's alignment into account uint32_t disp = GetFloatArgStackDisp(usedIntSlots_, usedFloatSlots_, &padding_); - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), Move::DOUBLE); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), MoveOp::DOUBLE); } usedFloatSlots_++; } else { Register r; if (GetIntArgReg(usedIntSlots_, usedFloatSlots_, &r)) { if (!from.isGeneralReg() || from.reg() != r) { - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(r), Move::GENERAL); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(r), MoveOp::GENERAL); } // else nothing to do; the value is in the right register already } else { uint32_t disp = GetIntArgStackDisp(usedIntSlots_, usedFloatSlots_, &padding_); - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), Move::GENERAL); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), MoveOp::GENERAL); } usedIntSlots_++; } @@ -3552,13 +3552,13 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from) uint32_t increment = 1; bool useResolver = true; ++passedArgs_; - Move::Kind kind = Move::GENERAL; + MoveOp::Kind kind = MoveOp::GENERAL; if (from.isDouble()) { // Double arguments need to be rounded up to the nearest doubleword // boundary, even if it is in a register! usedSlots_ = (usedSlots_ + 1) & ~1; increment = 2; - kind = Move::DOUBLE; + kind = MoveOp::DOUBLE; } Register destReg; diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 9fb423cb43b1..ad0e79f2c58f 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -462,7 +462,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM // Used to work around the move resolver's lack of support for // moving into register pairs, which the softfp ABI needs. - mozilla::Array floatArgsInGPR; + mozilla::Array floatArgsInGPR; mozilla::Array floatArgsInGPRValid; // Compute space needed for the function call and set the properties of the @@ -485,9 +485,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM setFramePushed(framePushed_ + value); } public: - typedef MoveResolver::MoveOperand MoveOperand; - typedef MoveResolver::Move Move; - enum Result { GENERAL, DOUBLE, diff --git a/js/src/jit/arm/MoveEmitter-arm.cpp b/js/src/jit/arm/MoveEmitter-arm.cpp index 72fabee9a4e1..f5ded1122b99 100644 --- a/js/src/jit/arm/MoveEmitter-arm.cpp +++ b/js/src/jit/arm/MoveEmitter-arm.cpp @@ -100,7 +100,7 @@ MoveEmitterARM::tempReg() } void -MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind) +MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind) { // There is some pattern: // (A -> B) @@ -108,7 +108,7 @@ MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, Move: // // This case handles (A -> B), which we reach first. We save B, then allow // the original move to continue. - if (kind == Move::DOUBLE) { + if (kind == MoveOp::DOUBLE) { if (to.isMemory()) { FloatRegister temp = ScratchFloatReg; masm.ma_vldr(toOperand(to, true), temp); @@ -134,7 +134,7 @@ MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, Move: } void -MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind) +MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind) { // There is some pattern: // (A -> B) @@ -142,7 +142,7 @@ MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, Mo // // This case handles (B -> A), which we reach last. We emit a move from the // saved value of B, to A. - if (kind == Move::DOUBLE) { + if (kind == MoveOp::DOUBLE) { if (to.isMemory()) { FloatRegister temp = ScratchFloatReg; masm.ma_vldr(cycleSlot(), temp); @@ -232,7 +232,7 @@ MoveEmitterARM::emitDoubleMove(const MoveOperand &from, const MoveOperand &to) } void -MoveEmitterARM::emit(const Move &move) +MoveEmitterARM::emit(const MoveOp &move) { const MoveOperand &from = move.from(); const MoveOperand &to = move.to(); @@ -248,7 +248,7 @@ MoveEmitterARM::emit(const Move &move) inCycle_ = true; } - if (move.kind() == Move::DOUBLE) + if (move.kind() == MoveOp::DOUBLE) emitDoubleMove(from, to); else emitMove(from, to); diff --git a/js/src/jit/arm/MoveEmitter-arm.h b/js/src/jit/arm/MoveEmitter-arm.h index 049dc28fdf13..c1dca9f5f38f 100644 --- a/js/src/jit/arm/MoveEmitter-arm.h +++ b/js/src/jit/arm/MoveEmitter-arm.h @@ -17,9 +17,6 @@ class CodeGenerator; class MoveEmitterARM { - typedef MoveResolver::Move Move; - typedef MoveResolver::MoveOperand MoveOperand; - bool inCycle_; MacroAssemblerARMCompat &masm; @@ -49,9 +46,9 @@ class MoveEmitterARM void emitMove(const MoveOperand &from, const MoveOperand &to); void emitDoubleMove(const MoveOperand &from, const MoveOperand &to); - void breakCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind); - void completeCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind); - void emit(const Move &move); + void breakCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind); + void completeCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind); + void emit(const MoveOp &move); public: MoveEmitterARM(MacroAssemblerARMCompat &masm); diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index 33478fb93f74..6a7291338d3f 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -654,8 +654,6 @@ JitRuntime::generateBailoutHandler(JSContext *cx) IonCode * JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) { - typedef MoveResolver::MoveOperand MoveOperand; - JS_ASSERT(functionWrappers_); JS_ASSERT(functionWrappers_->initialized()); VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f); diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.cpp b/js/src/jit/shared/CodeGenerator-x86-shared.cpp index e7a1fff0ccc7..2a563f422cac 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -1329,8 +1329,6 @@ CodeGeneratorX86Shared::visitUrshD(LUrshD *ins) return true; } -typedef MoveResolver::MoveOperand MoveOperand; - MoveOperand CodeGeneratorX86Shared::toMoveOperand(const LAllocation *a) const { diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.h b/js/src/jit/shared/CodeGenerator-x86-shared.h index 8525a41ab8dd..5ec76b33da0a 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/shared/CodeGenerator-x86-shared.h @@ -49,7 +49,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared return ToOperand(def->output()); } - MoveResolver::MoveOperand toMoveOperand(const LAllocation *a) const; + MoveOperand toMoveOperand(const LAllocation *a) const; bool bailoutIf(Assembler::Condition condition, LSnapshot *snapshot); bool bailoutIf(Assembler::DoubleCondition condition, LSnapshot *snapshot); diff --git a/js/src/jit/shared/MoveEmitter-x86-shared.cpp b/js/src/jit/shared/MoveEmitter-x86-shared.cpp index 55ce862e0c6d..51ef2b76b73e 100644 --- a/js/src/jit/shared/MoveEmitter-x86-shared.cpp +++ b/js/src/jit/shared/MoveEmitter-x86-shared.cpp @@ -27,7 +27,7 @@ MoveEmitterX86::characterizeCycle(const MoveResolver &moves, size_t i, size_t swapCount = 0; for (size_t j = i; ; j++) { - const Move &move = moves.getMove(j); + const MoveOp &move = moves.getMove(j); // If it isn't a cycle of registers of the same kind, we won't be able // to optimize it. @@ -56,7 +56,7 @@ MoveEmitterX86::characterizeCycle(const MoveResolver &moves, size_t i, } // Check that the last move cycles back to the first move. - const Move &move = moves.getMove(i + swapCount); + const MoveOp &move = moves.getMove(i + swapCount); if (move.from() != moves.getMove(i).to()) { *allGeneralRegs = false; *allFloatRegs = false; @@ -99,7 +99,7 @@ void MoveEmitterX86::emit(const MoveResolver &moves) { for (size_t i = 0; i < moves.numMoves(); i++) { - const Move &move = moves.getMove(i); + const MoveOp &move = moves.getMove(i); const MoveOperand &from = move.from(); const MoveOperand &to = move.to(); @@ -128,7 +128,7 @@ MoveEmitterX86::emit(const MoveResolver &moves) } // A normal move which is not part of a cycle. - if (move.kind() == Move::DOUBLE) + if (move.kind() == MoveOp::DOUBLE) emitDoubleMove(from, to); else emitGeneralMove(from, to); @@ -204,7 +204,7 @@ MoveEmitterX86::toPopOperand(const MoveOperand &operand) const } void -MoveEmitterX86::breakCycle(const MoveOperand &to, Move::Kind kind) +MoveEmitterX86::breakCycle(const MoveOperand &to, MoveOp::Kind kind) { // There is some pattern: // (A -> B) @@ -212,7 +212,7 @@ MoveEmitterX86::breakCycle(const MoveOperand &to, Move::Kind kind) // // This case handles (A -> B), which we reach first. We save B, then allow // the original move to continue. - if (kind == Move::DOUBLE) { + if (kind == MoveOp::DOUBLE) { if (to.isMemory()) { masm.loadDouble(toAddress(to), ScratchFloatReg); masm.storeDouble(ScratchFloatReg, cycleSlot()); @@ -225,7 +225,7 @@ MoveEmitterX86::breakCycle(const MoveOperand &to, Move::Kind kind) } void -MoveEmitterX86::completeCycle(const MoveOperand &to, Move::Kind kind) +MoveEmitterX86::completeCycle(const MoveOperand &to, MoveOp::Kind kind) { // There is some pattern: // (A -> B) @@ -233,7 +233,7 @@ MoveEmitterX86::completeCycle(const MoveOperand &to, Move::Kind kind) // // This case handles (B -> A), which we reach last. We emit a move from the // saved value of B, to A. - if (kind == Move::DOUBLE) { + if (kind == MoveOp::DOUBLE) { if (to.isMemory()) { masm.loadDouble(cycleSlot(), ScratchFloatReg); masm.storeDouble(ScratchFloatReg, toAddress(to)); diff --git a/js/src/jit/shared/MoveEmitter-x86-shared.h b/js/src/jit/shared/MoveEmitter-x86-shared.h index b7d8fe7446df..01636af4d063 100644 --- a/js/src/jit/shared/MoveEmitter-x86-shared.h +++ b/js/src/jit/shared/MoveEmitter-x86-shared.h @@ -23,9 +23,6 @@ class CodeGenerator; class MoveEmitterX86 { - typedef MoveResolver::Move Move; - typedef MoveResolver::MoveOperand MoveOperand; - bool inCycle_; MacroAssemblerSpecific &masm; @@ -48,8 +45,8 @@ class MoveEmitterX86 bool allGeneralRegs, bool allFloatRegs, size_t swapCount); void emitGeneralMove(const MoveOperand &from, const MoveOperand &to); void emitDoubleMove(const MoveOperand &from, const MoveOperand &to); - void breakCycle(const MoveOperand &to, Move::Kind kind); - void completeCycle(const MoveOperand &to, Move::Kind kind); + void breakCycle(const MoveOperand &to, MoveOp::Kind kind); + void completeCycle(const MoveOperand &to, MoveOp::Kind kind); public: MoveEmitterX86(MacroAssemblerSpecific &masm); diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 4586b3f01675..395a90955272 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -149,7 +149,7 @@ MacroAssemblerX64::passABIArg(const MoveOperand &from) to = MoveOperand(StackPointer, stackForCall_); stackForCall_ += sizeof(double); } - enoughMemory_ = moveResolver_.addMove(from, to, Move::DOUBLE); + enoughMemory_ = moveResolver_.addMove(from, to, MoveOp::DOUBLE); } else { Register dest; if (GetIntArgReg(passedIntArgs_++, passedFloatArgs_, &dest)) { @@ -162,7 +162,7 @@ MacroAssemblerX64::passABIArg(const MoveOperand &from) to = MoveOperand(StackPointer, stackForCall_); stackForCall_ += sizeof(int64_t); } - enoughMemory_ = moveResolver_.addMove(from, to, Move::GENERAL); + enoughMemory_ = moveResolver_.addMove(from, to, MoveOp::GENERAL); } } @@ -264,7 +264,7 @@ MacroAssemblerX64::callWithABI(Address fun, Result result) if (IsIntArgReg(fun.base)) { // Callee register may be clobbered for an argument. Move the callee to // r10, a volatile, non-argument register. - moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10), Move::GENERAL); + moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10), MoveOp::GENERAL); fun.base = r10; } diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index fc68f4ad9601..28f4eed9f6f8 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -85,9 +85,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared FLOAT }; - typedef MoveResolver::MoveOperand MoveOperand; - typedef MoveResolver::Move Move; - MacroAssemblerX64() : inCall_(false), enoughMemory_(true) diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index d41d42bea39d..14348720e429 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -503,8 +503,6 @@ JitRuntime::generateBailoutHandler(JSContext *cx) IonCode * JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) { - typedef MoveResolver::MoveOperand MoveOperand; - JS_ASSERT(!StackKeptAligned); JS_ASSERT(functionWrappers_); JS_ASSERT(functionWrappers_->initialized()); diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index ace3e98d36af..c430c2778441 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -169,10 +169,10 @@ MacroAssemblerX86::passABIArg(const MoveOperand &from) MoveOperand to = MoveOperand(StackPointer, stackForCall_); if (from.isDouble()) { stackForCall_ += sizeof(double); - enoughMemory_ &= moveResolver_.addMove(from, to, Move::DOUBLE); + enoughMemory_ &= moveResolver_.addMove(from, to, MoveOp::DOUBLE); } else { stackForCall_ += sizeof(int32_t); - enoughMemory_ &= moveResolver_.addMove(from, to, Move::GENERAL); + enoughMemory_ &= moveResolver_.addMove(from, to, MoveOp::GENERAL); } } diff --git a/js/src/jit/x86/MacroAssembler-x86.h b/js/src/jit/x86/MacroAssembler-x86.h index 0d1395a08300..e7ee36481786 100644 --- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -76,9 +76,6 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared FLOAT }; - typedef MoveResolver::MoveOperand MoveOperand; - typedef MoveResolver::Move Move; - MacroAssemblerX86() : inCall_(false), enoughMemory_(true) diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index a14d8e2c2dfc..2c6558ccee22 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -538,8 +538,6 @@ JitRuntime::generateBailoutHandler(JSContext *cx) IonCode * JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) { - typedef MoveResolver::MoveOperand MoveOperand; - JS_ASSERT(!StackKeptAligned); JS_ASSERT(functionWrappers_); JS_ASSERT(functionWrappers_->initialized()); From 3a62ff8315859d72808c9bf379893c936615c4a8 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 096/459] Bug 949668 - SpiderMonkey: MoveOp cleanups. r=jandem --- js/src/jit/MoveResolver.h | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/js/src/jit/MoveResolver.h b/js/src/jit/MoveResolver.h index bbe6f1201a32..3ae8a50fd053 100644 --- a/js/src/jit/MoveResolver.h +++ b/js/src/jit/MoveResolver.h @@ -125,16 +125,10 @@ class MoveOp public: MoveOp() { } - MoveOp(const MoveOp &other) - : from_(other.from_), - to_(other.to_), - cycle_(other.cycle_), - kind_(other.kind_) - { } - MoveOp(const MoveOperand &from, const MoveOperand &to, Kind kind, bool cycle = false) + MoveOp(const MoveOperand &from, const MoveOperand &to, Kind kind) : from_(from), to_(to), - cycle_(cycle), + cycle_(false), kind_(kind) { } @@ -163,14 +157,13 @@ class MoveResolver PendingMove() { } PendingMove(const MoveOperand &from, const MoveOperand &to, Kind kind) - : MoveOp(from, to, kind, false) + : MoveOp(from, to, kind) { } - + void setInCycle() { JS_ASSERT(!inCycle()); cycle_ = true; } - }; typedef InlineList::iterator PendingMoveIterator; From 3a820e1318f9998d11e797fdd20d573ac5571e7c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 097/459] Bug 949668 - SpiderMonkey: Add an LDefinition::Float32 r=jandem --- js/src/jit/BacktrackingAllocator.cpp | 4 +++- js/src/jit/LIR.cpp | 5 +++-- js/src/jit/LIR.h | 6 ++++-- js/src/jit/LinearScan.cpp | 6 ++++-- js/src/jit/LiveRangeAllocator.cpp | 1 + js/src/jit/LiveRangeAllocator.h | 6 +++--- js/src/jit/Lowering.cpp | 26 +++++++++++------------ js/src/jit/RegisterAllocator.h | 2 +- js/src/jit/StupidAllocator.cpp | 6 ++++-- js/src/jit/arm/Lowering-arm.cpp | 2 +- js/src/jit/shared/Lowering-shared-inl.h | 12 +++++++++-- js/src/jit/shared/Lowering-shared.h | 3 ++- js/src/jit/shared/Lowering-x86-shared.cpp | 6 +++--- 13 files changed, 52 insertions(+), 33 deletions(-) diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index 64e46a4edfcc..b06c2f039fdf 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -1128,7 +1128,9 @@ BacktrackingAllocator::populateSafepoints() if (ins == reg->ins() && !reg->isTemp()) { DebugOnly def = reg->def(); JS_ASSERT_IF(def->policy() == LDefinition::MUST_REUSE_INPUT, - def->type() == LDefinition::GENERAL || def->type() == LDefinition::DOUBLE); + def->type() == LDefinition::GENERAL || + def->type() == LDefinition::FLOAT32 || + def->type() == LDefinition::DOUBLE); continue; } diff --git a/js/src/jit/LIR.cpp b/js/src/jit/LIR.cpp index fc672bdb356e..70bf0e01a659 100644 --- a/js/src/jit/LIR.cpp +++ b/js/src/jit/LIR.cpp @@ -197,10 +197,11 @@ static const char * const TypeChars[] = { "i", // INTEGER "o", // OBJECT - "f", // DOUBLE + "f", // FLOAT32 + "d", // DOUBLE #ifdef JS_NUNBOX32 "t", // TYPE - "d" // PAYLOAD + "p" // PAYLOAD #elif JS_PUNBOX64 "x" // BOX #endif diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h index 1f802ba79186..5e020fe01ba5 100644 --- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -446,7 +446,8 @@ class LDefinition GENERAL, // Generic, integer or pointer-width data (GPR). OBJECT, // Pointer that may be collected as garbage (GPR). SLOTS, // Slots/elements pointer that may be moved by minor GCs (GPR). - DOUBLE, // 64-bit point value (FPU). + FLOAT32, // 32-bit floating-point value (FPU). + DOUBLE, // 64-bit floating-point value (FPU). #ifdef JS_NUNBOX32 // A type virtual register must be followed by a payload virtual // register, as both will be tracked as a single gcthing. @@ -540,8 +541,9 @@ class LDefinition case MIRType_Object: return LDefinition::OBJECT; case MIRType_Double: - case MIRType_Float32: return LDefinition::DOUBLE; + case MIRType_Float32: + return LDefinition::FLOAT32; #if defined(JS_PUNBOX64) case MIRType_Value: return LDefinition::BOX; diff --git a/js/src/jit/LinearScan.cpp b/js/src/jit/LinearScan.cpp index cd22f4d36818..86731c3ece01 100644 --- a/js/src/jit/LinearScan.cpp +++ b/js/src/jit/LinearScan.cpp @@ -515,7 +515,9 @@ LinearScanAllocator::populateSafepoints() if (ins == reg->ins() && !reg->isTemp()) { DebugOnly def = reg->def(); JS_ASSERT_IF(def->policy() == LDefinition::MUST_REUSE_INPUT, - def->type() == LDefinition::GENERAL || def->type() == LDefinition::DOUBLE); + def->type() == LDefinition::GENERAL || + def->type() == LDefinition::FLOAT32 || + def->type() == LDefinition::DOUBLE); continue; } @@ -792,7 +794,7 @@ LinearScanAllocator::allocateSlotFor(const LiveInterval *interval) LinearScanVirtualRegister *reg = &vregs[interval->vreg()]; SlotList *freed; - if (reg->type() == LDefinition::DOUBLE) + if (reg->type() == LDefinition::DOUBLE || reg->type() == LDefinition::FLOAT32) freed = &finishedDoubleSlots_; #ifdef JS_NUNBOX32 else if (IsNunbox(reg)) diff --git a/js/src/jit/LiveRangeAllocator.cpp b/js/src/jit/LiveRangeAllocator.cpp index 361fdb60ebcd..0d457286657e 100644 --- a/js/src/jit/LiveRangeAllocator.cpp +++ b/js/src/jit/LiveRangeAllocator.cpp @@ -468,6 +468,7 @@ AddRegisterToSafepoint(LSafepoint *safepoint, AnyRegister reg, const LDefinition JS_ASSERT(def.type() == LDefinition::GENERAL || def.type() == LDefinition::DOUBLE || + def.type() == LDefinition::FLOAT32 || def.type() == LDefinition::OBJECT); if (def.type() == LDefinition::OBJECT) diff --git a/js/src/jit/LiveRangeAllocator.h b/js/src/jit/LiveRangeAllocator.h index a469ebda4aeb..cfff988c4163 100644 --- a/js/src/jit/LiveRangeAllocator.h +++ b/js/src/jit/LiveRangeAllocator.h @@ -136,7 +136,7 @@ static inline bool DefinitionCompatibleWith(LInstruction *ins, const LDefinition *def, LAllocation alloc) { if (ins->isPhi()) { - if (def->type() == LDefinition::DOUBLE) + if (def->type() == LDefinition::DOUBLE || def->type() == LDefinition::FLOAT32) return alloc.isFloatReg() || alloc.kind() == LAllocation::DOUBLE_SLOT; return alloc.isGeneralReg() || alloc.kind() == LAllocation::STACK_SLOT; } @@ -145,7 +145,7 @@ DefinitionCompatibleWith(LInstruction *ins, const LDefinition *def, LAllocation case LDefinition::DEFAULT: if (!alloc.isRegister()) return false; - return alloc.isFloatReg() == (def->type() == LDefinition::DOUBLE); + return alloc.isFloatReg() == (def->type() == LDefinition::DOUBLE || def->type() == LDefinition::FLOAT32); case LDefinition::PRESET: return alloc == *def->output(); case LDefinition::MUST_REUSE_INPUT: @@ -473,7 +473,7 @@ class VirtualRegister return intervals_.insert(found, interval); } bool isDouble() const { - return def_->type() == LDefinition::DOUBLE; + return def_->type() == LDefinition::DOUBLE || def_->type() == LDefinition::FLOAT32; } LiveInterval *intervalFor(CodePosition pos); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 480ca2ae611f..67d8af974949 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -660,7 +660,7 @@ LIRGenerator::visitTest(MTest *test) temp0 = LDefinition::BogusTemp(); temp1 = LDefinition::BogusTemp(); } - LTestVAndBranch *lir = new(alloc()) LTestVAndBranch(ifTrue, ifFalse, tempFloat(), temp0, temp1); + LTestVAndBranch *lir = new(alloc()) LTestVAndBranch(ifTrue, ifFalse, tempDouble(), temp0, temp1); if (!useBox(lir, LTestVAndBranch::Input, opd)) return false; return add(lir, test); @@ -1027,7 +1027,7 @@ LIRGenerator::visitTypeOf(MTypeOf *ins) bool LIRGenerator::visitToId(MToId *ins) { - LToIdV *lir = new(alloc()) LToIdV(tempFloat()); + LToIdV *lir = new(alloc()) LToIdV(tempDouble()); if (!useBox(lir, LToIdV::Object, ins->lhs())) return false; if (!useBox(lir, LToIdV::Index, ins->rhs())) @@ -1176,7 +1176,7 @@ bool LIRGenerator::visitRound(MRound *ins) { JS_ASSERT(ins->num()->type() == MIRType_Double); - LRound *lir = new(alloc()) LRound(useRegister(ins->num()), tempFloat()); + LRound *lir = new(alloc()) LRound(useRegister(ins->num()), tempDouble()); if (!assignSnapshot(lir)) return false; return define(lir, ins); @@ -1755,7 +1755,7 @@ LIRGenerator::visitToInt32(MToInt32 *convert) switch (opd->type()) { case MIRType_Value: { - LValueToInt32 *lir = new(alloc()) LValueToInt32(tempFloat(), temp(), LValueToInt32::NORMAL); + LValueToInt32 *lir = new(alloc()) LValueToInt32(tempDouble(), temp(), LValueToInt32::NORMAL); if (!useBox(lir, LValueToInt32::Input, opd)) return false; return assignSnapshot(lir) && define(lir, convert) && assignSafepoint(lir, convert); @@ -1800,7 +1800,7 @@ LIRGenerator::visitTruncateToInt32(MTruncateToInt32 *truncate) switch (opd->type()) { case MIRType_Value: { - LValueToInt32 *lir = new(alloc()) LValueToInt32(tempFloat(), temp(), LValueToInt32::TRUNCATE); + LValueToInt32 *lir = new(alloc()) LValueToInt32(tempDouble(), temp(), LValueToInt32::TRUNCATE); if (!useBox(lir, LValueToInt32::Input, opd)) return false; return assignSnapshot(lir) && define(lir, truncate) && assignSafepoint(lir, truncate); @@ -2026,7 +2026,7 @@ LIRGenerator::visitMaybeToDoubleElement(MMaybeToDoubleElement *ins) LMaybeToDoubleElement *lir = new(alloc()) LMaybeToDoubleElement(useRegisterAtStart(ins->elements()), useRegisterAtStart(ins->value()), - tempFloat()); + tempDouble()); return defineBox(lir, ins); } @@ -2343,7 +2343,7 @@ LIRGenerator::visitNot(MNot *ins) temp1 = LDefinition::BogusTemp(); } - LNotV *lir = new(alloc()) LNotV(tempFloat(), temp0, temp1); + LNotV *lir = new(alloc()) LNotV(tempDouble(), temp0, temp1); if (!useBox(lir, LNotV::Input, op)) return false; return define(lir, ins); @@ -2626,7 +2626,7 @@ LIRGenerator::visitClampToUint8(MClampToUint8 *ins) case MIRType_Value: { - LClampVToUint8 *lir = new(alloc()) LClampVToUint8(tempFloat()); + LClampVToUint8 *lir = new(alloc()) LClampVToUint8(tempDouble()); if (!useBox(lir, LClampVToUint8::Input, in)) return false; return assignSnapshot(lir) && define(lir, ins) && assignSafepoint(lir, ins); @@ -2913,15 +2913,15 @@ LIRGenerator::visitAssertRange(MAssertRange *ins) break; case MIRType_Double: - lir = new(alloc()) LAssertRangeD(useRegister(input), tempFloat()); + lir = new(alloc()) LAssertRangeD(useRegister(input), tempDouble()); break; case MIRType_Float32: - lir = new(alloc()) LAssertRangeF(useRegister(input), tempFloat()); + lir = new(alloc()) LAssertRangeF(useRegister(input), tempFloat32()); break; case MIRType_Value: - lir = new(alloc()) LAssertRangeV(tempToUnbox(), tempFloat(), tempFloat()); + lir = new(alloc()) LAssertRangeV(tempToUnbox(), tempDouble(), tempDouble()); if (!useBox(lir, LAssertRangeV::Input, input)) return false; break; @@ -3027,7 +3027,7 @@ LIRGenerator::visitSetElementCache(MSetElementCache *ins) LInstruction *lir; if (ins->value()->type() == MIRType_Value) { lir = new(alloc()) LSetElementCacheV(useByteOpRegister(ins->object()), tempToUnbox(), - temp(), tempFloat()); + temp(), tempDouble()); if (!useBox(lir, LSetElementCacheV::Index, ins->index())) return false; @@ -3036,7 +3036,7 @@ LIRGenerator::visitSetElementCache(MSetElementCache *ins) } else { lir = new(alloc()) LSetElementCacheT(useByteOpRegister(ins->object()), useRegisterOrConstant(ins->value()), - tempToUnbox(), temp(), tempFloat()); + tempToUnbox(), temp(), tempDouble()); if (!useBox(lir, LSetElementCacheT::Index, ins->index())) return false; diff --git a/js/src/jit/RegisterAllocator.h b/js/src/jit/RegisterAllocator.h index e6059eaef31d..f5f0d2770cfd 100644 --- a/js/src/jit/RegisterAllocator.h +++ b/js/src/jit/RegisterAllocator.h @@ -369,7 +369,7 @@ class RegisterAllocator static inline AnyRegister GetFixedRegister(const LDefinition *def, const LUse *use) { - return def->type() == LDefinition::DOUBLE + return (def->type() == LDefinition::DOUBLE || def->type() == LDefinition::FLOAT32) ? AnyRegister(FloatRegister::FromCode(use->registerCode())) : AnyRegister(Register::FromCode(use->registerCode())); } diff --git a/js/src/jit/StupidAllocator.cpp b/js/src/jit/StupidAllocator.cpp index 921bdf9d08d4..9c350555d9cc 100644 --- a/js/src/jit/StupidAllocator.cpp +++ b/js/src/jit/StupidAllocator.cpp @@ -28,7 +28,9 @@ StupidAllocator::stackLocation(uint32_t vreg) if (def->policy() == LDefinition::PRESET && def->output()->isArgument()) return def->output(); - return new(alloc()) LStackSlot(DefaultStackSlot(vreg), def->type() == LDefinition::DOUBLE); + return new(alloc()) LStackSlot(DefaultStackSlot(vreg), + def->type() == LDefinition::DOUBLE || + def->type() == LDefinition::FLOAT32); } StupidAllocator::RegisterIndex @@ -162,7 +164,7 @@ StupidAllocator::allocateRegister(LInstruction *ins, uint32_t vreg) for (size_t i = 0; i < registerCount; i++) { AnyRegister reg = registers[i].reg; - if (reg.isFloat() != (def->type() == LDefinition::DOUBLE)) + if (reg.isFloat() != (def->type() == LDefinition::DOUBLE || def->type() == LDefinition::FLOAT32)) continue; // Skip the register if it is in use for an allocated input or output. diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 8f4ca0c78d19..b74971f3e3f6 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -362,7 +362,7 @@ LIRGeneratorARM::newLTableSwitch(const LAllocation &in, const LDefinition &input LTableSwitchV * LIRGeneratorARM::newLTableSwitchV(MTableSwitch *tableswitch) { - return new(alloc()) LTableSwitchV(temp(), tempFloat(), tableswitch); + return new(alloc()) LTableSwitchV(temp(), tempDouble(), tableswitch); } bool diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index 940d346cae9d..fcc6fdc68ca7 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -149,12 +149,14 @@ LIRGeneratorShared::defineReturn(LInstruction *lir, MDefinition *mir) #endif break; case MIRType_Float32: + lir->setDef(0, LDefinition(vreg, LDefinition::FLOAT32, LFloatReg(ReturnFloatReg))); + break; case MIRType_Double: lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnFloatReg))); break; default: LDefinition::Type type = LDefinition::TypeFrom(mir->type()); - JS_ASSERT(type != LDefinition::DOUBLE); + JS_ASSERT(type != LDefinition::DOUBLE && type != LDefinition::FLOAT32); lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg))); break; } @@ -421,7 +423,13 @@ LIRGeneratorShared::tempFixed(Register reg) } LDefinition -LIRGeneratorShared::tempFloat() +LIRGeneratorShared::tempFloat32() +{ + return temp(LDefinition::FLOAT32); +} + +LDefinition +LIRGeneratorShared::tempDouble() { return temp(LDefinition::DOUBLE); } diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index fdfb74276682..35075335af13 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -104,7 +104,8 @@ class LIRGeneratorShared : public MInstructionVisitorWithDefaults // These create temporary register requests. inline LDefinition temp(LDefinition::Type type = LDefinition::GENERAL, LDefinition::Policy policy = LDefinition::DEFAULT); - inline LDefinition tempFloat(); + inline LDefinition tempFloat32(); + inline LDefinition tempDouble(); inline LDefinition tempCopy(MDefinition *input, uint32_t reusedInput); // Note that the fixed register has a GENERAL type. diff --git a/js/src/jit/shared/Lowering-x86-shared.cpp b/js/src/jit/shared/Lowering-x86-shared.cpp index 8895dba562bc..71d687543dfa 100644 --- a/js/src/jit/shared/Lowering-x86-shared.cpp +++ b/js/src/jit/shared/Lowering-x86-shared.cpp @@ -27,7 +27,7 @@ LIRGeneratorX86Shared::newLTableSwitch(const LAllocation &in, const LDefinition LTableSwitchV * LIRGeneratorX86Shared::newLTableSwitchV(MTableSwitch *tableswitch) { - return new(alloc()) LTableSwitchV(temp(), tempFloat(), temp(), tableswitch); + return new(alloc()) LTableSwitchV(temp(), tempDouble(), temp(), tableswitch); } bool @@ -333,7 +333,7 @@ LIRGeneratorX86Shared::lowerTruncateDToInt32(MTruncateToInt32 *ins) MDefinition *opd = ins->input(); JS_ASSERT(opd->type() == MIRType_Double); - LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat(); + LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempDouble(); return define(new(alloc()) LTruncateDToInt32(useRegister(opd), maybeTemp), ins); } @@ -343,6 +343,6 @@ LIRGeneratorX86Shared::lowerTruncateFToInt32(MTruncateToInt32 *ins) MDefinition *opd = ins->input(); JS_ASSERT(opd->type() == MIRType_Float32); - LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat(); + LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat32(); return define(new(alloc()) LTruncateFToInt32(useRegister(opd), maybeTemp), ins); } From f53aa6bc3248a627ff17589fa4317ec5de1f442b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 098/459] Bug 949668 - SpiderMonkey: Add a type to LMoveGroup. r=jandem --- js/src/jit/BacktrackingAllocator.cpp | 15 +++++++------- js/src/jit/LIR-Common.h | 13 +++++++++---- js/src/jit/LIR.cpp | 10 +++++----- js/src/jit/LinearScan.cpp | 29 +++++++++++++++------------- js/src/jit/LinearScan.h | 2 +- js/src/jit/LiveRangeAllocator.h | 20 +++++++++---------- js/src/jit/StupidAllocator.cpp | 17 +++++++++------- js/src/jit/StupidAllocator.h | 5 ++++- 8 files changed, 63 insertions(+), 48 deletions(-) diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index b06c2f039fdf..803079fc2879 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -918,10 +918,10 @@ BacktrackingAllocator::resolveControlFlow() LiveInterval *prevInterval = reg->intervalFor(start.previous()); if (start.subpos() == CodePosition::INPUT) { - if (!moveInput(inputOf(data->ins()), prevInterval, interval)) + if (!moveInput(inputOf(data->ins()), prevInterval, interval, reg->type())) return false; } else { - if (!moveAfter(outputOf(data->ins()), prevInterval, interval)) + if (!moveAfter(outputOf(data->ins()), prevInterval, interval, reg->type())) return false; } } @@ -941,7 +941,8 @@ BacktrackingAllocator::resolveControlFlow() for (size_t j = 0; j < successor->numPhis(); j++) { LPhi *phi = successor->getPhi(j); JS_ASSERT(phi->numDefs() == 1); - VirtualRegister *vreg = &vregs[phi->getDef(0)]; + LDefinition *def = phi->getDef(0); + VirtualRegister *vreg = &vregs[def]; LiveInterval *to = vreg->intervalFor(inputOf(successor->firstId())); JS_ASSERT(to); @@ -953,7 +954,7 @@ BacktrackingAllocator::resolveControlFlow() LiveInterval *from = vregs[input].intervalFor(outputOf(predecessor->lastId())); JS_ASSERT(from); - if (!moveAtExit(predecessor, from, to)) + if (!moveAtExit(predecessor, from, to, def->type())) return false; } } @@ -978,10 +979,10 @@ BacktrackingAllocator::resolveControlFlow() if (mSuccessor->numPredecessors() > 1) { JS_ASSERT(predecessor->mir()->numSuccessors() == 1); - if (!moveAtExit(predecessor, from, to)) + if (!moveAtExit(predecessor, from, to, reg.type())) return false; } else { - if (!moveAtEntry(successor, from, to)) + if (!moveAtEntry(successor, from, to, reg.type())) return false; } } @@ -1077,7 +1078,7 @@ BacktrackingAllocator::reifyAllocations() if (*res != *alloc) { LMoveGroup *group = getInputMoveGroup(inputOf(ins)); - if (!group->addAfter(sourceAlloc, res)) + if (!group->addAfter(sourceAlloc, res, def->type())) return false; *alloc = *res; } diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 8f94028624a1..c4d11cee87e2 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -71,11 +71,13 @@ class LMove { LAllocation *from_; LAllocation *to_; + LDefinition::Type type_; public: - LMove(LAllocation *from, LAllocation *to) + LMove(LAllocation *from, LAllocation *to, LDefinition::Type type) : from_(from), - to_(to) + to_(to), + type_(type) { } LAllocation *from() { @@ -90,6 +92,9 @@ class LMove const LAllocation *to() const { return to_; } + LDefinition::Type type() const { + return type_; + } }; class LMoveGroup : public LInstructionHelper<0, 0, 0> @@ -110,10 +115,10 @@ class LMoveGroup : public LInstructionHelper<0, 0, 0> void printOperands(FILE *fp); // Add a move which takes place simultaneously with all others in the group. - bool add(LAllocation *from, LAllocation *to); + bool add(LAllocation *from, LAllocation *to, LDefinition::Type type); // Add a move which takes place after existing moves in the group. - bool addAfter(LAllocation *from, LAllocation *to); + bool addAfter(LAllocation *from, LAllocation *to, LDefinition::Type type); size_t numMoves() const { return moves_.length(); diff --git a/js/src/jit/LIR.cpp b/js/src/jit/LIR.cpp index 70bf0e01a659..7fef35f37b2f 100644 --- a/js/src/jit/LIR.cpp +++ b/js/src/jit/LIR.cpp @@ -364,18 +364,18 @@ LInstruction::initSafepoint(TempAllocator &alloc) } bool -LMoveGroup::add(LAllocation *from, LAllocation *to) +LMoveGroup::add(LAllocation *from, LAllocation *to, LDefinition::Type type) { #ifdef DEBUG JS_ASSERT(*from != *to); for (size_t i = 0; i < moves_.length(); i++) JS_ASSERT(*to != *moves_[i].to()); #endif - return moves_.append(LMove(from, to)); + return moves_.append(LMove(from, to, type)); } bool -LMoveGroup::addAfter(LAllocation *from, LAllocation *to) +LMoveGroup::addAfter(LAllocation *from, LAllocation *to, LDefinition::Type type) { // Transform the operands to this move so that performing the result // simultaneously with existing moves in the group will have the same @@ -393,12 +393,12 @@ LMoveGroup::addAfter(LAllocation *from, LAllocation *to) for (size_t i = 0; i < moves_.length(); i++) { if (*to == *moves_[i].to()) { - moves_[i] = LMove(from, to); + moves_[i] = LMove(from, to, type); return true; } } - return add(from, to); + return add(from, to, type); } void diff --git a/js/src/jit/LinearScan.cpp b/js/src/jit/LinearScan.cpp index 86731c3ece01..6a46531ab37b 100644 --- a/js/src/jit/LinearScan.cpp +++ b/js/src/jit/LinearScan.cpp @@ -233,7 +233,8 @@ LinearScanAllocator::resolveControlFlow() for (size_t j = 0; j < successor->numPhis(); j++) { LPhi *phi = successor->getPhi(j); JS_ASSERT(phi->numDefs() == 1); - LinearScanVirtualRegister *vreg = &vregs[phi->getDef(0)]; + LDefinition *def = phi->getDef(0); + LinearScanVirtualRegister *vreg = &vregs[def]; LiveInterval *to = vreg->intervalFor(inputOf(successor->firstId())); JS_ASSERT(to); @@ -245,14 +246,15 @@ LinearScanAllocator::resolveControlFlow() LiveInterval *from = vregs[input].intervalFor(outputOf(predecessor->lastId())); JS_ASSERT(from); - if (!moveAtExit(predecessor, from, to)) + if (!moveAtExit(predecessor, from, to, def->type())) return false; } if (vreg->mustSpillAtDefinition() && !to->isSpill()) { // Make sure this phi is spilled at the loop header. LMoveGroup *moves = successor->getEntryMoveGroup(alloc()); - if (!moves->add(to->getAllocation(), vregs[to->vreg()].canonicalSpill())) + if (!moves->add(to->getAllocation(), vregs[to->vreg()].canonicalSpill(), + def->type())) return false; } } @@ -283,10 +285,10 @@ LinearScanAllocator::resolveControlFlow() if (mSuccessor->numPredecessors() > 1) { JS_ASSERT(predecessor->mir()->numSuccessors() == 1); - if (!moveAtExit(predecessor, from, to)) + if (!moveAtExit(predecessor, from, to, vreg->type())) return false; } else { - if (!moveAtEntry(successor, from, to)) + if (!moveAtEntry(successor, from, to, vreg->type())) return false; } } @@ -297,12 +299,13 @@ LinearScanAllocator::resolveControlFlow() } bool -LinearScanAllocator::moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to) +LinearScanAllocator::moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to, + LDefinition::Type type) { if (*from == *to) return true; LMoveGroup *moves = getInputMoveGroup(pos); - return moves->add(from, to); + return moves->add(from, to, type); } static inline void @@ -350,7 +353,7 @@ LinearScanAllocator::reifyAllocations() LiveInterval *to = fixedIntervals[GetFixedRegister(reg->def(), usePos->use).code()]; *static_cast(usePos->use) = *to->getAllocation(); - if (!moveInput(usePos->pos, interval, to)) + if (!moveInput(usePos->pos, interval, to, reg->type())) return false; } else { JS_ASSERT(UseCompatibleWith(usePos->use, *interval->getAllocation())); @@ -376,7 +379,7 @@ LinearScanAllocator::reifyAllocations() // it should use the fixed register instead. SetOsiPointUses(interval, defEnd, LAllocation(fixedReg)); - if (!moveAfter(defEnd, from, interval)) + if (!moveAfter(defEnd, from, interval, def->type())) return false; spillFrom = from->getAllocation(); } else { @@ -387,7 +390,7 @@ LinearScanAllocator::reifyAllocations() JS_ASSERT(!inputAlloc->isUse()); *inputAlloc = *interval->getAllocation(); - if (!moveInputAlloc(inputOf(reg->ins()), origAlloc, inputAlloc)) + if (!moveInputAlloc(inputOf(reg->ins()), origAlloc, inputAlloc, def->type())) return false; } @@ -417,7 +420,7 @@ LinearScanAllocator::reifyAllocations() // or Nop instructions). Note that we explicitly ignore phis, // which should have been handled in resolveControlFlow(). LMoveGroup *moves = getMoveGroupAfter(defEnd); - if (!moves->add(spillFrom, reg->canonicalSpill())) + if (!moves->add(spillFrom, reg->canonicalSpill(), def->type())) return false; } } @@ -443,10 +446,10 @@ LinearScanAllocator::reifyAllocations() JS_ASSERT(start == inputOf(data->ins()) || start == outputOf(data->ins())); if (start.subpos() == CodePosition::INPUT) { - if (!moveInput(inputOf(data->ins()), prevInterval, interval)) + if (!moveInput(inputOf(data->ins()), prevInterval, interval, reg->type())) return false; } else { - if (!moveAfter(outputOf(data->ins()), prevInterval, interval)) + if (!moveAfter(outputOf(data->ins()), prevInterval, interval, reg->type())) return false; } diff --git a/js/src/jit/LinearScan.h b/js/src/jit/LinearScan.h index 64ce47d9191a..cf194b9e20b9 100644 --- a/js/src/jit/LinearScan.h +++ b/js/src/jit/LinearScan.h @@ -111,7 +111,7 @@ class LinearScanAllocator AnyRegister::Code findBestFreeRegister(CodePosition *freeUntil); AnyRegister::Code findBestBlockedRegister(CodePosition *nextUsed); bool canCoexist(LiveInterval *a, LiveInterval *b); - bool moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to); + bool moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to, LDefinition::Type type); void setIntervalRequirement(LiveInterval *interval); bool isSpilledAt(LiveInterval *interval, CodePosition pos); diff --git a/js/src/jit/LiveRangeAllocator.h b/js/src/jit/LiveRangeAllocator.h index cfff988c4163..993f1c495451 100644 --- a/js/src/jit/LiveRangeAllocator.h +++ b/js/src/jit/LiveRangeAllocator.h @@ -633,37 +633,37 @@ class LiveRangeAllocator : protected RegisterAllocator } #endif - bool addMove(LMoveGroup *moves, LiveInterval *from, LiveInterval *to) { + bool addMove(LMoveGroup *moves, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { JS_ASSERT(*from->getAllocation() != *to->getAllocation()); - return moves->add(from->getAllocation(), to->getAllocation()); + return moves->add(from->getAllocation(), to->getAllocation(), type); } - bool moveInput(CodePosition pos, LiveInterval *from, LiveInterval *to) { + bool moveInput(CodePosition pos, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { if (*from->getAllocation() == *to->getAllocation()) return true; LMoveGroup *moves = getInputMoveGroup(pos); - return addMove(moves, from, to); + return addMove(moves, from, to, type); } - bool moveAfter(CodePosition pos, LiveInterval *from, LiveInterval *to) { + bool moveAfter(CodePosition pos, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { if (*from->getAllocation() == *to->getAllocation()) return true; LMoveGroup *moves = getMoveGroupAfter(pos); - return addMove(moves, from, to); + return addMove(moves, from, to, type); } - bool moveAtExit(LBlock *block, LiveInterval *from, LiveInterval *to) { + bool moveAtExit(LBlock *block, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { if (*from->getAllocation() == *to->getAllocation()) return true; LMoveGroup *moves = block->getExitMoveGroup(alloc()); - return addMove(moves, from, to); + return addMove(moves, from, to, type); } - bool moveAtEntry(LBlock *block, LiveInterval *from, LiveInterval *to) { + bool moveAtEntry(LBlock *block, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { if (*from->getAllocation() == *to->getAllocation()) return true; LMoveGroup *moves = block->getEntryMoveGroup(alloc()); - return addMove(moves, from, to); + return addMove(moves, from, to, type); } size_t findFirstNonCallSafepoint(CodePosition from) const diff --git a/js/src/jit/StupidAllocator.cpp b/js/src/jit/StupidAllocator.cpp index 9c350555d9cc..8f9751faba72 100644 --- a/js/src/jit/StupidAllocator.cpp +++ b/js/src/jit/StupidAllocator.cpp @@ -143,7 +143,7 @@ StupidAllocator::ensureHasRegister(LInstruction *ins, uint32_t vreg) } RegisterIndex best = allocateRegister(ins, vreg); - loadRegister(ins, vreg, best); + loadRegister(ins, vreg, best, virtualRegisters[vreg]->type()); return registers[best].reg; } @@ -192,7 +192,7 @@ StupidAllocator::syncRegister(LInstruction *ins, RegisterIndex index) uint32_t existing = registers[index].vreg; LAllocation *dest = stackLocation(existing); - input->addAfter(source, dest); + input->addAfter(source, dest, registers[index].type); registers[index].dirty = false; } @@ -206,14 +206,15 @@ StupidAllocator::evictRegister(LInstruction *ins, RegisterIndex index) } void -StupidAllocator::loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index) +StupidAllocator::loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index, LDefinition::Type type) { // Load a vreg from its stack location to a register. LMoveGroup *input = getInputMoveGroup(ins->id()); LAllocation *source = stackLocation(vreg); LAllocation *dest = new(alloc()) LAllocation(registers[index].reg); - input->addAfter(source, dest); + input->addAfter(source, dest, type); registers[index].set(vreg, ins); + registers[index].type = type; } StupidAllocator::RegisterIndex @@ -312,7 +313,7 @@ StupidAllocator::syncForBlockEnd(LBlock *block, LInstruction *ins) } } - group->add(source, dest); + group->add(source, dest, phi->getDef(0)->type()); } } } @@ -336,14 +337,14 @@ StupidAllocator::allocateForInstruction(LInstruction *ins) AnyRegister reg = ensureHasRegister(ins, vreg); alloc.replace(LAllocation(reg)); } else if (use->policy() == LUse::FIXED) { - AnyRegister reg = GetFixedRegister(virtualRegisters[use->virtualRegister()], use); + AnyRegister reg = GetFixedRegister(virtualRegisters[vreg], use); RegisterIndex index = registerIndex(reg); if (registers[index].vreg != vreg) { evictRegister(ins, index); RegisterIndex existing = findExistingRegister(vreg); if (existing != UINT32_MAX) evictRegister(ins, existing); - loadRegister(ins, vreg, index); + loadRegister(ins, vreg, index, virtualRegisters[vreg]->type()); } alloc.replace(LAllocation(reg)); } else { @@ -409,6 +410,7 @@ StupidAllocator::allocateForDefinition(LInstruction *ins, LDefinition *def) : ins->getOperand(def->getReusedInput())->toRegister()); evictRegister(ins, index); registers[index].set(vreg, ins, true); + registers[index].type = virtualRegisters[vreg]->type(); def->setOutput(LAllocation(registers[index].reg)); } else if (def->policy() == LDefinition::PRESET) { // The result must be a stack location. @@ -417,6 +419,7 @@ StupidAllocator::allocateForDefinition(LInstruction *ins, LDefinition *def) // Find a register to hold the result of the instruction. RegisterIndex best = allocateRegister(ins, vreg); registers[best].set(vreg, ins, true); + registers[best].type = virtualRegisters[vreg]->type(); def->setOutput(LAllocation(registers[best].reg)); } } diff --git a/js/src/jit/StupidAllocator.h b/js/src/jit/StupidAllocator.h index f48befa0ff8b..e7891b65a264 100644 --- a/js/src/jit/StupidAllocator.h +++ b/js/src/jit/StupidAllocator.h @@ -22,6 +22,9 @@ class StupidAllocator : public RegisterAllocator struct AllocatedRegister { AnyRegister reg; + // The type of the value in the register. + LDefinition::Type type; + // Virtual register this physical reg backs, or MISSING_ALLOCATION. uint32_t vreg; @@ -72,7 +75,7 @@ class StupidAllocator : public RegisterAllocator void syncRegister(LInstruction *ins, RegisterIndex index); void evictRegister(LInstruction *ins, RegisterIndex index); - void loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index); + void loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index, LDefinition::Type type); RegisterIndex findExistingRegister(uint32_t vreg); From 9f39fe797e36149148e8c7313cb589d18a3806b1 Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Fri, 13 Dec 2013 16:33:13 -0500 Subject: [PATCH 099/459] Backout fa2130fc4f08 (bug 863872) due to xpcshell orange. r=bustage-fix for CLOSED TREE. --- .../components/telemetry/TelemetryFile.jsm | 75 +---- toolkit/components/telemetry/TelemetryPing.js | 26 +- .../tests/unit/test_TelemetryPing.js | 4 +- .../tests/unit/test_TelemetrySendOldPings.js | 265 ------------------ .../telemetry/tests/unit/xpcshell.ini | 2 - 5 files changed, 19 insertions(+), 353 deletions(-) delete mode 100644 toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js diff --git a/toolkit/components/telemetry/TelemetryFile.jsm b/toolkit/components/telemetry/TelemetryFile.jsm index 8db9eb815b58..7a21d5c2fd4e 100644 --- a/toolkit/components/telemetry/TelemetryFile.jsm +++ b/toolkit/components/telemetry/TelemetryFile.jsm @@ -22,13 +22,8 @@ const PR_EXCL = 0x80; const RW_OWNER = parseInt("0600", 8); const RWX_OWNER = parseInt("0700", 8); -// Files that have been lying around for longer than MAX_PING_FILE_AGE are -// deleted without being loaded. -const MAX_PING_FILE_AGE = 14 * 24 * 60 * 60 * 1000; // 2 weeks - -// Files that are older than OVERDUE_PING_FILE_AGE, but younger than -// MAX_PING_FILE_AGE indicate that we need to send all of our pings ASAP. -const OVERDUE_PING_FILE_AGE = 7 * 24 * 60 * 60 * 1000; // 1 week +// Delete ping files that have been lying around for longer than this. +const MAX_PING_FILE_AGE = 7 * 24 * 60 * 60 * 1000; // 1 week // The number of outstanding saved pings that we have issued loading // requests for. @@ -37,14 +32,6 @@ let pingsLoaded = 0; // The number of those requests that have actually completed. let pingLoadsCompleted = 0; -// The number of pings that we have destroyed due to being older -// than MAX_PING_FILE_AGE. -let pingsDiscarded = 0; - -// The number of pings that are older than OVERDUE_PING_FILE_AGE -// but younger than MAX_PING_FILE_AGE. -let pingsOverdue = 0; - // If |true|, send notifications "telemetry-test-save-complete" // and "telemetry-test-load-complete" once save/load is complete. let shouldNotifyUponSave = false; @@ -54,14 +41,6 @@ let pendingPings = []; this.TelemetryFile = { - get MAX_PING_FILE_AGE() { - return MAX_PING_FILE_AGE; - }, - - get OVERDUE_PING_FILE_AGE() { - return OVERDUE_PING_FILE_AGE; - }, - /** * Save a single ping to a file. * @@ -163,7 +142,7 @@ this.TelemetryFile = { * ping. It is passed |true| in case of success, |false| in case of * format error. */ - loadSavedPings: function(sync, onLoad = null, onDone = null) { + loadSavedPings: function(sync, onLoad = null) { let directory = ensurePingDirectory(); let entries = directory.directoryEntries .QueryInterface(Ci.nsIDirectoryEnumerator); @@ -171,7 +150,7 @@ this.TelemetryFile = { pingLoadsCompleted = 0; try { while (entries.hasMoreElements()) { - this.loadHistograms(entries.nextFile, sync, onLoad, onDone); + this.loadHistograms(entries.nextFile, sync, onLoad); } } finally { entries.close(); @@ -190,26 +169,20 @@ this.TelemetryFile = { * ping. It is passed |true| in case of success, |false| in case of * format error. */ - loadHistograms: function loadHistograms(file, sync, onLoad = null, onDone = null) { - let now = Date.now(); + loadHistograms: function loadHistograms(file, sync, onLoad = null) { + let now = new Date(); if (now - file.lastModifiedTime > MAX_PING_FILE_AGE) { // We haven't had much luck in sending this file; delete it. file.remove(true); - pingsDiscarded++; return; } - // This file is a bit stale, and overdue for sending. - if (now - file.lastModifiedTime > OVERDUE_PING_FILE_AGE) { - pingsOverdue++; - } - pingsLoaded++; if (sync) { let stream = Cc["@mozilla.org/network/file-input-stream;1"] .createInstance(Ci.nsIFileInputStream); stream.init(file, -1, -1, 0); - addToPendingPings(file, stream, onLoad, onDone); + addToPendingPings(file, stream, onLoad); } else { let channel = NetUtil.newChannel(file); channel.contentType = "application/json"; @@ -218,7 +191,7 @@ this.TelemetryFile = { if (!Components.isSuccessCode(result)) { return; } - addToPendingPings(file, stream, onLoad, onDone); + addToPendingPings(file, stream, onLoad); }).bind(this)); } }, @@ -230,22 +203,6 @@ this.TelemetryFile = { return pingsLoaded; }, - /** - * The number of pings loaded that are older than OVERDUE_PING_FILE_AGE - * but younger than MAX_PING_FILE_AGE. - */ - get pingsOverdue() { - return pingsOverdue; - }, - - /** - * The number of pings that we just tossed out for being older than - * MAX_PING_FILE_AGE. - */ - get pingsDiscarded() { - return pingsDiscarded; - }, - /** * Iterate destructively through the pending pings. * @@ -292,7 +249,7 @@ function ensurePingDirectory() { return directory; }; -function addToPendingPings(file, stream, onLoad, onDone) { +function addToPendingPings(file, stream, onLoad) { let success = false; try { @@ -306,25 +263,19 @@ function addToPendingPings(file, stream, onLoad, onDone) { } pingLoadsCompleted++; pendingPings.push(ping); + if (shouldNotifyUponSave && + pingLoadsCompleted == pingsLoaded) { + Services.obs.notifyObservers(null, "telemetry-test-load-complete", null); + } success = true; } catch (e) { // An error reading the file, or an error parsing the contents. stream.close(); // close is idempotent. file.remove(true); // FIXME: Should be false, isn't it? } - if (onLoad) { onLoad(success); } - - if (pingLoadsCompleted == pingsLoaded) { - if (onDone) { - onDone(); - } - if (shouldNotifyUponSave) { - Services.obs.notifyObservers(null, "telemetry-test-load-complete", null); - } - } }; function finishTelemetrySave(ok, stream) { diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index ed7cc2f26f01..b96bbde06be2 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -39,8 +39,6 @@ const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID"; const TELEMETRY_INTERVAL = 60000; // Delay before intializing telemetry (ms) const TELEMETRY_DELAY = 60000; -// Delay before initializing telemetry if we're testing (ms) -const TELEMETRY_TEST_DELAY = 100; // Seconds of idle time before pinging. // On idle-daily a gather-telemetry notification is fired, during it probes can @@ -220,9 +218,6 @@ TelemetryPing.prototype = { ret.savedPings = TelemetryFile.pingsLoaded; } - ret.pingsOverdue = TelemetryFile.pingsOverdue; - ret.pingsDiscarded = TelemetryFile.pingsDiscarded; - return ret; }, @@ -604,9 +599,7 @@ TelemetryPing.prototype = { popPayloads: function popPayloads(reason) { function payloadIter() { - if (reason != "overdue-flush") { - yield this.getSessionPayloadAndSlug(reason); - } + yield this.getSessionPayloadAndSlug(reason); let iterator = TelemetryFile.popPendingPings(reason); for (let data of iterator) { yield data; @@ -768,7 +761,7 @@ TelemetryPing.prototype = { /** * Initializes telemetry within a timer. If there is no PREF_SERVER set, don't turn on telemetry. */ - setup: function setup(aTesting) { + setup: function setup() { // Initialize some probes that are kept in their own modules this._thirdPartyCookies = new ThirdPartyCookieProbe(); this._thirdPartyCookies.init(); @@ -831,17 +824,7 @@ TelemetryPing.prototype = { { let success_histogram = Telemetry.getHistogramById("READ_SAVED_PING_SUCCESS"); success_histogram.add(success); - }), () => - { - // If we have any TelemetryPings lying around, we'll be aggressive - // and try to send them all off ASAP. - if (TelemetryFile.pingsOverdue > 0) { - // It doesn't really matter what we pass to this.send as a reason, - // since it's never sent to the server. All that this.send does with - // the reason is check to make sure it's not a test-ping. - this.send("overdue-flush", this._server); - } - }); + })); this.attachObservers(); this.gatherMemory(); @@ -849,8 +832,7 @@ TelemetryPing.prototype = { }); delete this._timer; } - this._timer.initWithCallback(timerCallback.bind(this), - aTesting ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY, + this._timer.initWithCallback(timerCallback.bind(this), TELEMETRY_DELAY, Ci.nsITimer.TYPE_ONE_SHOT); }, diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 06d9292719cd..c859b779c947 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -142,7 +142,7 @@ function decodeRequestPayload(request) { let observer = { buffer: "", onStreamComplete: function(loader, context, status, length, result) { - this.buffer = String.fromCharCode.apply(this, result); + this.buffer = String.fromCharCode.apply(this, result); } }; @@ -363,7 +363,7 @@ function runOldPingFileTest() { do_check_true(histogramsFile.exists()); let mtime = histogramsFile.lastModifiedTime; - histogramsFile.lastModifiedTime = mtime - 14 * 24 * 60 * 60 * 1000; // 14 days. + histogramsFile.lastModifiedTime = mtime - 8 * 24 * 60 * 60 * 1000; // 8 days. TelemetryPing.testLoadHistograms(histogramsFile, true); do_check_false(histogramsFile.exists()); } diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js deleted file mode 100644 index 0ba9707d8f53..000000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js +++ /dev/null @@ -1,265 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ - -/** - * This test case populates the profile with some fake stored - * pings, and checks that: - * - * 1) Pings that are considered "expired" are deleted and never sent. - * 2) Pings that are considered "overdue" trigger a send of all - * overdue and recent pings. - */ - -Components.utils.import("resource://gre/modules/Services.jsm"); - -// Get the TelemetryPing definitions directly so we can test it without going through xpcom. -// That gives us Cc, Ci, Cr and Cu, as well as a number of consts like PREF_ENABLED, -// and PREF_SERVER. -Services.scriptloader.loadSubScript("resource://gre/components/TelemetryPing.js"); - -Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/TelemetryFile.jsm"); - -// We increment TelemetryFile's MAX_PING_FILE_AGE and -// OVERDUE_PING_FILE_AGE by 1ms so that our test pings exceed -// those points in time. -const EXPIRED_PING_FILE_AGE = TelemetryFile.MAX_PING_FILE_AGE + 1; -const OVERDUE_PING_FILE_AGE = TelemetryFile.OVERDUE_PING_FILE_AGE + 1; - -const PING_SAVE_FOLDER = "saved-telemetry-pings"; -const PING_TIMEOUT_LENGTH = 1500; -const EXPIRED_PINGS = 5; -const OVERDUE_PINGS = 6; -const RECENT_PINGS = 4; - -const TOTAL_EXPECTED_PINGS = OVERDUE_PINGS + RECENT_PINGS; - -let gHttpServer = new HttpServer(); -let gCreatedPings = 0; -let gSeenPings = 0; - -/** - * Creates some TelemetryPings for the current session and - * saves them to disk. Each ping gets a unique ID slug based on - * an incrementor. - * - * @param aNum the number of pings to create. - * @param aAge the age in milliseconds to offset from now. A value - * of 10 would make the ping 10ms older than now, for - * example. - * @returns an Array with the created pings. - */ -function createSavedPings(aNum, aAge) { - // Create a TelemetryPing service that we can generate payloads from. - // Luckily, the TelemetryPing constructor does nothing that we need to - // clean up. - let pingService = new TelemetryPing(); - let pings = []; - let age = Date.now() - aAge; - for (let i = 0; i < aNum; ++i) { - let payload = pingService.getPayload(); - let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload }; - TelemetryFile.savePing(ping); - if (aAge) { - // savePing writes to the file synchronously, so we're good to - // modify the lastModifedTime now. - let file = getSaveFileForPing(ping); - file.lastModifiedTime = age; - } - gCreatedPings++; - pings.push(ping); - } - return pings; -} - -/** - * Deletes locally saved pings in aPings if they - * exist. - * - * @param aPings an Array of pings to delete. - */ -function clearPings(aPings) { - for (let ping of aPings) { - let file = getSaveFileForPing(ping); - if (file.exists()) { - file.remove(false); - } - } -} - -/** - * Returns a handle for the file that aPing should be - * stored in locally. - * - * @returns nsILocalFile - */ -function getSaveFileForPing(aPing) { - let file = Services.dirsvc.get("ProfD", Ci.nsILocalFile).clone(); - file.append(PING_SAVE_FOLDER); - file.append(aPing.slug); - return file; -} - -/** - * Wait for PING_TIMEOUT_LENGTH ms, and make sure we didn't receive - * TelemetryPings in that time. - * - * @returns Promise - */ -function assertReceivedNoPings() { - let deferred = Promise.defer(); - - do_timeout(PING_TIMEOUT_LENGTH, function() { - if (gSeenPings > 0) { - deferred.reject(); - } else { - deferred.resolve(); - } - }); - - return deferred.promise; -} - -/** - * Returns a Promise that rejects if the number of TelemetryPings - * received by the HttpServer is not equal to aExpectedNum. - * - * @param aExpectedNum the number of pings we expect to receive. - * @returns Promise - */ -function assertReceivedPings(aExpectedNum) { - let deferred = Promise.defer(); - - do_timeout(PING_TIMEOUT_LENGTH, function() { - if (gSeenPings == aExpectedNum) { - deferred.resolve(); - } else { - deferred.reject("Saw " + gSeenPings + " TelemetryPings, " + - "but expected " + aExpectedNum); - } - }) - - return deferred.promise; -} - -/** - * Throws if any pings in aPings is saved locally. - * - * @param aPings an Array of pings to check. - */ -function assertNotSaved(aPings) { - let saved = 0; - for (let ping of aPings) { - let file = getSaveFileForPing(ping); - if (file.exists()) { - saved++; - } - } - if (saved > 0) { - do_throw("Found " + saved + " unexpected saved pings."); - } -} - -/** - * Our handler function for the HttpServer that simply - * increments the gSeenPings global when it successfully - * receives and decodes a TelemetryPing payload. - * - * @param aRequest the HTTP request sent from HttpServer. - */ -function pingHandler(aRequest) { - gSeenPings++; -} - -/** - * Returns a Promise that resolves when gHttpServer has been - * successfully shut down. - * - * @returns Promise - */ -function stopHttpServer() { - let deferred = Promise.defer(); - gHttpServer.stop(function() { - deferred.resolve(); - }) - return deferred.promise; -} - -/** - * Teardown a TelemetryPing instance and clear out any pending - * pings to put as back in the starting state. - */ -function resetTelemetry(aPingService) { - aPingService.uninstall(); - // Quick and dirty way to clear TelemetryFile's pendingPings - // collection, and put it back in its initial state. - let gen = TelemetryFile.popPendingPings(); - for (let item of gen) {}; -} - -/** - * Creates and returns a TelemetryPing instance in "testing" - * mode. - */ -function startTelemetry() { - let service = new TelemetryPing(); - service.setup(true); - return service; -} - -function run_test() { - gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler); - gHttpServer.start(-1); - do_get_profile(); - Services.prefs.setBoolPref(PREF_ENABLED, true); - Services.prefs.setCharPref(PREF_SERVER, - "http://localhost:" + gHttpServer.identity.primaryPort); - run_next_test(); -} - -/** - * Test that pings that are considered too old are just chucked out - * immediately and never sent. - */ -add_task(function test_expired_pings_are_deleted() { - let expiredPings = createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE); - let pingService = startTelemetry(); - yield assertReceivedNoPings(); - assertNotSaved(expiredPings); - resetTelemetry(pingService); -}) - -/** - * Test that really recent pings are not sent on Telemetry initialization. - */ -add_task(function test_recent_pings_not_sent() { - let recentPings = createSavedPings(RECENT_PINGS); - let pingService = startTelemetry(); - yield assertReceivedNoPings(); - resetTelemetry(pingService); - clearPings(recentPings); -}); - -/** - * Create some recent, expired and overdue pings. The overdue pings should - * trigger a send of all recent and overdue pings, but the expired pings - * should just be deleted. - */ -add_task(function test_overdue_pings_trigger_send() { - let recentPings = createSavedPings(RECENT_PINGS); - let expiredPings = createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE); - let overduePings = createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE); - - let pingService = startTelemetry(); - yield assertReceivedPings(TOTAL_EXPECTED_PINGS); - - assertNotSaved(recentPings); - assertNotSaved(expiredPings); - assertNotSaved(overduePings); - resetTelemetry(pingService); -}) - -add_task(function teardown() { - yield stopHttpServer(); -}); diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.ini b/toolkit/components/telemetry/tests/unit/xpcshell.ini index 5a0d1b138cd3..339219fedcdc 100644 --- a/toolkit/components/telemetry/tests/unit/xpcshell.ini +++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini @@ -12,5 +12,3 @@ tail = [test_TelemetryStopwatch.js] [test_TelemetryPingBuildID.js] [test_ThirdPartyCookieProbe.js] -[test_TelemetrySendOldPings.js] -skip-if = debug == true From 2e8aaf7ed5be77d4ed2f01e92d59b857f79093db Mon Sep 17 00:00:00 2001 From: Monica Chew Date: Thu, 12 Dec 2013 11:20:11 -0800 Subject: [PATCH 100/459] Bug 837202: Add telemetry for application reputation check (r=paolo,yoric) --- .../downloads/ApplicationReputation.cpp | 53 ++++++++++++++++++- toolkit/components/telemetry/Histograms.json | 18 +++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/toolkit/components/downloads/ApplicationReputation.cpp b/toolkit/components/downloads/ApplicationReputation.cpp index d8fc7c633f29..b8c7bd7864dd 100644 --- a/toolkit/components/downloads/ApplicationReputation.cpp +++ b/toolkit/components/downloads/ApplicationReputation.cpp @@ -21,6 +21,7 @@ #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "mozilla/Telemetry.h" #include "nsCOMPtr.h" #include "nsDebug.h" @@ -33,6 +34,7 @@ #include "nsXPCOMStrings.h" using mozilla::Preferences; +using mozilla::Telemetry::Accumulate; // Preferences that we need to initialize the query. We may need another // preference than browser.safebrowsing.malware.enabled, or simply use @@ -61,6 +63,27 @@ public: ~PendingLookup(); private: + /** + * Telemetry states. + */ + /** + * The download appeared on the allowlist, blocklist, or no list (and thus + * could trigger a remote query). + */ + enum LIST_TYPES { + ALLOW_LIST = 0, + BLOCK_LIST = 1, + NO_LIST = 2, + }; + /** + * Status of the remote response (valid or not). + */ + enum SERVER_RESPONSE_TYPES { + SERVER_RESPONSE_VALID = 0, + SERVER_RESPONSE_FAILED = 1, + SERVER_RESPONSE_INVALID = 2, + }; + nsCOMPtr mQuery; nsCOMPtr mCallback; /** @@ -105,6 +128,8 @@ PendingLookup::~PendingLookup() { nsresult PendingLookup::OnComplete(bool shouldBlock, nsresult rv) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK, + shouldBlock); nsresult res = mCallback->OnComplete(shouldBlock, rv); return res; } @@ -119,15 +144,18 @@ PendingLookup::HandleEvent(const nsACString& tables) { nsCString allow_list; Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allow_list); if (FindInReadable(tables, allow_list)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST); return OnComplete(false, NS_OK); } nsCString block_list; Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &block_list); if (FindInReadable(tables, block_list)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST); return OnComplete(true, NS_OK); } + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST); #if 0 nsresult rv = SendRemoteQuery(); if (NS_FAILED(rv)) { @@ -268,16 +296,32 @@ PendingLookup::OnStopRequestInternal(nsIRequest *aRequest, nsISupports *aContext, nsresult aResult, bool* aShouldBlock) { + if (NS_FAILED(aResult)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_FAILED); + return aResult; + } + *aShouldBlock = false; nsresult rv; nsCOMPtr channel = do_QueryInterface(aRequest, &rv); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_FAILED); + return rv; + } uint32_t status = 0; rv = channel->GetResponseStatus(&status); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_FAILED); + return rv; + } if (status != 200) { + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_FAILED); return NS_ERROR_NOT_AVAILABLE; } @@ -285,11 +329,15 @@ PendingLookup::OnStopRequestInternal(nsIRequest *aRequest, safe_browsing::ClientDownloadResponse response; if (!response.ParseFromString(buf)) { NS_WARNING("Could not parse protocol buffer"); + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_INVALID); return NS_ERROR_CANNOT_CONVERT_DATA; } // There are several more verdicts, but we only respect one for now and treat // everything else as SAFE. + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER, + SERVER_RESPONSE_VALID); if (response.verdict() == safe_browsing::ClientDownloadResponse::DANGEROUS) { *aShouldBlock = true; } @@ -335,6 +383,7 @@ ApplicationReputationService::QueryReputation( NS_ENSURE_ARG_POINTER(aQuery); NS_ENSURE_ARG_POINTER(aCallback); + Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true); nsresult rv = QueryReputationInternal(aQuery, aCallback); if (NS_FAILED(rv)) { aCallback->OnComplete(false, rv); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index a8e5707d4a06..6a2cab5804b6 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -23,6 +23,24 @@ "extended_statistics_ok": true, "description": "time spent updating accessibility (ms)" }, + "APPLICATION_REPUTATION_SHOULD_BLOCK": { + "kind": "boolean", + "description": "Application reputation verdict (shouldBlock=false is OK)" + }, + "APPLICATION_REPUTATION_LOCAL": { + "kind": "enumerated", + "n_values": 3, + "description": "Application reputation local results (0=ALLOW, 1=BLOCK, 2=NONE)" + }, + "APPLICATION_REPUTATION_SERVER": { + "kind": "enumerated", + "n_values": 3, + "description": "Application reputation remote status (0 = OK, 1 = FAIL, 2 = INVALID)" + }, + "APPLICATION_REPUTATION_COUNT": { + "kind": "flag", + "description": "Application reputation query count (both local and remote)" + }, "BACKGROUNDFILESAVER_THREAD_COUNT": { "kind": "enumerated", "n_values": 21, From f88abf8fd3685219f933ab32861b80d0246300f3 Mon Sep 17 00:00:00 2001 From: Stephen Pohl Date: Fri, 13 Dec 2013 17:43:30 -0500 Subject: [PATCH 101/459] Bug 944362: Ensure that scroll updates get propagated to apz. r=jimm --- browser/metro/base/content/bindings/browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/metro/base/content/bindings/browser.js b/browser/metro/base/content/bindings/browser.js index 4038ffa2ecdd..56017ef960bc 100644 --- a/browser/metro/base/content/bindings/browser.js +++ b/browser/metro/base/content/bindings/browser.js @@ -652,7 +652,7 @@ let ContentScroll = { isRoot = true; } } else { - var window = target.currentDoc.defaultView; + var window = target.ownerDocument.defaultView; var scrollOffset = this.getScrollOffsetForElement(target); var element = target; } From 724ee75bf2cde28e1d2140fe320dd78b456a5d67 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Tue, 12 Nov 2013 14:02:24 -0800 Subject: [PATCH 102/459] Bug 927685 - Enable --ion-parallel-compile=on by default in the shell; r=jandem,sr=jorendorff * * * Bug 927685 - Followup to fix bustage in --disable-threadsafe builds on a CLOSED TREE; r=bustage --HG-- extra : rebase_source : b6f59f7f173e0e7c34b60cd0268cc9c36a0f485a --- js/src/jit-test/jit_test.py | 12 ++---------- js/src/shell/js.cpp | 23 ++++++----------------- js/src/tests/jstests.py | 28 +++++++++++----------------- js/src/tests/lib/tests.py | 11 +++++++++++ 4 files changed, 30 insertions(+), 44 deletions(-) diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index 37b47dca4286..48db63fb3fd4 100755 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -15,6 +15,7 @@ def add_libdir_to_path(): add_libdir_to_path() import jittests +from tests import TBPL_FLAGS def main(argv): @@ -161,17 +162,8 @@ def main(argv): job_list = [] if options.tbpl: # Running all bits would take forever. Instead, we test a few interesting combinations. - flags = [ - [], # no flags, normal baseline and ion - ['--ion-eager'], # implies --baseline-eager - ['--ion-eager', '--ion-check-range-analysis', '--no-sse3'], - ['--baseline-eager'], - ['--baseline-eager', '--no-ti', '--no-fpu'], - ['--no-baseline', '--no-ion'], - ['--no-baseline', '--no-ion', '--no-ti'], - ] for test in test_list: - for variant in flags: + for variant in TBPL_FLAGS: new_test = test.copy() new_test.jitflags.extend(variant) job_list.append(new_test) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 96d3553a7ebc..193620038e54 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5567,25 +5567,16 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op) if (op->getBoolOption("ion-compile-try-catch")) jit::js_IonOptions.compileTryCatch = true; -#ifdef JS_THREADSAFE - bool parallelCompilation = false; + bool parallelCompilation = true; if (const char *str = op->getStringOption("ion-parallel-compile")) { - if (strcmp(str, "on") == 0) { - if (cx->runtime()->workerThreadCount() == 0) { - fprintf(stderr, "Parallel compilation not available without helper threads"); - return EXIT_FAILURE; - } - parallelCompilation = true; - } else if (strcmp(str, "off") != 0) { + if (strcmp(str, "off") == 0) + parallelCompilation = false; + else if (strcmp(str, "on") != 0) return OptionFailure("ion-parallel-compile", str); - } } - /* - * Note: In shell builds, parallel compilation is only enabled with an - * explicit option. - */ +#ifdef JS_THREADSAFE cx->runtime()->setParallelIonCompilationEnabled(parallelCompilation); -#endif /* JS_THREADSAFE */ +#endif #endif /* JS_ION */ @@ -5838,10 +5829,8 @@ main(int argc, char **argv, char **envp) " stupid: Simple block local register allocation") || !op.addBoolOption('\0', "ion-eager", "Always ion-compile methods (implies --baseline-eager)") || !op.addBoolOption('\0', "ion-compile-try-catch", "Ion-compile try-catch statements") -#ifdef JS_THREADSAFE || !op.addStringOption('\0', "ion-parallel-compile", "on/off", "Compile scripts off thread (default: off)") -#endif || !op.addBoolOption('\0', "baseline", "Enable baseline compiler (default)") || !op.addBoolOption('\0', "no-baseline", "Disable baseline compiler") || !op.addBoolOption('\0', "baseline-eager", "Always baseline-compile methods") diff --git a/js/src/tests/jstests.py b/js/src/tests/jstests.py index 229b4571dabe..aefcd2554c60 100755 --- a/js/src/tests/jstests.py +++ b/js/src/tests/jstests.py @@ -11,7 +11,7 @@ from copy import copy from subprocess import list2cmdline, call from lib.results import NullTestOutput -from lib.tests import TestCase +from lib.tests import TestCase, TBPL_FLAGS from lib.results import ResultsSink from lib.progressbar import ProgressBar @@ -86,8 +86,9 @@ def parse_args(): help='Set maximum time a test is allows to run (in seconds).') harness_og.add_option('-a', '--args', dest='shell_args', default='', help='Extra args to pass to the JS shell.') - harness_og.add_option('--jitflags', default='', - help='Example: --jitflags=m,amd to run each test with -m, -a -m -d [default=%default]') + harness_og.add_option('--jitflags', default='', help="Obsolete. Does nothing.") + harness_og.add_option('--tbpl', action='store_true', + help='Runs each test in all configurations tbpl tests.') harness_og.add_option('-g', '--debug', action='store_true', help='Run a test in debugger.') harness_og.add_option('--debugger', default='gdb -q --args', help='Debugger command.') harness_og.add_option('-J', '--jorendb', action='store_true', help='Run under JS debugger.') @@ -206,16 +207,6 @@ def parse_args(): return (options, requested_paths, excluded_paths) -def parse_jitflags(op_jitflags): - jitflags = [ [ '-' + flag for flag in flags ] - for flags in op_jitflags.split(',') ] - for flags in jitflags: - for flag in flags: - if flag not in ('-m', '-a', '-p', '-d', '-n'): - print('Invalid jit flag: "%s"'%flag) - sys.exit(1) - return jitflags - def load_tests(options, requested_paths, excluded_paths): """ Returns a tuple: (skipped_tests, test_list) @@ -243,18 +234,21 @@ def load_tests(options, requested_paths, excluded_paths): manifest.make_manifests(options.make_manifests, test_list) sys.exit() - # Create a new test list. Apply each JIT configuration to every test. - if options.jitflags: + # Create a new test list. Apply each TBPL configuration to every test. + if options.tbpl: new_test_list = [] - jitflags_list = parse_jitflags(options.jitflags) + flags_list = TBPL_FLAGS for test in test_list: - for jitflags in jitflags_list: + for jitflags in flags_list: tmp_test = copy(test) tmp_test.options = copy(test.options) tmp_test.options.extend(jitflags) new_test_list.append(tmp_test) test_list = new_test_list + if options.jitflags: + print("Warning: the --jitflags option is obsolete and does nothing now.") + if options.test_file: paths = set() for test_file in options.test_file: diff --git a/js/src/tests/lib/tests.py b/js/src/tests/lib/tests.py index 4b66dcc3e908..92cee4394025 100644 --- a/js/src/tests/lib/tests.py +++ b/js/src/tests/lib/tests.py @@ -9,6 +9,17 @@ from threading import Thread from results import TestOutput +# When run on tbpl, we run each test multiple times with the following arguments. +TBPL_FLAGS = [ + [], # no flags, normal baseline and ion + ['--ion-eager', '--ion-parallel-compile=off'], # implies --baseline-eager + ['--ion-eager', '--ion-parallel-compile=off', '--ion-check-range-analysis', '--no-sse3'], + ['--baseline-eager'], + ['--baseline-eager', '--no-ti', '--no-fpu'], + ['--no-baseline', '--no-ion'], + ['--no-baseline', '--no-ion', '--no-ti'], +] + def do_run_cmd(cmd): l = [ None, None ] th_run_cmd(cmd, l) From 0dd4e7e1640e36ef2c265ec304c190c2bbd827f5 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Mon, 18 Nov 2013 13:02:11 -0800 Subject: [PATCH 103/459] Bug 928195 - Part 3: Consolidate all WebIDL Python logic into mozwebidl module; r=bz, froydnj --HG-- extra : rebase_source : 050f1c3eb3bccdf369164be4e58c59fa71c19060 --- build/virtualenv_packages.txt | 2 + dom/bindings/Makefile.in | 2 + dom/bindings/mozwebidlcodegen/__init__.py | 523 ++++++++++++++++++ .../mozwebidlcodegen/test/Child.webidl | 3 + .../mozwebidlcodegen/test/DummyBinding.webidl | 2 + .../test/ExampleBinding.webidl | 3 + .../mozwebidlcodegen/test/Parent.webidl | 3 + .../mozwebidlcodegen/test/TestEvent.webidl | 13 + .../test/test_mozwebidlcodegen.py | 278 ++++++++++ 9 files changed, 829 insertions(+) create mode 100644 dom/bindings/mozwebidlcodegen/__init__.py create mode 100644 dom/bindings/mozwebidlcodegen/test/Child.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/DummyBinding.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/Parent.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/TestEvent.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py diff --git a/build/virtualenv_packages.txt b/build/virtualenv_packages.txt index aade8e7598e3..4fd374decd99 100644 --- a/build/virtualenv_packages.txt +++ b/build/virtualenv_packages.txt @@ -13,6 +13,8 @@ mock.pth:python/mock-1.0.0 mozilla.pth:build mozilla.pth:config mozilla.pth:xpcom/typelib/xpt/tools +mozilla.pth:dom/bindings +mozilla.pth:dom/bindings/parser moztreedocs.pth:tools/docs copy:build/buildconfig.py packages.txt:testing/mozbase/packages.txt diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in index 8bc94e224f03..a4a029ae5964 100644 --- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -84,6 +84,8 @@ globalgen_headers_DEST = $(ABS_DIST)/include/mozilla/dom globalgen_headers_TARGET := export INSTALL_TARGETS += globalgen_headers +PYTHON_UNIT_TESTS += $(srcdir)/mozwebidlcodegen/test/test_mozwebidlcodegen.py + include $(topsrcdir)/config/rules.mk ifdef GNU_CC diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py new file mode 100644 index 000000000000..4fc5d3bbd046 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/__init__.py @@ -0,0 +1,523 @@ +# 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/. + +# This module contains code for managing WebIDL files and bindings for +# the build system. + +from __future__ import unicode_literals + +import errno +import hashlib +import json +import logging +import os + +from copy import deepcopy + +from mach.mixin.logging import LoggingMixin + +from mozbuild.makeutil import Makefile +from mozbuild.pythonutil import iter_modules_in_path +from mozbuild.util import FileAvoidWrite + +import mozpack.path as mozpath + +import WebIDL +from Codegen import ( + CGBindingRoot, + CGEventRoot, + CGExampleRoot, + GlobalGenRoots, +) +from Configuration import Configuration + + +class BuildResult(object): + """Represents the result of processing WebIDL files. + + This holds a summary of output file generation during code generation. + """ + + def __init__(self): + # The .webidl files that had their outputs regenerated. + self.inputs = set() + + # The output files that were created. + self.created = set() + + # The output files that changed. + self.updated = set() + + # The output files that didn't change. + self.unchanged = set() + + +class WebIDLCodegenManagerState(dict): + """Holds state for the WebIDL code generation manager. + + State is currently just an extended dict. The internal implementation of + state should be considered a black box to everyone except + WebIDLCodegenManager. But we'll still document it. + + Fields: + + version + The integer version of the format. This is to detect incompatible + changes between state. It should be bumped whenever the format + changes or semantics change. + + webidls + A dictionary holding information about every known WebIDL input. + Keys are the basenames of input WebIDL files. Values are dicts of + metadata. Keys in those dicts are: + + * filename - The full path to the input filename. + * inputs - A set of full paths to other webidl files this webidl + depends on. + * outputs - Set of full output paths that are created/derived from + this file. + * sha1 - The hexidecimal SHA-1 of the input filename from the last + processing time. + + global_inputs + A dictionary defining files that influence all processing. Keys + are full filenames. Values are hexidecimal SHA-1 from the last + processing time. + """ + + VERSION = 1 + + def __init__(self, fh=None): + self['version'] = self.VERSION + self['webidls'] = {} + self['global_depends'] = {} + + if not fh: + return + + state = json.load(fh) + if state['version'] != self.VERSION: + raise Exception('Unknown state version: %s' % state['version']) + + self['version'] = state['version'] + self['global_depends'] = state['global_depends'] + + for k, v in state['webidls'].items(): + self['webidls'][k] = v + + # Sets are converted to lists for serialization because JSON + # doesn't support sets. + self['webidls'][k]['inputs'] = set(v['inputs']) + self['webidls'][k]['outputs'] = set(v['outputs']) + + def dump(self, fh): + """Dump serialized state to a file handle.""" + normalized = deepcopy(self) + + for k, v in self['webidls'].items(): + # Convert sets to lists because JSON doesn't support sets. + normalized['webidls'][k]['outputs'] = sorted(v['outputs']) + normalized['webidls'][k]['inputs'] = sorted(v['inputs']) + + json.dump(normalized, fh, sort_keys=True) + + +class WebIDLCodegenManager(LoggingMixin): + """Manages all code generation around WebIDL. + + To facilitate testing, this object is meant to be generic and reusable. + Paths, etc should be parameters and not hardcoded. + """ + + # Global parser derived declaration files. + GLOBAL_DECLARE_FILES = { + 'GeneratedAtomList.h', + 'PrototypeList.h', + 'RegisterBindings.h', + 'UnionConversions.h', + 'UnionTypes.h', + } + + # Global parser derived definition files. + GLOBAL_DEFINE_FILES = { + 'RegisterBindings.cpp', + 'UnionTypes.cpp', + } + + # Example interfaces to build along with the tree. Other example + # interfaces will need to be generated manually. + BUILD_EXAMPLE_INTERFACES = { + 'TestExampleInterface', + 'TestExampleProxyInterface', + } + + def __init__(self, config_path, inputs, exported_header_dir, + codegen_dir, state_path, cache_dir=None, make_deps_path=None, + make_deps_target=None): + """Create an instance that manages WebIDLs in the build system. + + config_path refers to a WebIDL config file (e.g. Bindings.conf). + inputs is a 3-tuple describing the input .webidl files and how to + process them. Members are: + (set(.webidl files), set(basenames of exported files), + set(basenames of generated events files)) + + exported_header_dir and codegen_dir are directories where generated + files will be written to. + state_path is the path to a file that will receive JSON state from our + actions. + make_deps_path is the path to a make dependency file that we can + optionally write. + make_deps_target is the target that receives the make dependencies. It + must be defined if using make_deps_path. + """ + self.populate_logger() + + input_paths, exported_stems, generated_events_stems = inputs + + self._config_path = config_path + self._input_paths = set(input_paths) + self._exported_stems = set(exported_stems) + self._generated_events_stems = set(generated_events_stems) + self._exported_header_dir = exported_header_dir + self._codegen_dir = codegen_dir + self._state_path = state_path + self._cache_dir = cache_dir + self._make_deps_path = make_deps_path + self._make_deps_target = make_deps_target + + if (make_deps_path and not make_deps_target) or (not make_deps_path and + make_deps_target): + raise Exception('Must define both make_deps_path and make_deps_target ' + 'if one is defined.') + + self._parser_results = None + self._config = None + self._state = WebIDLCodegenManagerState() + + if os.path.exists(state_path): + with open(state_path, 'rb') as fh: + try: + self._state = WebIDLCodegenManagerState(fh=fh) + except Exception as e: + self.log(logging.WARN, 'webidl_bad_state', {'msg': str(e)}, + 'Bad WebIDL state: {msg}') + + @property + def config(self): + if not self._config: + self._parse_webidl() + + return self._config + + def generate_build_files(self): + """Generate files required for the build. + + This function is in charge of generating all the .h/.cpp files derived + from input .webidl files. Please note that there are build actions + required to produce .webidl files and these build actions are + explicitly not captured here: this function assumes all .webidl files + are present and up to date. + + This routine is called as part of the build to ensure files that need + to exist are present and up to date. This routine may not be called if + the build dependencies (generated as a result of calling this the first + time) say everything is up to date. + + Because reprocessing outputs for every .webidl on every invocation + is expensive, we only regenerate the minimal set of files on every + invocation. The rules for deciding what needs done are roughly as + follows: + + 1. If any .webidl changes, reparse all .webidl files and regenerate + the global derived files. Only regenerate output files (.h/.cpp) + impacted by the modified .webidl files. + 2. If an non-.webidl dependency (Python files, config file) changes, + assume everything is out of date and regenerate the world. This + is because changes in those could globally impact every output + file. + 3. If an output file is missing, ensure it is present by performing + necessary regeneration. + """ + # Despite #1 above, we assume the build system is smart enough to not + # invoke us if nothing has changed. Therefore, any invocation means + # something has changed. And, if anything has changed, we need to + # parse the WebIDL. + self._parse_webidl() + + result = BuildResult() + + # If we parse, we always update globals - they are cheap and it is + # easier that way. + created, updated, unchanged = self._write_global_derived() + result.created |= created + result.updated |= updated + result.unchanged |= unchanged + + # If any of the extra dependencies changed, regenerate the world. + global_changed, global_hashes = self._global_dependencies_changed() + if global_changed: + # Make a copy because we may modify. + changed_inputs = set(self._input_paths) + else: + changed_inputs = self._compute_changed_inputs() + + self._state['global_depends'] = global_hashes + + # Generate bindings from .webidl files. + for filename in sorted(changed_inputs): + basename = mozpath.basename(filename) + result.inputs.add(filename) + written, deps = self._generate_build_files_for_webidl(filename) + result.created |= written[0] + result.updated |= written[1] + result.unchanged |= written[2] + + self._state['webidls'][basename] = dict( + filename=filename, + outputs=written[0] | written[1] | written[2], + inputs=set(deps), + sha1=self._input_hashes[filename], + ) + + # Process some special interfaces required for testing. + for interface in self.BUILD_EXAMPLE_INTERFACES: + written = self.generate_example_files(interface) + result.created |= written[0] + result.updated |= written[1] + result.unchanged |= written[2] + + # Generate a make dependency file. + if self._make_deps_path: + mk = Makefile() + codegen_rule = mk.create_rule([self._make_deps_target]) + codegen_rule.add_dependencies(global_hashes.keys()) + codegen_rule.add_dependencies(self._input_paths) + + with FileAvoidWrite(self._make_deps_path) as fh: + mk.dump(fh) + + self._save_state() + + return result + + def generate_example_files(self, interface): + """Generates example files for a given interface.""" + root = CGExampleRoot(self.config, interface) + + return self._maybe_write_codegen(root, *self._example_paths(interface)) + + def _parse_webidl(self): + self.log(logging.INFO, 'webidl_parse', + {'count': len(self._input_paths)}, + 'Parsing {count} WebIDL files.') + + hashes = {} + parser = WebIDL.Parser(self._cache_dir) + + for path in sorted(self._input_paths): + with open(path, 'rb') as fh: + data = fh.read() + hashes[path] = hashlib.sha1(data).hexdigest() + parser.parse(data, path) + + self._parser_results = parser.finish() + self._config = Configuration(self._config_path, self._parser_results) + self._input_hashes = hashes + + def _write_global_derived(self): + things = [('declare', f) for f in self.GLOBAL_DECLARE_FILES] + things.extend(('define', f) for f in self.GLOBAL_DEFINE_FILES) + + result = (set(), set(), set()) + + for what, filename in things: + stem = mozpath.splitext(filename)[0] + root = getattr(GlobalGenRoots, stem)(self._config) + + if what == 'declare': + code = root.declare() + output_root = self._exported_header_dir + elif what == 'define': + code = root.define() + output_root = self._codegen_dir + else: + raise Exception('Unknown global gen type: %s' % what) + + output_path = mozpath.join(output_root, filename) + self._maybe_write_file(output_path, code, result) + + return result + + def _compute_changed_inputs(self): + """Compute the set of input files that need to be regenerated.""" + changed_inputs = set() + expected_outputs = self.expected_build_output_files() + + # Look for missing output files. + if any(not os.path.exists(f) for f in expected_outputs): + # FUTURE Bug 940469 Only regenerate minimum set. + changed_inputs |= self._input_paths + + # That's it for examining output files. We /could/ examine SHA-1's of + # output files from a previous run to detect modifications. But that's + # a lot of extra work and most build systems don't do that anyway. + + # Now we move on to the input files. + old_hashes = {v['filename']: v['sha1'] + for v in self._state['webidls'].values()} + + old_filenames = set(old_hashes.keys()) + new_filenames = self._input_paths + + # If an old file has disappeared or a new file has arrived, mark + # it. + changed_inputs |= old_filenames ^ new_filenames + + # For the files in common between runs, compare content. If the file + # has changed, mark it. We don't need to perform mtime comparisons + # because content is a stronger validator. + for filename in old_filenames & new_filenames: + if old_hashes[filename] != self._input_hashes[filename]: + changed_inputs.add(filename) + + # We've now populated the base set of inputs that have changed. + + # Inherit dependencies from previous run. The full set of dependencies + # is associated with each record, so we don't need to perform any fancy + # graph traversal. + for v in self._state['webidls'].values(): + if any(dep for dep in v['inputs'] if dep in changed_inputs): + changed_inputs.add(v['filename']) + + # Ensure all changed inputs actually exist (some changed inputs could + # have been from deleted files). + return set(f for f in changed_inputs if os.path.exists(f)) + + def _binding_info(self, p): + """Compute binding metadata for an input path. + + Returns a tuple of: + + (stem, binding_stem, is_event, output_files) + + output_files is itself a tuple. The first two items are the binding + header and C++ paths, respectively. The 2nd pair are the event header + and C++ paths or None if this isn't an event binding. + """ + basename = mozpath.basename(p) + stem = mozpath.splitext(basename)[0] + binding_stem = '%sBinding' % stem + + if stem in self._exported_stems: + header_dir = self._exported_header_dir + else: + header_dir = self._codegen_dir + + is_event = stem in self._generated_events_stems + + files = ( + mozpath.join(header_dir, '%s.h' % binding_stem), + mozpath.join(self._codegen_dir, '%s.cpp' % binding_stem), + mozpath.join(header_dir, '%s.h' % stem) if is_event else None, + mozpath.join(self._codegen_dir, '%s.cpp' % stem) if is_event else None, + ) + + return stem, binding_stem, is_event, header_dir, files + + def _example_paths(self, interface): + return ( + mozpath.join(self._codegen_dir, '%s-example.h' % interface), + mozpath.join(self._codegen_dir, '%s-example.cpp' % interface)) + + def expected_build_output_files(self): + """Obtain the set of files generate_build_files() should write.""" + paths = set() + + # Account for global generation. + for p in self.GLOBAL_DECLARE_FILES: + paths.add(mozpath.join(self._exported_header_dir, p)) + for p in self.GLOBAL_DEFINE_FILES: + paths.add(mozpath.join(self._codegen_dir, p)) + + for p in self._input_paths: + stem, binding_stem, is_event, header_dir, files = self._binding_info(p) + paths |= {f for f in files if f} + + for interface in self.BUILD_EXAMPLE_INTERFACES: + for p in self._example_paths(interface): + paths.add(p) + + return paths + + def _generate_build_files_for_webidl(self, filename): + self.log(logging.INFO, 'webidl_generate_build_for_input', + {'filename': filename}, + 'Generating WebIDL files derived from {filename}') + + stem, binding_stem, is_event, header_dir, files = self._binding_info(filename) + root = CGBindingRoot(self._config, binding_stem, filename) + + result = self._maybe_write_codegen(root, files[0], files[1]) + + if is_event: + generated_event = CGEventRoot(self._config, stem) + result = self._maybe_write_codegen(generated_event, files[2], + files[3], result) + + return result, root.deps() + + def _global_dependencies_changed(self): + """Determine whether the global dependencies have changed.""" + current_files = set(iter_modules_in_path(mozpath.dirname(__file__))) + + # We need to catch other .py files from /dom/bindings. We assume these + # are in the same directory as the config file. + current_files |= set(iter_modules_in_path(mozpath.dirname(self._config_path))) + + current_files.add(self._config_path) + + current_hashes = {} + for f in current_files: + # This will fail if the file doesn't exist. If a current global + # dependency doesn't exist, something else is wrong. + with open(f, 'rb') as fh: + current_hashes[f] = hashlib.sha1(fh.read()).hexdigest() + + # The set of files has changed. + if current_files ^ set(self._state['global_depends'].keys()): + return True, current_hashes + + # Compare hashes. + for f, sha1 in current_hashes.items(): + if sha1 != self._state['global_depends'][f]: + return True, current_hashes + + return False, current_hashes + + def _save_state(self): + with open(self._state_path, 'wb') as fh: + self._state.dump(fh) + + def _maybe_write_codegen(self, obj, declare_path, define_path, result=None): + assert declare_path and define_path + if not result: + result = (set(), set(), set()) + + self._maybe_write_file(declare_path, obj.declare(), result) + self._maybe_write_file(define_path, obj.define(), result) + + return result + + def _maybe_write_file(self, path, content, result): + fh = FileAvoidWrite(path) + fh.write(content) + existed, updated = fh.close() + + if not existed: + result[0].add(path) + elif updated: + result[1].add(path) + else: + result[2].add(path) diff --git a/dom/bindings/mozwebidlcodegen/test/Child.webidl b/dom/bindings/mozwebidlcodegen/test/Child.webidl new file mode 100644 index 000000000000..f7d0a76c9d3f --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/Child.webidl @@ -0,0 +1,3 @@ +interface Child : Parent { + void ChildBaz(); +}; diff --git a/dom/bindings/mozwebidlcodegen/test/DummyBinding.webidl b/dom/bindings/mozwebidlcodegen/test/DummyBinding.webidl new file mode 100644 index 000000000000..e2ccc4b69745 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/DummyBinding.webidl @@ -0,0 +1,2 @@ +interface DummyInterface {}; +interface DummyInterfaceWorkers {}; diff --git a/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl b/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl new file mode 100644 index 000000000000..34794993fe4a --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl @@ -0,0 +1,3 @@ +/* These interfaces are hard-coded and need to be defined. */ +interface TestExampleInterface {}; +interface TestExampleProxyInterface {}; diff --git a/dom/bindings/mozwebidlcodegen/test/Parent.webidl b/dom/bindings/mozwebidlcodegen/test/Parent.webidl new file mode 100644 index 000000000000..423f364aea23 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/Parent.webidl @@ -0,0 +1,3 @@ +interface Parent { + void MethodFoo(); +}; diff --git a/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl b/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl new file mode 100644 index 000000000000..db085629186c --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl @@ -0,0 +1,13 @@ +interface EventTarget { + void addEventListener(); +}; + +interface Event {}; + +callback EventHandlerNonNull = any (Event event); +typedef EventHandlerNonNull? EventHandler; + +[NoInterfaceObject] +interface TestEvent : EventTarget { + attribute EventHandler onfoo; +}; diff --git a/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py b/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py new file mode 100644 index 000000000000..fd35e8c63485 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py @@ -0,0 +1,278 @@ +# 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/. + +from __future__ import unicode_literals + +import imp +import json +import os +import shutil +import sys +import tempfile +import unittest + +import mozpack.path as mozpath + +from mozwebidlcodegen import ( + WebIDLCodegenManager, + WebIDLCodegenManagerState, +) + +from mozfile import NamedTemporaryFile + +from mozunit import ( + MockedOpen, + main, +) + + +OUR_DIR = mozpath.abspath(mozpath.dirname(__file__)) +TOPSRCDIR = mozpath.normpath(mozpath.join(OUR_DIR, '..', '..', '..', '..')) + + +class TestWebIDLCodegenManager(unittest.TestCase): + TEST_STEMS = { + 'Child', + 'Parent', + 'ExampleBinding', + 'TestEvent', + } + + @property + def _static_input_paths(self): + s = {mozpath.join(OUR_DIR, p) for p in os.listdir(OUR_DIR) + if p.endswith('.webidl')} + + return s + + @property + def _config_path(self): + config = mozpath.join(TOPSRCDIR, 'dom', 'bindings', 'Bindings.conf') + self.assertTrue(os.path.exists(config)) + + return config + + def _get_manager_args(self): + tmp = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, tmp) + + cache_dir = mozpath.join(tmp, 'cache') + os.mkdir(cache_dir) + + ip = self._static_input_paths + + inputs = ( + ip, + {mozpath.splitext(mozpath.basename(p))[0] for p in ip}, + set() + ) + + return dict( + config_path=self._config_path, + inputs=inputs, + exported_header_dir=mozpath.join(tmp, 'exports'), + codegen_dir=mozpath.join(tmp, 'codegen'), + state_path=mozpath.join(tmp, 'state.json'), + make_deps_path=mozpath.join(tmp, 'codegen.pp'), + make_deps_target='codegen.pp', + cache_dir=cache_dir, + ) + + def _get_manager(self): + return WebIDLCodegenManager(**self._get_manager_args()) + + def test_unknown_state_version(self): + """Loading a state file with a too new version resets state.""" + args = self._get_manager_args() + + p = args['state_path'] + + with open(p, 'wb') as fh: + json.dump({ + 'version': WebIDLCodegenManagerState.VERSION + 1, + 'foobar': '1', + }, fh) + + manager = WebIDLCodegenManager(**args) + + self.assertEqual(manager._state['version'], + WebIDLCodegenManagerState.VERSION) + self.assertNotIn('foobar', manager._state) + + def test_generate_build_files(self): + """generate_build_files() does the right thing from empty.""" + manager = self._get_manager() + result = manager.generate_build_files() + self.assertEqual(len(result.inputs), 5) + + output = manager.expected_build_output_files() + self.assertEqual(result.created, output) + self.assertEqual(len(result.updated), 0) + self.assertEqual(len(result.unchanged), 0) + + for f in output: + self.assertTrue(os.path.isfile(f)) + + for f in manager.GLOBAL_DECLARE_FILES: + self.assertIn(mozpath.join(manager._exported_header_dir, f), output) + + for f in manager.GLOBAL_DEFINE_FILES: + self.assertIn(mozpath.join(manager._codegen_dir, f), output) + + for s in self.TEST_STEMS: + self.assertTrue(os.path.isfile(mozpath.join( + manager._exported_header_dir, '%sBinding.h' % s))) + self.assertTrue(os.path.isfile(mozpath.join( + manager._codegen_dir, '%sBinding.cpp' % s))) + + self.assertTrue(os.path.isfile(manager._state_path)) + + with open(manager._state_path, 'rb') as fh: + state = json.load(fh) + self.assertEqual(state['version'], 1) + self.assertIn('webidls', state) + + child = state['webidls']['Child.webidl'] + self.assertEqual(len(child['inputs']), 2) + self.assertEqual(len(child['outputs']), 2) + self.assertEqual(child['sha1'], 'c41527cad3bc161fa6e7909e48fa11f9eca0468b') + + def test_generate_build_files_load_state(self): + """State should be equivalent when instantiating a new instance.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + self.assertEqual(len(m1._state['webidls']), 0) + m1.generate_build_files() + + m2 = WebIDLCodegenManager(**args) + self.assertGreater(len(m2._state['webidls']), 2) + self.assertEqual(m1._state, m2._state) + + def test_no_change_no_writes(self): + """If nothing changes, no files should be updated.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + + self.assertEqual(len(result.inputs), 0) + self.assertEqual(len(result.created), 0) + self.assertEqual(len(result.updated), 0) + + def test_output_file_regenerated(self): + """If an output file disappears, it is regenerated.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + rm_count = 0 + for p in m1._state['webidls']['Child.webidl']['outputs']: + rm_count += 1 + os.unlink(p) + + for p in m1.GLOBAL_DECLARE_FILES: + rm_count += 1 + os.unlink(mozpath.join(m1._exported_header_dir, p)) + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(len(result.created), rm_count) + + def test_only_rebuild_self(self): + """If an input file changes, only rebuild that one file.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + child_path = None + for p in m1._input_paths: + if p.endswith('Child.webidl'): + child_path = p + break + + self.assertIsNotNone(child_path) + child_content = open(child_path, 'rb').read() + + with MockedOpen({child_path: child_content + '\n/* */'}): + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(result.inputs, set([child_path])) + self.assertEqual(len(result.updated), 0) + self.assertEqual(len(result.created), 0) + + def test_rebuild_dependencies(self): + """Ensure an input file used by others results in others rebuilding.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + parent_path = None + child_path = None + for p in m1._input_paths: + if p.endswith('Parent.webidl'): + parent_path = p + elif p.endswith('Child.webidl'): + child_path = p + + self.assertIsNotNone(parent_path) + parent_content = open(parent_path, 'rb').read() + + with MockedOpen({parent_path: parent_content + '\n/* */'}): + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(result.inputs, {child_path, parent_path}) + self.assertEqual(len(result.updated), 0) + self.assertEqual(len(result.created), 0) + + def test_python_change_regenerate_everything(self): + """If a Python file changes, we should attempt to rebuild everything.""" + + # We don't want to mutate files in the source directory because we want + # to be able to build from a read-only filesystem. So, we install a + # dummy module and rewrite the metadata to say it comes from the source + # directory. + # + # Hacking imp to accept a MockedFile doesn't appear possible. So for + # the first iteration we read from a temp file. The second iteration + # doesn't need to import, so we are fine with a mocked file. + fake_path = mozpath.join(OUR_DIR, 'fakemodule.py') + with NamedTemporaryFile('wt') as fh: + fh.write('# Original content') + fh.flush() + mod = imp.load_source('mozwebidlcodegen.fakemodule', fh.name) + mod.__file__ = fake_path + + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + with MockedOpen({fake_path: '# Original content'}): + old_exists = os.path.exists + try: + def exists(p): + if p == fake_path: + return True + return old_exists(p) + + os.path.exists = exists + + result = m1.generate_build_files() + l = len(result.inputs) + + with open(fake_path, 'wt') as fh: + fh.write('# Modified content') + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(len(result.inputs), l) + + result = m2.generate_build_files() + self.assertEqual(len(result.inputs), 0) + finally: + os.path.exists = old_exists + del sys.modules['mozwebidlcodegen.fakemodule'] + + +if __name__ == '__main__': + main() From b47eeb6ab324dc64044dd09bb8b44abac4198ba0 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 12 Dec 2013 16:26:38 +0900 Subject: [PATCH 104/459] Bug 928195 - Part 4: Rewrite WebIDL build system integration; r=bz, r=glandium WebIDL build system integration has been rewritten from the ground up. Changes: * GlobalGen.py, BindingGen.py, and ExampleGen.py have been removed in favor of mozwebidl.py. * Static .webidl files are now processed directly in their original location and aren't copied to the object directory. * Generated events .cpp files are now compiled into the unified sources. Previously, only the Binding.cpp files were compiled into unified sources. * Exported .h files are now generated directly into their final location. Previously, they were generated into the local directory then installed in their final location. * The list of globalgen-generated files now lives in Python and isn't duplicated in 3 places. * The make dependencies are much simpler as a result of using a single command to perform all code generation. The auto-generated .pp file from code generation sets up all dependencies necessary to reinvoke code generation and Python takes care of dependency management. --HG-- extra : rebase_source : e4918878274b22a412329c7cb18cc7138daf5dc6 --- CLOBBER | 7 +- dom/bindings/BindingGen.py | 98 ------- dom/bindings/Codegen.py | 21 -- dom/bindings/ExampleGen.py | 46 --- dom/bindings/GlobalGen.py | 81 ----- dom/bindings/Makefile.in | 276 ++++-------------- dom/bindings/moz.build | 2 + dom/bindings/mozwebidlcodegen/__init__.py | 42 +++ dom/bindings/test/Makefile.in | 80 +---- dom/bindings/test/moz.build | 13 +- dom/moz.build | 3 - dom/webidl/moz.build | 12 - python/mozbuild/mozbuild/action/webidl.py | 17 ++ python/mozbuild/mozbuild/backend/common.py | 108 ++++++- .../mozbuild/backend/recursivemake.py | 179 ++++++------ python/mozbuild/mozbuild/base.py | 4 + 16 files changed, 351 insertions(+), 638 deletions(-) delete mode 100644 dom/bindings/BindingGen.py delete mode 100644 dom/bindings/ExampleGen.py delete mode 100644 dom/bindings/GlobalGen.py create mode 100644 python/mozbuild/mozbuild/action/webidl.py diff --git a/CLOBBER b/CLOBBER index 91acfa9390b2..f61ddd42af5e 100644 --- a/CLOBBER +++ b/CLOBBER @@ -18,4 +18,9 @@ # Modifying this file will now automatically clobber the buildbot machines \o/ # -Bug 887836 - webidl changes require a Windows clobber. +# Are you updating CLOBBER because you think it's needed for your WebIDL +# changes to stick? As of bug 928195, this shouldn't be necessary! Please +# don't change CLOBBER for WebIDL changes any more. +Bug 928195 rewrote WebIDL build system integration from the ground up. This +will hopefully be the last required clobber due to WebIDLs poorly interacting +with the build system. diff --git a/dom/bindings/BindingGen.py b/dom/bindings/BindingGen.py deleted file mode 100644 index e064270513f5..000000000000 --- a/dom/bindings/BindingGen.py +++ /dev/null @@ -1,98 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -import os -import cPickle -from Configuration import Configuration -from Codegen import CGBindingRoot, replaceFileIfChanged, CGEventRoot -from mozbuild.makeutil import Makefile -from mozbuild.pythonutil import iter_modules_in_path -from buildconfig import topsrcdir - - -def generate_binding_files(config, outputprefix, srcprefix, webidlfile, - generatedEventsWebIDLFiles): - """ - |config| Is the configuration object. - |outputprefix| is a prefix to use for the header guards and filename. - """ - - depsname = ".deps/" + outputprefix + ".pp" - root = CGBindingRoot(config, outputprefix, webidlfile) - replaceFileIfChanged(outputprefix + ".h", root.declare()) - replaceFileIfChanged(outputprefix + ".cpp", root.define()) - - if webidlfile in generatedEventsWebIDLFiles: - eventName = webidlfile[:-len(".webidl")] - generatedEvent = CGEventRoot(config, eventName) - replaceFileIfChanged(eventName + ".h", generatedEvent.declare()) - replaceFileIfChanged(eventName + ".cpp", generatedEvent.define()) - - mk = Makefile() - # NOTE: it's VERY important that we output dependencies for the FooBinding - # file here, not for the header or generated cpp file. These dependencies - # are used later to properly determine changedDeps and prevent rebuilding - # too much. See the comment explaining $(binding_dependency_trackers) in - # Makefile.in. - rule = mk.create_rule([outputprefix]) - rule.add_dependencies(os.path.join(srcprefix, x) for x in sorted(root.deps())) - rule.add_dependencies(iter_modules_in_path(topsrcdir)) - with open(depsname, 'w') as f: - mk.dump(f) - -def main(): - # Parse arguments. - from optparse import OptionParser - usagestring = "usage: %prog [header|cpp] configFile outputPrefix srcPrefix webIDLFile" - o = OptionParser(usage=usagestring) - o.add_option("--verbose-errors", action='store_true', default=False, - help="When an error happens, display the Python traceback.") - (options, args) = o.parse_args() - - configFile = os.path.normpath(args[0]) - srcPrefix = os.path.normpath(args[1]) - - # Load the configuration - f = open('ParserResults.pkl', 'rb') - config = cPickle.load(f) - f.close() - - def readFile(f): - file = open(f, 'rb') - try: - contents = file.read() - finally: - file.close() - return contents - allWebIDLFiles = readFile(args[2]).split() - generatedEventsWebIDLFiles = readFile(args[3]).split() - changedDeps = readFile(args[4]).split() - - if all(f.endswith("Binding") or f == "ParserResults.pkl" for f in changedDeps): - toRegenerate = filter(lambda f: f.endswith("Binding"), changedDeps) - if len(toRegenerate) == 0 and len(changedDeps) == 1: - # Work around build system bug 874923: if we get here that means - # that changedDeps contained only one entry and it was - # "ParserResults.pkl". That should never happen: if the - # ParserResults.pkl changes then either one of the globalgen files - # changed (in which case we wouldn't be in this "only - # ParserResults.pkl and *Binding changed" code) or some .webidl - # files changed (and then the corresponding *Binding files should - # show up in changedDeps). Since clearly the build system is - # confused, just regenerate everything to be safe. - toRegenerate = allWebIDLFiles - else: - toRegenerate = map(lambda f: f[:-len("Binding")] + ".webidl", - toRegenerate) - else: - toRegenerate = allWebIDLFiles - - for webIDLFile in toRegenerate: - assert webIDLFile.endswith(".webidl") - outputPrefix = webIDLFile[:-len(".webidl")] + "Binding" - generate_binding_files(config, outputPrefix, srcPrefix, webIDLFile, - generatedEventsWebIDLFiles); - -if __name__ == '__main__': - main() diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index c32b821fa513..97272e5a1b22 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -29,27 +29,6 @@ INSTANCE_RESERVED_SLOTS = 3 def memberReservedSlot(member): return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex -def replaceFileIfChanged(filename, newContents): - """ - Read a copy of the old file, so that we don't touch it if it hasn't changed. - Returns True if the file was updated, false otherwise. - """ - oldFileContents = "" - try: - oldFile = open(filename, 'rb') - oldFileContents = ''.join(oldFile.readlines()) - oldFile.close() - except: - pass - - if newContents == oldFileContents: - return False - - f = open(filename, 'wb') - f.write(newContents) - f.close() - return True - def toStringBool(arg): return str(not not arg).lower() diff --git a/dom/bindings/ExampleGen.py b/dom/bindings/ExampleGen.py deleted file mode 100644 index 60eebe221a38..000000000000 --- a/dom/bindings/ExampleGen.py +++ /dev/null @@ -1,46 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -import os -import cPickle -from Configuration import Configuration -from Codegen import CGExampleRoot, replaceFileIfChanged - -def generate_interface_example(config, interfaceName): - """ - |config| Is the configuration object. - |interfaceName| is the name of the interface we're generating an example for. - """ - - root = CGExampleRoot(config, interfaceName) - exampleHeader = interfaceName + "-example.h" - exampleImpl = interfaceName + "-example.cpp" - replaceFileIfChanged(exampleHeader, root.declare()) - replaceFileIfChanged(exampleImpl, root.define()) - -def main(): - - # Parse arguments. - from optparse import OptionParser - usagestring = "usage: %prog configFile interfaceName" - o = OptionParser(usage=usagestring) - o.add_option("--verbose-errors", action='store_true', default=False, - help="When an error happens, display the Python traceback.") - (options, args) = o.parse_args() - - if len(args) != 2: - o.error(usagestring) - configFile = os.path.normpath(args[0]) - interfaceName = args[1] - - # Load the configuration - f = open('ParserResults.pkl', 'rb') - config = cPickle.load(f) - f.close() - - # Generate the example class. - generate_interface_example(config, interfaceName) - -if __name__ == '__main__': - main() diff --git a/dom/bindings/GlobalGen.py b/dom/bindings/GlobalGen.py deleted file mode 100644 index f3f02a4d1e48..000000000000 --- a/dom/bindings/GlobalGen.py +++ /dev/null @@ -1,81 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -# We do one global pass over all the WebIDL to generate our prototype enum -# and generate information for subsequent phases. - -import os -import WebIDL -import cPickle -from Configuration import Configuration -from Codegen import GlobalGenRoots, replaceFileIfChanged - -def generate_file(config, name, action): - - root = getattr(GlobalGenRoots, name)(config) - if action is 'declare': - filename = name + '.h' - code = root.declare() - else: - assert action is 'define' - filename = name + '.cpp' - code = root.define() - - if replaceFileIfChanged(filename, code): - print "Generating %s" % (filename) - else: - print "%s hasn't changed - not touching it" % (filename) - -def main(): - # Parse arguments. - from optparse import OptionParser - usageString = "usage: %prog [options] webidldir [files]" - o = OptionParser(usage=usageString) - o.add_option("--cachedir", dest='cachedir', default=None, - help="Directory in which to cache lex/parse tables.") - o.add_option("--verbose-errors", action='store_true', default=False, - help="When an error happens, display the Python traceback.") - (options, args) = o.parse_args() - - if len(args) < 2: - o.error(usageString) - - configFile = args[0] - baseDir = args[1] - fileList = args[2:] - - # Parse the WebIDL. - parser = WebIDL.Parser(options.cachedir) - for filename in fileList: - fullPath = os.path.normpath(os.path.join(baseDir, filename)) - f = open(fullPath, 'rb') - lines = f.readlines() - f.close() - parser.parse(''.join(lines), fullPath) - parserResults = parser.finish() - - # Load the configuration. - config = Configuration(configFile, parserResults) - - # Write the configuration out to a pickle. - resultsFile = open('ParserResults.pkl', 'wb') - cPickle.dump(config, resultsFile, -1) - resultsFile.close() - - # Generate the atom list. - generate_file(config, 'GeneratedAtomList', 'declare') - - # Generate the prototype list. - generate_file(config, 'PrototypeList', 'declare') - - # Generate the common code. - generate_file(config, 'RegisterBindings', 'declare') - generate_file(config, 'RegisterBindings', 'define') - - generate_file(config, 'UnionTypes', 'declare') - generate_file(config, 'UnionTypes', 'define') - generate_file(config, 'UnionConversions', 'declare') - -if __name__ == '__main__': - main() diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in index a4a029ae5964..418a39837eec 100644 --- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -1,245 +1,85 @@ # 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/. +# 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/. + +abs_dist := $(abspath $(DIST)) +webidl_base := $(topsrcdir)/dom/webidl -webidl_base = $(topsrcdir)/dom/webidl # Generated by moz.build include webidlsrcs.mk -binding_include_path := mozilla/dom -webidl_files += $(generated_events_webidl_files) -all_webidl_files = $(webidl_files) $(generated_webidl_files) $(preprocessed_webidl_files) - -# Set exported_binding_headers before adding the test IDL to the mix -exported_binding_headers := $(subst .webidl,Binding.h,$(all_webidl_files)) -exported_generated_events_headers := $(subst .webidl,.h,$(generated_events_webidl_files)) - -# Set linked_binding_cpp_files before adding the test IDL to the mix -linked_binding_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files)) -linked_generated_events_cpp_files := $(subst .webidl,.cpp,$(generated_events_webidl_files)) - -all_webidl_files += $(test_webidl_files) $(preprocessed_test_webidl_files) - -generated_header_files := $(subst .webidl,Binding.h,$(all_webidl_files)) $(exported_generated_events_headers) -generated_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files)) $(linked_generated_events_cpp_files) - -# We want to be able to only regenerate the .cpp and .h files that really need -# to change when a .webidl file changes. We do this by making the -# binding_dependency_trackers targets have dependencies on the right .webidl -# files via generated .pp files, having a .BindingGen target that depends on the -# binding_dependency_trackers and which has all the generated binding .h/.cpp -# depending on it, and then in the make commands for that target being able to -# check which exact binding_dependency_trackers changed. -binding_dependency_trackers := $(subst .webidl,Binding,$(all_webidl_files)) - -globalgen_targets := \ - GeneratedAtomList.h \ - PrototypeList.h \ - RegisterBindings.h \ - RegisterBindings.cpp \ - UnionTypes.h \ - UnionTypes.cpp \ - UnionConversions.h \ - $(NULL) - -# Nasty hack: when the test/Makefile.in invokes us to do codegen, it -# uses a target of -# "export TestExampleInterface-example TestExampleProxyInterface-example". -# We don't actually need to load our .o.pp files in that case, so just -# pretend like we have no CPPSRCS if that's the target. It makes the -# test cycle much faster, which is why we're doing it. -# -# XXXbz We could try to cheat even more and only include our CPPSRCS -# when $(MAKECMDGOALS) contains libs, so that we can skip loading all -# those .o.pp when trying to make a single .cpp file too, but that -# would break |make FooBinding.o(bj)|. Ah, well. -ifneq (export TestExampleInterface-example TestExampleProxyInterface-example,$(MAKECMDGOALS)) -CPPSRCS = \ - $(unified_binding_cpp_files) \ - $(linked_generated_events_cpp_files) \ - $(filter %.cpp, $(globalgen_targets)) \ - $(NULL) +ifdef GNU_CC +OS_CXXFLAGS += -Wno-uninitialized endif -ABS_DIST := $(abspath $(DIST)) +# These come from webidlsrcs.mk. +# TODO Write directly into backend.mk. +CPPSRCS += $(globalgen_sources) $(unified_binding_cpp_files) -EXTRA_EXPORT_MDDEPEND_FILES := $(addsuffix .pp,$(binding_dependency_trackers)) - -EXPORTS_GENERATED_FILES := $(exported_binding_headers) $(exported_generated_events_headers) -EXPORTS_GENERATED_DEST := $(ABS_DIST)/include/$(binding_include_path) -EXPORTS_GENERATED_TARGET := export -INSTALL_TARGETS += EXPORTS_GENERATED - -# Install auto-generated GlobalGen files. The rules for the install must -# be in the same target/subtier as GlobalGen.py, otherwise the files will not -# get installed into the appropriate location as they are generated. -globalgen_headers_FILES := \ - GeneratedAtomList.h \ - PrototypeList.h \ - RegisterBindings.h \ - UnionConversions.h \ - UnionTypes.h \ - $(NULL) -globalgen_headers_DEST = $(ABS_DIST)/include/mozilla/dom -globalgen_headers_TARGET := export -INSTALL_TARGETS += globalgen_headers +# Generated bindings reference *Binding.h, not mozilla/dom/*Binding.h. And, +# since we generate exported bindings directly to $(DIST)/include, we need +# to add that path to the search list. +# +# Ideally, binding generation uses the prefixed header file names. +# Bug 932092 tracks. +LOCAL_INCLUDES += -I$(DIST)/include/mozilla/dom PYTHON_UNIT_TESTS += $(srcdir)/mozwebidlcodegen/test/test_mozwebidlcodegen.py include $(topsrcdir)/config/rules.mk -ifdef GNU_CC -CXXFLAGS += -Wno-uninitialized -endif - -# If you change bindinggen_dependencies here, change it in -# dom/bindings/test/Makefile.in too. -bindinggen_dependencies := \ - BindingGen.py \ - Bindings.conf \ - Configuration.py \ - Codegen.py \ - ParserResults.pkl \ - parser/WebIDL.py \ +# TODO This list should be emitted to a .pp file via +# GenerateCSS2PropertiesWebIDL.py. +css2properties_dependencies = \ + $(topsrcdir)/layout/style/nsCSSPropList.h \ + $(topsrcdir)/layout/style/nsCSSPropAliasList.h \ + $(webidl_base)/CSS2Properties.webidl.in \ + $(webidl_base)/CSS2PropertiesProps.h \ + $(srcdir)/GenerateCSS2PropertiesWebIDL.py \ $(GLOBAL_DEPS) \ $(NULL) -CSS2Properties.webidl: $(topsrcdir)/layout/style/nsCSSPropList.h \ - $(topsrcdir)/layout/style/nsCSSPropAliasList.h \ - $(webidl_base)/CSS2Properties.webidl.in \ - $(webidl_base)/CSS2PropertiesProps.h \ - $(srcdir)/GenerateCSS2PropertiesWebIDL.py \ - $(GLOBAL_DEPS) - $(CPP) $(DEFINES) $(ACDEFINES) -I$(topsrcdir)/layout/style $(webidl_base)/CSS2PropertiesProps.h | \ - PYTHONDONTWRITEBYTECODE=1 $(PYTHON) \ - $(srcdir)/GenerateCSS2PropertiesWebIDL.py $(webidl_base)/CSS2Properties.webidl.in > CSS2Properties.webidl +CSS2Properties.webidl: $(css2properties_dependencies) + $(CPP) $(DEFINES) $(ACDEFINES) -I$(topsrcdir)/layout/style \ + $(webidl_base)/CSS2PropertiesProps.h | \ + PYTHONDONTWRITEBYTECODE=1 $(PYTHON) \ + $(srcdir)/GenerateCSS2PropertiesWebIDL.py \ + $(webidl_base)/CSS2Properties.webidl.in > $@ -$(webidl_files): %: $(webidl_base)/% - $(INSTALL) $(IFLAGS1) $(webidl_base)/$* . - -$(test_webidl_files): %: $(srcdir)/test/% - $(INSTALL) $(IFLAGS1) $(srcdir)/test/$* . - -# We can't easily use PP_TARGETS here because it insists on outputting targets -# that look like "$(CURDIR)/foo" whereas we want our target to just be "foo". -# Make sure to include $(GLOBAL_DEPS) so we pick up changes to what symbols are -# defined. Also make sure to remove $@ before writing to it, because otherwise -# if a file goes from non-preprocessed to preprocessed we can end up writing to -# a symlink, which will clobber files in the srcdir, which is bad. -$(preprocessed_webidl_files): %: $(webidl_base)/% $(GLOBAL_DEPS) - $(RM) $@ - $(call py_action,preprocessor, \ - $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $(webidl_base)/$* -o $@) - -# See the comment about PP_TARGETS for $(preprocessed_webidl_files) -$(preprocessed_test_webidl_files): %: $(srcdir)/test/% $(GLOBAL_DEPS) - $(RM) $@ - $(call py_action,preprocessor, \ - $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $(srcdir)/test/$* -o $@) - -# Make is dumb and can get confused between "foo" and "$(CURDIR)/foo". Make -# sure that the latter depends on the former, since the latter gets used in .pp -# files. -all_webidl_files_absolute = $(addprefix $(CURDIR)/,$(all_webidl_files)) -$(all_webidl_files_absolute): $(CURDIR)/%: % - -$(generated_header_files): .BindingGen - -$(generated_cpp_files): .BindingGen - -# $(binding_dependency_trackers) pick up additional dependencies via .pp files -# The rule: just brings the tracker up to date, if it's out of date, so that -# we'll know that we have to redo binding generation and flag this prerequisite -# there as being newer than the bindinggen target. -$(binding_dependency_trackers): - @$(TOUCH) $@ - -$(globalgen_targets): ParserResults.pkl - -%-example: .BindingGen - PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ - $(PLY_INCLUDE) -I$(srcdir)/parser \ - $(srcdir)/ExampleGen.py \ - $(srcdir)/Bindings.conf $* - -CACHE_DIR = _cache - -globalgen_dependencies := \ - GlobalGen.py \ - Bindings.conf \ - Configuration.py \ - Codegen.py \ - parser/WebIDL.py \ - webidlsrcs.mk \ - $(all_webidl_files) \ - $(CACHE_DIR)/.done \ +# Most of the logic for dependencies lives inside Python so it can be +# used by multiple build backends. We simply have rules to generate +# and include the .pp file. +# +# The generated .pp file contains all the important dependencies such as +# changes to .webidl or .py files should result in code generation being +# performed. +codegen_dependencies := \ + $(nonstatic_webidl_files) \ $(GLOBAL_DEPS) \ $(NULL) -$(CACHE_DIR)/.done: - $(MKDIR) -p $(CACHE_DIR) +$(call include_deps,codegen.pp) + +codegen.pp: $(codegen_dependencies) + $(call py_action,webidl,$(srcdir)) @$(TOUCH) $@ -# Running GlobalGen.py updates ParserResults.pkl as a side-effect -ParserResults.pkl: $(globalgen_dependencies) - $(info Generating global WebIDL files) - PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ - $(PLY_INCLUDE) -I$(srcdir)/parser \ - $(srcdir)/GlobalGen.py $(srcdir)/Bindings.conf . \ - --cachedir=$(CACHE_DIR) \ - $(all_webidl_files) - -$(globalgen_headers_FILES): ParserResults.pkl - -# Make sure .deps actually exists, since we'll try to write to it from -# BindingGen.py but we're typically running in the export phase, which is -# before anyone has bothered creating .deps. -# Then, pass our long lists through files to try to avoid blowing out the -# command line. -# Next, BindingGen.py will examine the changed dependency list to figure out -# what it really needs to regenerate. -# Finally, touch the .BindingGen file so that we don't have to keep redoing -# all that until something else actually changes. -.BindingGen: $(bindinggen_dependencies) $(binding_dependency_trackers) - $(info Generating WebIDL bindings) - $(MKDIR) -p .deps - echo $(all_webidl_files) > .all-webidl-file-list - echo $(generated_events_webidl_files) > .generated-events-webidl-files - echo $? > .changed-dependency-list - PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ - $(PLY_INCLUDE) -I$(srcdir)/parser \ - $(srcdir)/BindingGen.py \ - $(srcdir)/Bindings.conf \ - $(CURDIR) \ - .all-webidl-file-list \ - .generated-events-webidl-files \ - .changed-dependency-list - @$(TOUCH) $@ +.PHONY: compiletests +compiletests: + $(call SUBMAKE,libs,test) GARBAGE += \ - webidlyacc.py \ + codegen.pp \ + codegen.json \ parser.out \ - $(wildcard *-example.h) \ - $(wildcard *-example.cpp) \ - .BindingGen \ - .all-webidl-file-list \ - .generated-events-webidl-files \ - .changed-dependency-list \ - $(binding_dependency_trackers) \ + WebIDLGrammar.pkl \ + $(wildcard *.h) \ + $(wildcard *Binding.cpp) \ + $(wildcard *Event.cpp) \ + $(wildcard *-event.cpp) \ + $(wildcard *.webidl) \ $(NULL) -# Make sure all binding header files are created during the export stage, so we -# don't have issues with .cpp files being compiled before we've generated the -# headers they depend on. This is really only needed for the test files, since -# the non-test headers are all exported above anyway. Note that this means that -# we do all of our codegen during export. -export:: $(generated_header_files) - -distclean:: - -$(RM) \ - $(generated_header_files) \ - $(generated_cpp_files) \ - $(all_webidl_files) \ - $(globalgen_targets) \ - ParserResults.pkl \ - $(NULL) +DIST_GARBAGE += \ + file-lists.json \ + $(NULL) diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index bd46aa837b75..557d4a63e92f 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -4,6 +4,8 @@ # 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/. +TEST_DIRS += ['test'] + EXPORTS.mozilla += [ 'ErrorResult.h', ] diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py index 4fc5d3bbd046..5516dbb3a1a0 100644 --- a/dom/bindings/mozwebidlcodegen/__init__.py +++ b/dom/bindings/mozwebidlcodegen/__init__.py @@ -17,6 +17,7 @@ from copy import deepcopy from mach.mixin.logging import LoggingMixin +from mozbuild.base import MozbuildObject from mozbuild.makeutil import Makefile from mozbuild.pythonutil import iter_modules_in_path from mozbuild.util import FileAvoidWrite @@ -521,3 +522,44 @@ class WebIDLCodegenManager(LoggingMixin): result[1].add(path) else: result[2].add(path) + + +def create_build_system_manager(topsrcdir, topobjdir, dist_dir): + """Create a WebIDLCodegenManager for use by the build system.""" + src_dir = os.path.join(topsrcdir, 'dom', 'bindings') + obj_dir = os.path.join(topobjdir, 'dom', 'bindings') + + with open(os.path.join(obj_dir, 'file-lists.json'), 'rb') as fh: + files = json.load(fh) + + inputs = (files['webidls'], files['exported_stems'], + files['generated_events_stems']) + + cache_dir = os.path.join(obj_dir, '_cache') + try: + os.makedirs(cache_dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + return WebIDLCodegenManager( + os.path.join(src_dir, 'Bindings.conf'), + inputs, + os.path.join(dist_dir, 'include', 'mozilla', 'dom'), + obj_dir, + os.path.join(obj_dir, 'codegen.json'), + cache_dir=cache_dir, + # The make rules include a codegen.pp file containing dependencies. + make_deps_path=os.path.join(obj_dir, 'codegen.pp'), + make_deps_target='codegen.pp', + ) + + +class BuildSystemWebIDL(MozbuildObject): + @property + def manager(self): + if not hasattr(self, '_webidl_manager'): + self._webidl_manager = create_build_system_manager( + self.topsrcdir, self.topobjdir, self.distdir) + + return self._webidl_manager diff --git a/dom/bindings/test/Makefile.in b/dom/bindings/test/Makefile.in index 78756bf35a03..78a3cafd020c 100644 --- a/dom/bindings/test/Makefile.in +++ b/dom/bindings/test/Makefile.in @@ -2,89 +2,23 @@ # 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/. -# Do NOT export this library. We don't actually want our test code -# being added to libxul or anything. - -# pymake can't handle descending into dom/bindings several times simultaneously -ifdef .PYMAKE -.NOTPARALLEL: -endif - -# Need this for $(test_webidl_files) include ../webidlsrcs.mk -# But the webidl actually lives in our parent dir -test_webidl_files := $(addprefix ../,$(test_webidl_files)) -# Store the actual locations of our source preprocessed files, so we -# can depend on them sanely. -source_preprocessed_test_webidl_files := $(addprefix $(srcdir)/,$(preprocessed_test_webidl_files)) -preprocessed_test_webidl_files := $(addprefix ../,$(preprocessed_test_webidl_files)) - -CPPSRCS += \ - $(subst .webidl,Binding.cpp,$(test_webidl_files)) \ - $(subst .webidl,Binding.cpp,$(preprocessed_test_webidl_files)) \ - $(NULL) - -# If you change bindinggen_dependencies here, change it in -# dom/bindings/Makefile.in too. But note that we include ../Makefile -# here manually, since $(GLOBAL_DEPS) won't cover it. -bindinggen_dependencies := \ - ../BindingGen.py \ - ../Bindings.conf \ - ../Configuration.py \ - ../Codegen.py \ - ../ParserResults.pkl \ - ../parser/WebIDL.py \ - ../Makefile \ - $(GLOBAL_DEPS) \ - $(NULL) +# $(test_sources) comes from webidlsrcs.mk. +# TODO Update this variable in backend.mk. +CPPSRCS += $(addprefix ../,$(test_sources)) ifdef GNU_CC -CXXFLAGS += -Wno-uninitialized +OS_CXXFLAGS += -Wno-uninitialized endif +# Bug 932082 tracks having bindings use namespaced includes. +LOCAL_INCLUDES += -I$(DIST)/include/mozilla/dom -I.. + # Include rules.mk before any of our targets so our first target is coming from # rules.mk and running make with no target in this dir does the right thing. include $(topsrcdir)/config/rules.mk -$(CPPSRCS): .BindingGen - -.BindingGen: $(bindinggen_dependencies) \ - $(test_webidl_files) \ - $(source_preprocessed_test_webidl_files) \ - $(NULL) - # The export phase in dom/bindings is what actually looks at - # dependencies and regenerates things as needed, so just go ahead and - # make that phase here. Also make our example interface files. If the - # target used here ever changes, change the conditional around - # $(CPPSRCS) in dom/bindings/Makefile.in. - $(MAKE) -C .. export TestExampleInterface-example TestExampleProxyInterface-example - @$(TOUCH) $@ - check:: PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ $(PLY_INCLUDE) $(srcdir)/../parser/runtests.py - -# Since we define MOCHITEST_FILES, config/makefiles/mochitest.mk goes ahead and -# sets up a rule with libs:: in itm which makes our .DEFAULT_TARGET be "libs". -# Then ruls.mk does |.DEFAULT_TARGET ?= default| which leaves it as "libs". So -# if we make without an explicit target in this directory, we try to make -# "libs", but with a $(MAKECMDGOALS) of empty string. And then rules.mk -# helpfully does not include our *.o.pp files, since it includes them only if -# filtering some stuff out from $(MAKECMDGOALS) leaves it nonempty. The upshot -# is that if some headers change and we run make in this dir without an explicit -# target things don't get rebuilt. -# -# On the other hand, if we set .DEFAULT_TARGET to "default" explicitly here, -# then rules.mk will reinvoke make with "export" and "libs" but this time hey -# will be passed as explicit targets, show up in $(MAKECMDGOALS), and things -# will work. Do this at the end of our Makefile so the rest of the build system -# does not get a chance to muck with it after we set it. -.DEFAULT_GOAL := default - -# Make sure to add .BindingGen to GARBAGE so we'll rebuild our example -# files if someone goes through and deletes GARBAGE all over, which -# will delete example files from our parent dir. -GARBAGE += \ - .BindingGen \ - $(NULL) diff --git a/dom/bindings/test/moz.build b/dom/bindings/test/moz.build index 82db3c691cbc..5b129eee9e20 100644 --- a/dom/bindings/test/moz.build +++ b/dom/bindings/test/moz.build @@ -14,9 +14,20 @@ MOCHITEST_MANIFESTS += ['mochitest.ini'] MOCHITEST_CHROME_MANIFESTS += ['chrome.ini'] +TEST_WEBIDL_FILES += [ + 'TestDictionary.webidl', + 'TestJSImplInheritanceGen.webidl', + 'TestTypedef.webidl', +] + +PREPROCESSED_TEST_WEBIDL_FILES += [ + 'TestCodeGen.webidl', + 'TestExampleGen.webidl', + 'TestJSImplGen.webidl', +] + LOCAL_INCLUDES += [ '/dom/bindings', '/js/xpconnect/src', '/js/xpconnect/wrappers', ] - diff --git a/dom/moz.build b/dom/moz.build index 09bf93c46ed5..60aa95f2991f 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -107,12 +107,9 @@ if CONFIG['MOZ_NFC']: if CONFIG['MOZ_B2G']: PARALLEL_DIRS += ['downloads'] -# bindings/test is here, because it needs to build after bindings/, and -# we build subdirectories before ourselves. TEST_DIRS += [ 'tests', 'imptests', - 'bindings/test', ] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'cocoa', 'windows', 'android', 'qt', 'os2'): diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 108b38a1b3ec..e3f970284f36 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -553,18 +553,6 @@ if CONFIG['MOZ_B2G_FM']: 'FMRadio.webidl', ] -if CONFIG['ENABLE_TESTS']: - TEST_WEBIDL_FILES += [ - 'TestDictionary.webidl', - 'TestJSImplInheritanceGen.webidl', - 'TestTypedef.webidl', - ] - PREPROCESSED_TEST_WEBIDL_FILES += [ - 'TestCodeGen.webidl', - 'TestExampleGen.webidl', - 'TestJSImplGen.webidl', - ] - GENERATED_EVENTS_WEBIDL_FILES = [ 'BlobEvent.webidl', 'CallGroupErrorEvent.webidl', diff --git a/python/mozbuild/mozbuild/action/webidl.py b/python/mozbuild/mozbuild/action/webidl.py new file mode 100644 index 000000000000..6702701228e5 --- /dev/null +++ b/python/mozbuild/mozbuild/action/webidl.py @@ -0,0 +1,17 @@ +# 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/. + +import sys + +from mozwebidlcodegen import BuildSystemWebIDL + + +def main(argv): + """Perform WebIDL code generation required by the build system.""" + manager = BuildSystemWebIDL.from_environment().manager + manager.generate_build_files() + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/python/mozbuild/mozbuild/backend/common.py b/python/mozbuild/mozbuild/backend/common.py index eec09f7eecb8..0a6fe8ad4049 100644 --- a/python/mozbuild/mozbuild/backend/common.py +++ b/python/mozbuild/mozbuild/backend/common.py @@ -10,15 +10,19 @@ import re import mozpack.path as mozpath -from mozbuild.preprocessor import Preprocessor - from .base import BuildBackend from ..frontend.data import ( ConfigFileSubstitution, HeaderFileSubstitution, + GeneratedEventWebIDLFile, + GeneratedWebIDLFile, + PreprocessedTestWebIDLFile, + PreprocessedWebIDLFile, TestManifest, + TestWebIDLFile, XPIDLFile, + WebIDLFile, ) from ..util import DefaultOnReadDict @@ -56,6 +60,79 @@ class XPIDLManager(object): self.modules.setdefault(entry['module'], set()).add(entry['root']) +class WebIDLCollection(object): + """Collects WebIDL info referenced during the build.""" + + def __init__(self): + self.sources = set() + self.generated_sources = set() + self.generated_events_sources = set() + self.preprocessed_sources = set() + self.test_sources = set() + self.preprocessed_test_sources = set() + + def all_regular_sources(self): + return self.sources | self.generated_sources | \ + self.generated_events_sources | self.preprocessed_sources + + def all_regular_basenames(self): + return [os.path.basename(source) for source in self.all_regular_sources()] + + def all_regular_stems(self): + return [os.path.splitext(b)[0] for b in self.all_regular_basenames()] + + def all_regular_bindinggen_stems(self): + for stem in self.all_regular_stems(): + yield '%sBinding' % stem + + for source in self.generated_events_sources: + yield os.path.splitext(os.path.basename(source))[0] + + def all_regular_cpp_basenames(self): + for stem in self.all_regular_bindinggen_stems(): + yield '%s.cpp' % stem + + def all_test_sources(self): + return self.test_sources | self.preprocessed_test_sources + + def all_test_basenames(self): + return [os.path.basename(source) for source in self.all_test_sources()] + + def all_test_stems(self): + return [os.path.splitext(b)[0] for b in self.all_test_basenames()] + + def all_test_cpp_basenames(self): + return ['%sBinding.cpp' % s for s in self.all_test_stems()] + + def all_static_sources(self): + return self.sources | self.generated_events_sources | \ + self.test_sources + + def all_non_static_sources(self): + return self.generated_sources | self.all_preprocessed_sources() + + def all_non_static_basenames(self): + return [os.path.basename(s) for s in self.all_non_static_sources()] + + def all_preprocessed_sources(self): + return self.preprocessed_sources | self.preprocessed_test_sources + + def all_sources(self): + return set(self.all_regular_sources()) | set(self.all_test_sources()) + + def all_basenames(self): + return [os.path.basename(source) for source in self.all_sources()] + + def all_stems(self): + return [os.path.splitext(b)[0] for b in self.all_basenames()] + + def generated_events_basenames(self): + return [os.path.basename(s) for s in self.generated_events_sources] + + def generated_events_stems(self): + return [os.path.splitext(b)[0] for b in self.generated_events_basenames()] + + class TestManager(object): """Helps hold state related to tests.""" @@ -90,6 +167,7 @@ class CommonBackend(BuildBackend): def _init(self): self._idl_manager = XPIDLManager(self.environment) self._test_manager = TestManager(self.environment) + self._webidls = WebIDLCollection() def consume_object(self, obj): if isinstance(obj, TestManifest): @@ -113,6 +191,30 @@ class CommonBackend(BuildBackend): self._create_config_header(obj) self.backend_input_files.add(obj.input_path) + # We should consider aggregating WebIDL types in emitter.py. + elif isinstance(obj, WebIDLFile): + self._webidls.sources.add(mozpath.join(obj.srcdir, obj.basename)) + + elif isinstance(obj, GeneratedEventWebIDLFile): + self._webidls.generated_events_sources.add(mozpath.join( + obj.srcdir, obj.basename)) + + elif isinstance(obj, TestWebIDLFile): + self._webidls.test_sources.add(mozpath.join(obj.srcdir, + obj.basename)) + + elif isinstance(obj, PreprocessedTestWebIDLFile): + self._webidls.preprocessed_test_sources.add(mozpath.join( + obj.srcdir, obj.basename)) + + elif isinstance(obj, GeneratedWebIDLFile): + self._webidls.generated_sources.add(mozpath.join(obj.srcdir, + obj.basename)) + + elif isinstance(obj, PreprocessedWebIDLFile): + self._webidls.preprocessed_sources.add(mozpath.join( + obj.srcdir, obj.basename)) + else: return @@ -122,6 +224,8 @@ class CommonBackend(BuildBackend): if len(self._idl_manager.idls): self._handle_idl_manager(self._idl_manager) + self._handle_webidl_collection(self._webidls) + # Write out a machine-readable file describing every test. path = mozpath.join(self.environment.topobjdir, 'all-tests.json') with self._write_file(path) as fh: diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index 3742ad4f340b..c9a4000dd46a 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -5,13 +5,15 @@ from __future__ import unicode_literals import itertools +import json import logging import os -import re import types from collections import namedtuple +import mozwebidlcodegen + import mozbuild.makeutil as mozmakeutil from mozpack.copier import FilePurger from mozpack.manifests import ( @@ -25,9 +27,7 @@ from ..frontend.data import ( Defines, DirectoryTraversal, Exports, - GeneratedEventWebIDLFile, GeneratedInclude, - GeneratedWebIDLFile, HostProgram, HostSimpleProgram, InstallationTarget, @@ -35,17 +35,13 @@ from ..frontend.data import ( JavaJarData, LibraryDefinition, LocalInclude, - PreprocessedTestWebIDLFile, - PreprocessedWebIDLFile, Program, SandboxDerived, SandboxWrapped, SimpleProgram, - TestWebIDLFile, + TestManifest, VariablePassthru, XPIDLFile, - TestManifest, - WebIDLFile, ) from ..util import ( ensureParentDir, @@ -270,12 +266,6 @@ class RecursiveMakeBackend(CommonBackend): self._backend_files = {} self._ipdl_sources = set() - self._webidl_sources = set() - self._generated_events_webidl_sources = set() - self._test_webidl_sources = set() - self._preprocessed_test_webidl_sources = set() - self._preprocessed_webidl_sources = set() - self._generated_webidl_sources = set() def detailed(summary): s = '{:d} total backend files. {:d} created; {:d} updated; {:d} unchanged'.format( @@ -410,33 +400,6 @@ class RecursiveMakeBackend(CommonBackend): elif isinstance(obj, IPDLFile): self._ipdl_sources.add(mozpath.join(obj.srcdir, obj.basename)) - elif isinstance(obj, WebIDLFile): - self._webidl_sources.add(mozpath.join(obj.srcdir, obj.basename)) - self._process_webidl_basename(obj.basename) - - elif isinstance(obj, GeneratedEventWebIDLFile): - self._generated_events_webidl_sources.add(mozpath.join(obj.srcdir, obj.basename)) - - elif isinstance(obj, TestWebIDLFile): - self._test_webidl_sources.add(mozpath.join(obj.srcdir, - obj.basename)) - # Test WebIDL files are not exported. - - elif isinstance(obj, PreprocessedTestWebIDLFile): - self._preprocessed_test_webidl_sources.add(mozpath.join(obj.srcdir, - obj.basename)) - # Test WebIDL files are not exported. - - elif isinstance(obj, GeneratedWebIDLFile): - self._generated_webidl_sources.add(mozpath.join(obj.srcdir, - obj.basename)) - self._process_webidl_basename(obj.basename) - - elif isinstance(obj, PreprocessedWebIDLFile): - self._preprocessed_webidl_sources.add(mozpath.join(obj.srcdir, - obj.basename)) - self._process_webidl_basename(obj.basename) - elif isinstance(obj, Program): self._process_program(obj.program, backend_file) @@ -607,6 +570,9 @@ class RecursiveMakeBackend(CommonBackend): poison_windows_h=False, files_per_unified_file=16): + # In case it's a generator. + files = sorted(files) + explanation = "\n" \ "# We build files in 'unified' mode by including several files\n" \ "# together into a single source file. This cuts down on\n" \ @@ -632,7 +598,7 @@ class RecursiveMakeBackend(CommonBackend): return itertools.izip_longest(fillvalue=dummy_fill_value, *args) for i, unified_group in enumerate(grouper(files_per_unified_file, - sorted(files))): + files)): just_the_filenames = list(filter_out_dummy(unified_group)) yield '%s%d.%s' % (unified_prefix, i, unified_suffix), just_the_filenames @@ -750,42 +716,12 @@ class RecursiveMakeBackend(CommonBackend): with self._write_file(mozpath.join(ipdl_dir, 'ipdlsrcs.mk')) as ipdls: mk.dump(ipdls, removal_guard=False) - self._may_skip['compile'] -= set(['ipc/ipdl']) - - # Write out master lists of WebIDL source files. - bindings_dir = mozpath.join(self.environment.topobjdir, 'dom', 'bindings') - - mk = mozmakeutil.Makefile() - - def write_var(variable, sources): - files = [mozpath.basename(f) for f in sorted(sources)] - mk.add_statement('%s += %s' % (variable, ' '.join(files))) - write_var('webidl_files', self._webidl_sources) - write_var('generated_events_webidl_files', self._generated_events_webidl_sources) - write_var('test_webidl_files', self._test_webidl_sources) - write_var('preprocessed_test_webidl_files', self._preprocessed_test_webidl_sources) - write_var('generated_webidl_files', self._generated_webidl_sources) - write_var('preprocessed_webidl_files', self._preprocessed_webidl_sources) - - all_webidl_files = itertools.chain(iter(self._webidl_sources), - iter(self._generated_events_webidl_sources), - iter(self._generated_webidl_sources), - iter(self._preprocessed_webidl_sources)) - all_webidl_files = [mozpath.basename(x) for x in all_webidl_files] - all_webidl_sources = [re.sub(r'\.webidl$', 'Binding.cpp', x) for x in all_webidl_files] - - self._add_unified_build_rules(mk, all_webidl_sources, - bindings_dir, - unified_prefix='UnifiedBindings', - unified_files_makefile_variable='unified_binding_cpp_files', - poison_windows_h=True) - - # Assume that Somebody Else has responsibility for correctly - # specifying removal dependencies for |all_webidl_sources|. - with self._write_file(mozpath.join(bindings_dir, 'webidlsrcs.mk')) as webidls: - mk.dump(webidls, removal_guard=False) - - self._may_skip['compile'] -= set(['dom/bindings', 'dom/bindings/test']) + # These contain autogenerated sources that the build config doesn't + # yet know about. + # TODO Emit GENERATED_SOURCES so these special cases are dealt with + # the proper way. + self._may_skip['compile'] -= {'ipc/ipdl'} + self._may_skip['compile'] -= {'dom/bindings', 'dom/bindings/test'} self._fill_root_mk() @@ -1026,10 +962,6 @@ class RecursiveMakeBackend(CommonBackend): def _process_host_simple_program(self, program, backend_file): backend_file.write('HOST_SIMPLE_PROGRAMS += %s\n' % program) - def _process_webidl_basename(self, basename): - header = 'mozilla/dom/%sBinding.h' % mozpath.splitext(basename)[0] - self._install_manifests['dist_include'].add_optional_exists(header) - def _process_test_manifest(self, obj, backend_file): # Much of the logic in this function could be moved to CommonBackend. self.backend_input_files.add(mozpath.join(obj.topsrcdir, @@ -1171,3 +1103,86 @@ class RecursiveMakeBackend(CommonBackend): # Makefile.in files, the old file will get replaced with # the autogenerated one automatically. self.backend_input_files.add(obj.input_path) + + def _handle_webidl_collection(self, webidls): + if not webidls.all_stems(): + return + + bindings_dir = mozpath.join(self.environment.topobjdir, 'dom', + 'bindings') + + all_inputs = set(webidls.all_static_sources()) + for s in webidls.all_non_static_basenames(): + all_inputs.add(mozpath.join(bindings_dir, s)) + + generated_events_stems = webidls.generated_events_stems() + exported_stems = webidls.all_regular_stems() + + # The WebIDL manager reads configuration from a JSON file. So, we + # need to write this file early. + o = dict( + webidls=sorted(all_inputs), + generated_events_stems=sorted(generated_events_stems), + exported_stems=sorted(exported_stems), + ) + + file_lists = mozpath.join(bindings_dir, 'file-lists.json') + with self._write_file(file_lists) as fh: + json.dump(o, fh, sort_keys=True, indent=2) + + manager = mozwebidlcodegen.create_build_system_manager( + self.environment.topsrcdir, + self.environment.topobjdir, + mozpath.join(self.environment.topobjdir, 'dist') + ) + + # The manager is the source of truth on what files are generated. + # Consult it for install manifests. + include_dir = mozpath.join(self.environment.topobjdir, 'dist', + 'include') + for f in manager.expected_build_output_files(): + if f.startswith(include_dir): + self._install_manifests['dist_include'].add_optional_exists( + mozpath.relpath(f, include_dir)) + + # We pass WebIDL info to make via a completely generated make file. + mk = Makefile() + mk.add_statement('nonstatic_webidl_files := %s' % ' '.join( + sorted(webidls.all_non_static_basenames()))) + mk.add_statement('globalgen_sources := %s' % ' '.join( + sorted(manager.GLOBAL_DEFINE_FILES))) + mk.add_statement('test_sources := %s' % ' '.join( + sorted('%sBinding.cpp' % s for s in webidls.all_test_stems()))) + + # Add rules to preprocess bindings. + # This should ideally be using PP_TARGETS. However, since the input + # filenames match the output filenames, the existing PP_TARGETS rules + # result in circular dependencies and other make weirdness. One + # solution is to rename the input or output files repsectively. See + # bug 928195 comment 129. + for source in sorted(webidls.all_preprocessed_sources()): + basename = os.path.basename(source) + rule = mk.create_rule([basename]) + rule.add_dependencies([source, '$(GLOBAL_DEPS)']) + rule.add_commands([ + # Remove the file before writing so bindings that go from + # static to preprocessed don't end up writing to a symlink, + # which would modify content in the source directory. + '$(RM) $@', + '$(call py_action,preprocessor,$(DEFINES) $(ACDEFINES) ' + '$(XULPPFLAGS) $< -o $@)' + ]) + + # Bindings are compiled in unified mode to speed up compilation and + # to reduce linker memory size. Note that test bindings are separated + # from regular ones so tests bindings aren't shipped. + self._add_unified_build_rules(mk, + webidls.all_regular_cpp_basenames(), + bindings_dir, + unified_prefix='UnifiedBindings', + unified_files_makefile_variable='unified_binding_cpp_files', + poison_windows_h=True) + + webidls_mk = mozpath.join(bindings_dir, 'webidlsrcs.mk') + with self._write_file(webidls_mk) as fh: + mk.dump(fh, removal_guard=False) diff --git a/python/mozbuild/mozbuild/base.py b/python/mozbuild/mozbuild/base.py index 0c006855d0d4..6043c4e8f3e5 100644 --- a/python/mozbuild/mozbuild/base.py +++ b/python/mozbuild/mozbuild/base.py @@ -273,6 +273,10 @@ class MozbuildObject(ProcessExecutionMixin): def bindir(self): return os.path.join(self.topobjdir, 'dist', 'bin') + @property + def includedir(self): + return os.path.join(self.topobjdir, 'dist', 'include') + @property def statedir(self): return os.path.join(self.topobjdir, '.mozbuild') From faf292b942f8045390ec7af8d1b3116d36384a7f Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 14 Nov 2013 10:34:55 -0800 Subject: [PATCH 105/459] Bug 928195 - Part 5: mach command for generating WebIDL example files; r=froydnj --HG-- extra : rebase_source : c74d5f9556ca866622b7fefd4711e96fe4e4e020 --- dom/bindings/mach_commands.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dom/bindings/mach_commands.py b/dom/bindings/mach_commands.py index e176d91cd4e3..8b890c94b319 100644 --- a/dom/bindings/mach_commands.py +++ b/dom/bindings/mach_commands.py @@ -18,6 +18,17 @@ from mozbuild.base import MachCommandBase @CommandProvider class WebIDLProvider(MachCommandBase): + @Command('webidl-example', category='misc', + description='Generate example files for a WebIDL interface.') + @CommandArgument('interface', nargs='+', + help='Interface(s) whose examples to generate.') + def webidl_example(self, interface): + from mozwebidlcodegen import BuildSystemWebIDL + + manager = self._spawn(BuildSystemWebIDL).manager + for i in interface: + manager.generate_example_files(i) + @Command('webidl-parser-test', category='testing', description='Run WebIDL tests.') @CommandArgument('--verbose', '-v', action='store_true', From 4190812d0a0f93dd62c1ba0644e5cb89408a49b4 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 13 Dec 2013 14:24:36 +1100 Subject: [PATCH 106/459] Bug 935793 (part 1) - Add ownerIsBrowserOrApp to nsIFrameLoader. r=smaug --HG-- extra : rebase_source : 189b45b39c04e4272d7b0cc188f35bb02bc19970 --- content/base/public/nsIFrameLoader.idl | 7 ++++++- content/base/src/nsFrameLoader.cpp | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/content/base/public/nsIFrameLoader.idl b/content/base/public/nsIFrameLoader.idl index a2f3e812d621..bcbc5fd93028 100644 --- a/content/base/public/nsIFrameLoader.idl +++ b/content/base/public/nsIFrameLoader.idl @@ -111,7 +111,7 @@ interface nsIContentViewManager : nsISupports readonly attribute nsIContentView rootContentView; }; -[scriptable, builtinclass, uuid(5b9949dc-56f1-47b6-b6d2-3785bb90ed6d)] +[scriptable, builtinclass, uuid(a723673b-a26e-4cc6-ae23-ec70df9d97c9)] interface nsIFrameLoader : nsISupports { /** @@ -272,6 +272,11 @@ interface nsIFrameLoader : nsISupports * have a notion of visibility in the parent process when frames are OOP. */ [infallible] attribute boolean visible; + + /** + * Find out whether the owner content really is a browser or app frame + */ + readonly attribute boolean ownerIsBrowserOrAppFrame; }; %{C++ diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 1c4d99da8d62..306a90fedc4c 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -1409,6 +1409,14 @@ nsFrameLoader::OwnerIsBrowserOrAppFrame() return browserFrame ? browserFrame->GetReallyIsBrowserOrApp() : false; } +// The xpcom getter version +NS_IMETHODIMP +nsFrameLoader::GetOwnerIsBrowserOrAppFrame(bool* aResult) +{ + *aResult = OwnerIsBrowserOrAppFrame(); + return NS_OK; +} + bool nsFrameLoader::OwnerIsAppFrame() { From 4834261c634406983a4a33278dc3139867b6b2db Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 13 Dec 2013 14:24:37 +1100 Subject: [PATCH 107/459] Bug 935793 (part 2) - rename browser-shown notifications and send them for all frames. r=smaug --HG-- extra : rebase_source : 475b2cafc094117a2edf8ba95605e7f4d5f95410 --- accessible/src/jsat/AccessFu.jsm | 20 ++++++++++------- b2g/components/ErrorPage.jsm | 8 +++++-- content/base/src/nsFrameLoader.cpp | 24 ++++++++++----------- dom/browser-element/BrowserElementParent.js | 16 ++++++++++---- dom/inputmethod/Keyboard.jsm | 8 +++++-- dom/ipc/ProcessPriorityManager.cpp | 11 ++++++++-- 6 files changed, 56 insertions(+), 31 deletions(-) diff --git a/accessible/src/jsat/AccessFu.jsm b/accessible/src/jsat/AccessFu.jsm index c0cf612968c5..fb7bcdbbd263 100644 --- a/accessible/src/jsat/AccessFu.jsm +++ b/accessible/src/jsat/AccessFu.jsm @@ -118,8 +118,8 @@ this.AccessFu = { Output.start(); TouchAdapter.start(); - Services.obs.addObserver(this, 'remote-browser-frame-shown', false); - Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); + Services.obs.addObserver(this, 'remote-browser-shown', false); + Services.obs.addObserver(this, 'inprocess-browser-shown', false); Services.obs.addObserver(this, 'Accessibility:NextObject', false); Services.obs.addObserver(this, 'Accessibility:PreviousObject', false); Services.obs.addObserver(this, 'Accessibility:Focus', false); @@ -162,8 +162,8 @@ this.AccessFu = { Utils.win.removeEventListener('TabClose', this); Utils.win.removeEventListener('TabSelect', this); - Services.obs.removeObserver(this, 'remote-browser-frame-shown'); - Services.obs.removeObserver(this, 'in-process-browser-or-app-frame-shown'); + Services.obs.removeObserver(this, 'remote-browser-shown'); + Services.obs.removeObserver(this, 'inprocess-browser-shown'); Services.obs.removeObserver(this, 'Accessibility:NextObject'); Services.obs.removeObserver(this, 'Accessibility:PreviousObject'); Services.obs.removeObserver(this, 'Accessibility:Focus'); @@ -304,11 +304,15 @@ this.AccessFu = { case 'Accessibility:MoveByGranularity': this.Input.moveByGranularity(JSON.parse(aData)); break; - case 'remote-browser-frame-shown': - case 'in-process-browser-or-app-frame-shown': + case 'remote-browser-shown': + case 'inprocess-browser-shown': { - let mm = aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager; - this._handleMessageManager(mm); + // Ignore notifications that aren't from a BrowserOrApp + let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader); + if (!frameLoader.ownerIsBrowserOrAppFrame) { + return; + } + this._handleMessageManager(frameLoader.messageManager); break; } } diff --git a/b2g/components/ErrorPage.jsm b/b2g/components/ErrorPage.jsm index 8b6c2985e638..ba9cb3af03e0 100644 --- a/b2g/components/ErrorPage.jsm +++ b/b2g/components/ErrorPage.jsm @@ -150,12 +150,16 @@ let ErrorPage = { }, init: function errorPageInit() { - Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); - Services.obs.addObserver(this, 'remote-browser-frame-shown', false); + Services.obs.addObserver(this, 'inprocess-browser-shown', false); + Services.obs.addObserver(this, 'remote-browser-shown', false); }, observe: function errorPageObserve(aSubject, aTopic, aData) { let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader); + // Ignore notifications that aren't from a BrowserOrApp + if (!frameLoader.ownerIsBrowserOrAppFrame) { + return; + } let mm = frameLoader.messageManager; // This won't happen from dom/ipc/preload.js in non-OOP builds. diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 306a90fedc4c..9c74e96b4ae9 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -948,9 +948,9 @@ nsFrameLoader::ShowRemoteFrame(const nsIntSize& size, EnsureMessageManager(); nsCOMPtr os = services::GetObserverService(); - if (OwnerIsBrowserOrAppFrame() && os && !mRemoteBrowserInitialized) { + if (os && !mRemoteBrowserInitialized) { os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), - "remote-browser-frame-shown", nullptr); + "remote-browser-shown", nullptr); mRemoteBrowserInitialized = true; } } else { @@ -1685,18 +1685,16 @@ nsFrameLoader::MaybeCreateDocShell() mDocShell->SetIsBrowserInsideApp(containingAppId); } - if (OwnerIsBrowserOrAppFrame()) { - nsCOMPtr os = services::GetObserverService(); - if (os) { - os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), - "in-process-browser-or-app-frame-shown", nullptr); - } + nsCOMPtr os = services::GetObserverService(); + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), + "inprocess-browser-shown", nullptr); + } - if (mMessageManager) { - mMessageManager->LoadFrameScript( - NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"), - /* allowDelayedLoad = */ true); - } + if (OwnerIsBrowserOrAppFrame() && mMessageManager) { + mMessageManager->LoadFrameScript( + NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"), + /* allowDelayedLoad = */ true); } return NS_OK; diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js index 9bd9dd4280a3..b4741d477e0a 100644 --- a/dom/browser-element/BrowserElementParent.js +++ b/dom/browser-element/BrowserElementParent.js @@ -65,8 +65,8 @@ BrowserElementParentFactory.prototype = { // alive for as long as its frame element lives. this._bepMap = new WeakMap(); - Services.obs.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true); - Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', /* ownsWeak = */ true); + Services.obs.addObserver(this, 'remote-browser-shown', /* ownsWeak = */ true); + Services.obs.addObserver(this, 'inprocess-browser-shown', /* ownsWeak = */ true); }, _browserFramesPrefEnabled: function() { @@ -79,11 +79,19 @@ BrowserElementParentFactory.prototype = { }, _observeInProcessBrowserFrameShown: function(frameLoader) { + // Ignore notifications that aren't from a BrowserOrApp + if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) { + return; + } debug("In-process browser frame shown " + frameLoader); this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ false); }, _observeRemoteBrowserFrameShown: function(frameLoader) { + // Ignore notifications that aren't from a BrowserOrApp + if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) { + return; + } debug("Remote browser frame shown " + frameLoader); this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ true); }, @@ -103,10 +111,10 @@ BrowserElementParentFactory.prototype = { this._init(); } break; - case 'remote-browser-frame-shown': + case 'remote-browser-shown': this._observeRemoteBrowserFrameShown(subject); break; - case 'in-process-browser-or-app-frame-shown': + case 'inprocess-browser-shown': this._observeInProcessBrowserFrameShown(subject); break; case 'content-document-global-created': diff --git a/dom/inputmethod/Keyboard.jsm b/dom/inputmethod/Keyboard.jsm index 14b56041be66..94456ff8d510 100644 --- a/dom/inputmethod/Keyboard.jsm +++ b/dom/inputmethod/Keyboard.jsm @@ -44,8 +44,8 @@ this.Keyboard = { }, init: function keyboardInit() { - Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); - Services.obs.addObserver(this, 'remote-browser-frame-shown', false); + Services.obs.addObserver(this, 'inprocess-browser-shown', false); + Services.obs.addObserver(this, 'remote-browser-shown', false); Services.obs.addObserver(this, 'oop-frameloader-crashed', false); for (let name of this._messageNames) @@ -63,6 +63,10 @@ this.Keyboard = { ppmm.broadcastAsyncMessage('Keyboard:FocusChange', { 'type': 'blur' }); } } else { + // Ignore notifications that aren't from a BrowserOrApp + if (!frameLoader.ownerIsBrowserOrAppFrame) { + return; + } this.initFormsFrameScript(mm); } }, diff --git a/dom/ipc/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp index d6bb96cd352c..b9c34d7801cc 100644 --- a/dom/ipc/ProcessPriorityManager.cpp +++ b/dom/ipc/ProcessPriorityManager.cpp @@ -602,7 +602,7 @@ ParticularProcessPriorityManager::Init() nsCOMPtr os = services::GetObserverService(); if (os) { os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true); - os->AddObserver(this, "remote-browser-frame-shown", /* ownsWeak */ true); + os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true); os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true); os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true); } @@ -674,7 +674,7 @@ ParticularProcessPriorityManager::Observe(nsISupports* aSubject, if (topic.EqualsLiteral("audio-channel-process-changed")) { OnAudioChannelProcessChanged(aSubject); - } else if (topic.EqualsLiteral("remote-browser-frame-shown")) { + } else if (topic.EqualsLiteral("remote-browser-shown")) { OnRemoteBrowserFrameShown(aSubject); } else if (topic.EqualsLiteral("ipc:browser-destroyed")) { OnTabParentDestroyed(aSubject); @@ -747,6 +747,13 @@ ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubjec nsCOMPtr fl = do_QueryInterface(aSubject); NS_ENSURE_TRUE_VOID(fl); + // Ignore notifications that aren't from a BrowserOrApp + bool isBrowserOrApp; + fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp); + if (!isBrowserOrApp) { + return; + } + nsCOMPtr tp; fl->GetTabParent(getter_AddRefs(tp)); NS_ENSURE_TRUE_VOID(tp); From 59eb74b6f543a3a03d5707847cb8f138e35f9f7f Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Thu, 12 Dec 2013 19:38:49 -0800 Subject: [PATCH 108/459] Bug 944492, part 1 - Make XPCWrappedJS use the purple buffer. r=smaug --- js/xpconnect/src/XPCWrappedJS.cpp | 128 ++++++++++++++++++++---------- js/xpconnect/src/xpcprivate.h | 15 ++-- 2 files changed, 92 insertions(+), 51 deletions(-) diff --git a/js/xpconnect/src/XPCWrappedJS.cpp b/js/xpconnect/src/XPCWrappedJS.cpp index f97b28c84ca2..f0217a990cf2 100644 --- a/js/xpconnect/src/XPCWrappedJS.cpp +++ b/js/xpconnect/src/XPCWrappedJS.cpp @@ -16,6 +16,40 @@ using namespace mozilla; // NOTE: much of the fancy footwork is done in xpcstubs.cpp + +// nsXPCWrappedJS lifetime. +// +// An nsXPCWrappedJS is either rooting its JS object or is subject to finalization. +// The subject-to-finalization state lets wrappers support +// nsSupportsWeakReference in the case where the underlying JS object +// is strongly owned, but the wrapper itself is only weakly owned. +// +// A wrapper is rooting its JS object whenever its refcount is greater than 1. In +// this state, root wrappers are always added to the cycle collector graph. The +// wrapper keeps around an extra refcount, added in the constructor, to support +// the possibility of an eventual transition to the subject-to-finalization state. +// This extra refcount is ignored by the cycle collector, which traverses the "self" +// edge for this refcount. +// +// When the refcount of a rooting wrapper drops to 1, if there is no weak reference +// to the wrapper (which can only happen for the root wrapper), it is immediately +// Destroy()'d. Otherwise, it becomes subject to finalization. +// +// When a wrapper is subject to finalization, the wrapper has a refcount of 1. It is +// now owned exclusively by its JS object. Either a weak reference will be turned into +// a strong ref which will bring its refcount up to 2 and change the wrapper back to +// the rooting state, or it will stay alive until the JS object dies. If the JS object +// dies, then when XPCJSRuntime::FinalizeCallback calls FindDyingJSObjects +// it will find the wrapper and call Release() in it, destroying the wrapper. +// Otherwise, the wrapper will stay alive, even if it no longer has a weak reference +// to it. +// +// When the wrapper is subject to finalization, it is kept alive by an implicit reference +// from the JS object which is invisible to the cycle collector, so the cycle collector +// does not traverse any children of wrappers that are subject to finalization. This will +// result in a leak if a wrapper in the non-rooting state has an aggregated native that +// keeps alive the wrapper's JS object. See bug 947049. + NS_IMETHODIMP NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse (void *p, nsCycleCollectionTraversalCallback &cb) @@ -37,14 +71,17 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt) } - // nsXPCWrappedJS keeps its own refcount artificially at or above 1, see the - // comment above nsXPCWrappedJS::AddRef. + // A wrapper that is subject to finalization will only die when its JS object dies. + if (tmp->IsSubjectToFinalization()) + return NS_OK; + + // Don't let the extra reference for nsSupportsWeakReference keep a wrapper that is + // not subject to finalization alive. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self"); cb.NoteXPCOMChild(s); - if (refcnt > 1) { - // nsXPCWrappedJS roots its mJSObj when its refcount is > 1, see - // the comment above nsXPCWrappedJS::AddRef. + if (tmp->IsValid()) { + MOZ_ASSERT(refcnt > 1); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj"); cb.NoteJSChild(tmp->GetJSObjectPreserveColor()); } @@ -54,7 +91,7 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject()); } else { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root"); - cb.NoteXPCOMChild(static_cast(tmp->GetRootWrapper())); + cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper())); } return NS_OK; @@ -127,37 +164,22 @@ nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr) } -// Refcounting is now similar to that used in the chained (pre-flattening) -// wrappednative system. -// -// We are now holding an extra refcount for nsISupportsWeakReference support. -// -// Non-root wrappers remove themselves from the chain in their destructors. -// We root the JSObject as the refcount transitions from 1->2. And we unroot -// the JSObject when the refcount transitions from 2->1. -// -// When the transition from 2->1 is made and no one holds a weak ref to the -// (aggregated) object then we decrement the refcount again to 0 (and -// destruct) . However, if a weak ref is held at the 2->1 transition, then we -// leave the refcount at 1 to indicate that state. This leaves the JSObject -// no longer rooted by us and (as far as we know) subject to possible -// collection. Code in XPCJSRuntime watches for JS gc to happen and will do -// the final release on wrappers whose JSObjects get finalized. Note that -// even after tranistioning to this refcount-of-one state callers might do -// an addref and cause us to re-root the JSObject and continue on more normally. +// For a description of nsXPCWrappedJS lifetime and reference counting, see +// the comment at the top of this file. nsrefcnt nsXPCWrappedJS::AddRef(void) { if (!MOZ_LIKELY(NS_IsMainThread())) MOZ_CRASH(); - nsrefcnt cnt = ++mRefCnt; + + MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); + nsrefcnt cnt = mRefCnt.incr(); NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this)); if (2 == cnt && IsValid()) { GetJSObject(); // Unmark gray JSObject. - XPCJSRuntime* rt = mClass->GetRuntime(); - rt->AddWrappedJSRoot(this); + mClass->GetRuntime()->AddWrappedJSRoot(this); } return cnt; @@ -168,31 +190,44 @@ nsXPCWrappedJS::Release(void) { if (!MOZ_LIKELY(NS_IsMainThread())) MOZ_CRASH(); - NS_PRECONDITION(0 != mRefCnt, "dup release"); + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); + NS_ASSERT_OWNINGTHREAD(nsXPCWrappedJS); -do_decrement: - - nsrefcnt cnt = --mRefCnt; + bool shouldDelete = false; + nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); + nsrefcnt cnt = mRefCnt.decr(base, &shouldDelete); NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS"); if (0 == cnt) { - delete this; // also unlinks us from chain - return 0; - } - if (1 == cnt) { + if (MOZ_UNLIKELY(shouldDelete)) { + mRefCnt.stabilizeForDeletion(); + DeleteCycleCollectable(); + } else { + mRefCnt.incr(); + Destroy(); + mRefCnt.decr(base); + } + } else if (1 == cnt) { if (IsValid()) RemoveFromRootSet(); - // If we are not the root wrapper or if we are not being used from a - // weak reference, then this extra ref is not needed and we can let - // ourself be deleted. - // Note: HasWeakReferences() could only return true for the root. + // If we are not a root wrapper being used from a weak reference, + // then the extra ref is not needed and we can let outselves be + // deleted. if (!HasWeakReferences()) - goto do_decrement; + return Release(); + + MOZ_ASSERT(IsRootWrapper(), "Only root wrappers should have weak references"); } return cnt; } +NS_IMETHODIMP_(void) +nsXPCWrappedJS::DeleteCycleCollectable(void) +{ + delete this; +} + void nsXPCWrappedJS::TraceJS(JSTracer* trc) { @@ -345,9 +380,12 @@ nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx, { InitStub(GetClass()->GetIID()); - // intentionally do double addref - see Release(). + // There is an extra AddRef to support weak references to wrappers + // that are subject to finalization. See the top of the file for more + // details. NS_ADDREF_THIS(); NS_ADDREF_THIS(); + NS_ADDREF(aClass); NS_IF_ADDREF(mOuter); @@ -358,7 +396,13 @@ nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx, nsXPCWrappedJS::~nsXPCWrappedJS() { - NS_PRECONDITION(0 == mRefCnt, "refcounting error"); + Destroy(); +} + +void +nsXPCWrappedJS::Destroy() +{ + MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion"); if (IsRootWrapper()) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 99c2265a685b..2d9f539eba02 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2464,14 +2464,13 @@ class nsXPCWrappedJS : protected nsAutoXPTCStub, public XPCRootSetElem { public: - NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIXPCONNECTJSOBJECTHOLDER NS_DECL_NSIXPCONNECTWRAPPEDJS NS_DECL_NSISUPPORTSWEAKREFERENCE NS_DECL_NSIPROPERTYBAG NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS) - void DeleteCycleCollectable() {} NS_IMETHOD CallMethod(uint16_t methodIndex, const XPTMethodDescriptor *info, @@ -2513,12 +2512,9 @@ public: bool IsValid() const {return mJSObj != nullptr;} void SystemIsBeingShutDown(); - // This is used by XPCJSRuntime::GCCallback to find wrappers that no - // longer root their JSObject and are only still alive because they - // were being used via nsSupportsWeakReference at the time when their - // last (outside) reference was released. Wrappers that fit into that - // category are only deleted when we see that their corresponding JSObject - // is to be finalized. + // These two methods are used by JSObject2WrappedJSMap::FindDyingJSObjects + // to find non-rooting wrappers for dying JS objects. See the top of + // XPCWrappedJS.cpp for more details. bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;} bool IsObjectAboutToBeFinalized() {return JS_IsAboutToBeFinalized(&mJSObj);} @@ -2537,7 +2533,8 @@ protected: nsXPCWrappedJS* root, nsISupports* aOuter); - void Unlink(); + void Destroy(); + void Unlink(); private: JS::Heap mJSObj; From a324b39e7f60e8757e93492f604ba8ab1d5449d0 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Thu, 12 Dec 2013 19:38:50 -0800 Subject: [PATCH 109/459] Bug 944492, part 2 - Make XPCWrappedJS a proper skippable class. r=smaug --- js/xpconnect/src/XPCJSRuntime.cpp | 37 +--------------------- js/xpconnect/src/XPCWrappedJS.cpp | 51 +++++++++++++++++++++++++++++++ js/xpconnect/src/xpcprivate.h | 3 +- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 1a12e11d83b3..2f33a01747fa 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -668,35 +668,6 @@ XPCJSRuntime::SuspectWrappedNative(XPCWrappedNative *wrapper, cb.NoteJSRoot(obj); } -bool -CanSkipWrappedJS(nsXPCWrappedJS *wrappedJS) -{ - JSObject *obj = wrappedJS->GetJSObjectPreserveColor(); - // If traversing wrappedJS wouldn't release it, nor - // cause any other objects to be added to the graph, no - // need to add it to the graph at all. - bool isRootWrappedJS = wrappedJS->IsRootWrapper(); - if (nsCCUncollectableMarker::sGeneration && - (!obj || !xpc_IsGrayGCThing(obj)) && - !wrappedJS->IsSubjectToFinalization() && - (isRootWrappedJS || CanSkipWrappedJS(wrappedJS->GetRootWrapper()))) { - if (!wrappedJS->IsAggregatedToNative() || !isRootWrappedJS) { - return true; - } else { - nsISupports* agg = wrappedJS->GetAggregatedNativeObject(); - nsXPCOMCycleCollectionParticipant* cp = nullptr; - CallQueryInterface(agg, &cp); - nsISupports* canonical = nullptr; - agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), - reinterpret_cast(&canonical)); - if (cp && canonical && cp->CanSkipInCC(canonical)) { - return true; - } - } - } - return false; -} - void XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback &cb) { @@ -714,13 +685,7 @@ XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback &c } for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) { - nsXPCWrappedJS *wrappedJS = static_cast(e); - if (!cb.WantAllTraces() && - CanSkipWrappedJS(wrappedJS)) { - continue; - } - - cb.NoteXPCOMRoot(static_cast(wrappedJS)); + cb.NoteXPCOMRoot(ToSupports(static_cast(e))); } } diff --git a/js/xpconnect/src/XPCWrappedJS.cpp b/js/xpconnect/src/XPCWrappedJS.cpp index f0217a990cf2..3480ef0f099b 100644 --- a/js/xpconnect/src/XPCWrappedJS.cpp +++ b/js/xpconnect/src/XPCWrappedJS.cpp @@ -8,6 +8,7 @@ #include "xpcprivate.h" #include "jsprf.h" +#include "nsCCUncollectableMarker.h" #include "nsCxPusher.h" #include "nsContentUtils.h" #include "nsThreadUtils.h" @@ -50,6 +51,42 @@ using namespace mozilla; // result in a leak if a wrapper in the non-rooting state has an aggregated native that // keeps alive the wrapper's JS object. See bug 947049. + +// If traversing wrappedJS wouldn't release it, nor cause any other objects to be +// added to the graph, there is no need to add it to the graph at all. +bool +nsXPCWrappedJS::CanSkip() +{ + if (!nsCCUncollectableMarker::sGeneration) + return false; + + if (IsSubjectToFinalization()) + return true; + + // If this wrapper holds a gray object, need to trace it. + JSObject *obj = GetJSObjectPreserveColor(); + if (obj && xpc_IsGrayGCThing(obj)) + return false; + + // For non-root wrappers, check if the root wrapper will be + // added to the CC graph. + if (!IsRootWrapper()) + return mRoot->CanSkip(); + + // For the root wrapper, check if there is an aggregated + // native object that will be added to the CC graph. + if (!IsAggregatedToNative()) + return true; + + nsISupports* agg = GetAggregatedNativeObject(); + nsXPCOMCycleCollectionParticipant* cp = nullptr; + CallQueryInterface(agg, &cp); + nsISupports* canonical = nullptr; + agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), + reinterpret_cast(&canonical)); + return cp && canonical && cp->CanSkipThis(canonical); +} + NS_IMETHODIMP NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse (void *p, nsCycleCollectionTraversalCallback &cb) @@ -103,6 +140,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS) tmp->Unlink(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END +// XPCJSRuntime keeps a table of WJS, so we can remove them from +// the purple buffer in between CCs. +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS) + return true; +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS) + return tmp->CanSkip(); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXPCWrappedJS) + return tmp->CanSkip(); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + NS_IMETHODIMP nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr) { diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 2d9f539eba02..249b21911746 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2470,7 +2470,7 @@ public: NS_DECL_NSISUPPORTSWEAKREFERENCE NS_DECL_NSIPROPERTYBAG - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS) NS_IMETHOD CallMethod(uint16_t methodIndex, const XPTMethodDescriptor *info, @@ -2533,6 +2533,7 @@ protected: nsXPCWrappedJS* root, nsISupports* aOuter); + bool CanSkip(); void Destroy(); void Unlink(); From f853c97cb8422c050ea8c3f5394552f35f7c3587 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 12 Dec 2013 20:29:14 -0800 Subject: [PATCH 110/459] Ensure that the software compositor never uses deprecated textures (bug 947038, r=mattwoodrow). --- dom/ipc/TabChild.cpp | 5 + gfx/layers/basic/BasicCompositor.cpp | 155 ------------------ gfx/layers/client/ContentClient.cpp | 5 + gfx/layers/composite/TextureHost.cpp | 8 - gfx/layers/composite/ThebesLayerComposite.cpp | 1 + gfx/thebes/gfxPlatform.cpp | 20 +++ gfx/thebes/gfxPlatform.h | 5 +- widget/xpwidgets/nsBaseWidget.cpp | 2 + 8 files changed, 37 insertions(+), 164 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 8306641297ff..7e88a30e1b88 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -72,6 +72,7 @@ #include "APZCCallbackHelper.h" #include "nsILoadContext.h" #include "ipc/nsGUIEventIPC.h" +#include "gfxPlatform.h" #ifdef DEBUG #include "PCOMContentPermissionRequestChild.h" @@ -2281,6 +2282,10 @@ TabChild::InitRenderingState() NS_WARNING("failed to properly allocate layer transaction"); return false; } + + // Track which compositor backend is in use (see bug 947038). This can + // be removed when deprecated textures are removed. + gfxPlatform::GetPlatform()->SetCompositorBackend(mTextureFactoryIdentifier.mParentBackend); } else { // Pushing transactions to the parent content. shadowManager = remoteFrame->SendPLayerTransactionConstructor(); diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index cc934831b3b8..0bc966af4163 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -63,161 +63,6 @@ public: RefPtr mSurface; }; -/** - * Texture source and host implementaion for software compositing. - */ -class DeprecatedTextureHostBasic : public DeprecatedTextureHost - , public TextureSourceBasic -{ -public: - DeprecatedTextureHostBasic() - : mCompositor(nullptr) - {} - - SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; } - - virtual IntSize GetSize() const MOZ_OVERRIDE { return mSize; } - - virtual TextureSourceBasic* AsSourceBasic() MOZ_OVERRIDE { return this; } - - SourceSurface *GetSurface() MOZ_OVERRIDE { return mSurface; } - - virtual void SetCompositor(Compositor* aCompositor) - { - mCompositor = static_cast(aCompositor); - } - - virtual const char *Name() { return "DeprecatedTextureHostBasic"; } - -protected: - virtual void UpdateImpl(const SurfaceDescriptor& aImage, - nsIntRegion *aRegion, - nsIntPoint*) MOZ_OVERRIDE - { - AutoOpenSurface surf(OPEN_READ_ONLY, aImage); - nsRefPtr surface = ShadowLayerForwarder::OpenDescriptor(OPEN_READ_ONLY, aImage); - nsRefPtr image = surface->GetAsImageSurface(); - mFormat = ImageFormatToSurfaceFormat(image->Format()); - mSize = IntSize(image->Width(), image->Height()); - mSurface = Factory::CreateWrappingDataSourceSurface(image->Data(), - image->Stride(), - mSize, - mFormat); - } - - virtual bool EnsureSurface() { - return true; - } - - virtual bool Lock() MOZ_OVERRIDE { - return EnsureSurface(); - } - - virtual TemporaryRef GetAsSurface() MOZ_OVERRIDE { - if (!mSurface) { - return nullptr; - } - return mSurface->GetDataSurface(); - } - - BasicCompositor *mCompositor; - RefPtr mSurface; - IntSize mSize; - SurfaceFormat mFormat; -}; - -void -DeserializerToPlanarYCbCrImageData(YCbCrImageDataDeserializer& aDeserializer, PlanarYCbCrData& aData) -{ - aData.mYChannel = aDeserializer.GetYData(); - aData.mYStride = aDeserializer.GetYStride(); - aData.mYSize = aDeserializer.GetYSize(); - aData.mCbChannel = aDeserializer.GetCbData(); - aData.mCrChannel = aDeserializer.GetCrData(); - aData.mCbCrStride = aDeserializer.GetCbCrStride(); - aData.mCbCrSize = aDeserializer.GetCbCrSize(); - aData.mPicSize = aDeserializer.GetYSize(); -} - -class YCbCrDeprecatedTextureHostBasic : public DeprecatedTextureHostBasic -{ -public: - virtual void UpdateImpl(const SurfaceDescriptor& aImage, - nsIntRegion *aRegion, - nsIntPoint*) MOZ_OVERRIDE - { - MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TYCbCrImage); - mSurface = nullptr; - ConvertImageToRGB(aImage); - } - - virtual void SwapTexturesImpl(const SurfaceDescriptor& aImage, - nsIntRegion* aRegion) MOZ_OVERRIDE - { - MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TYCbCrImage); - mSurface = nullptr; - } - - virtual bool EnsureSurface() MOZ_OVERRIDE - { - if (mSurface) { - return true; - } - if (!mBuffer) { - return false; - } - return ConvertImageToRGB(*mBuffer); - } - - bool ConvertImageToRGB(const SurfaceDescriptor& aImage) - { - YCbCrImageDataDeserializer deserializer(aImage.get_YCbCrImage().data().get()); - PlanarYCbCrData data; - DeserializerToPlanarYCbCrImageData(deserializer, data); - - gfxImageFormat format = gfxImageFormatRGB24; - gfxIntSize size; - gfxUtils::GetYCbCrToRGBDestFormatAndSize(data, format, size); - if (size.width > PlanarYCbCrImage::MAX_DIMENSION || - size.height > PlanarYCbCrImage::MAX_DIMENSION) { - NS_ERROR("Illegal image dest width or height"); - return false; - } - - mSize = ToIntSize(size); - mFormat = (format == gfxImageFormatRGB24) - ? FORMAT_B8G8R8X8 - : FORMAT_B8G8R8A8; - - RefPtr surface = Factory::CreateDataSourceSurface(mSize, mFormat); - gfxUtils::ConvertYCbCrToRGB(data, format, size, - surface->GetData(), - surface->Stride()); - - mSurface = surface; - return true; - } -}; - -TemporaryRef -CreateBasicDeprecatedTextureHost(SurfaceDescriptorType aDescriptorType, - uint32_t aTextureHostFlags, - uint32_t aTextureFlags) -{ - RefPtr result = nullptr; - if (aDescriptorType == SurfaceDescriptor::TYCbCrImage) { - result = new YCbCrDeprecatedTextureHostBasic(); - } else { - MOZ_ASSERT(aDescriptorType == SurfaceDescriptor::TShmem || - aDescriptorType == SurfaceDescriptor::TMemoryImage, - "We can only support Shmem currently"); - result = new DeprecatedTextureHostBasic(); - } - - result->SetFlags(aTextureFlags); - return result.forget(); -} - BasicCompositor::BasicCompositor(nsIWidget *aWidget) : mWidget(aWidget) { diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index 544784812d91..157c01970fcf 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -58,6 +58,11 @@ ContentClient::CreateContentClient(CompositableForwarder* aForwarder) useDeprecatedTextures = gfxPlatform::GetPlatform()->UseDeprecatedTextures(); #endif + // Always use new textures for the basic compositor. + if (backend == LAYERS_BASIC) { + useDeprecatedTextures = false; + } + #ifdef XP_WIN if (backend == LAYERS_D3D11) { useDoubleBuffering = !!gfxWindowsPlatform::GetPlatform()->GetD2DDevice(); diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index 5e4da901d9ac..4caeb0b8030a 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -84,10 +84,6 @@ TextureHost::AsTextureHost(PTextureParent* actor) TemporaryRef CreateDeprecatedTextureHostOGL(SurfaceDescriptorType aDescriptorType, uint32_t aDeprecatedTextureHostFlags, uint32_t aTextureFlags); -// implemented in BasicCompositor.cpp -TemporaryRef CreateBasicDeprecatedTextureHost(SurfaceDescriptorType aDescriptorType, - uint32_t aDeprecatedTextureHostFlags, - uint32_t aTextureFlags); #ifdef XP_WIN TemporaryRef CreateDeprecatedTextureHostD3D9(SurfaceDescriptorType aDescriptorType, @@ -127,10 +123,6 @@ DeprecatedTextureHost::CreateDeprecatedTextureHost(SurfaceDescriptorType aDescri aDeprecatedTextureHostFlags, aTextureFlags); #endif - case LAYERS_BASIC: - return CreateBasicDeprecatedTextureHost(aDescriptorType, - aDeprecatedTextureHostFlags, - aTextureFlags); default: MOZ_CRASH("Couldn't create texture host"); } diff --git a/gfx/layers/composite/ThebesLayerComposite.cpp b/gfx/layers/composite/ThebesLayerComposite.cpp index 1f35533634d0..7a9cc3f00c45 100644 --- a/gfx/layers/composite/ThebesLayerComposite.cpp +++ b/gfx/layers/composite/ThebesLayerComposite.cpp @@ -138,6 +138,7 @@ ThebesLayerComposite::RenderLayer(const nsIntRect& aClipRect) mBuffer->SetPaintWillResample(MayResample()); + mBuffer->SetCompositor(mCompositeManager->GetCompositor()); mBuffer->Composite(effectChain, GetEffectiveOpacity(), transform, diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index b563f75429e4..89c74cc2c52e 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -281,6 +281,7 @@ gfxPlatform::gfxPlatform() , mDrawLayerBorders(false) , mDrawTileBorders(false) , mDrawBigImageBorders(false) + , mCompositorBackend(LAYERS_NONE) { mUseHarfBuzzScripts = UNINITIALIZED_VALUE; mAllowDownloadableFonts = UNINITIALIZED_VALUE; @@ -2213,3 +2214,22 @@ gfxPlatform::ComponentAlphaEnabled() InitLayersAccelerationPrefs(); return sComponentAlphaEnabled; } + +void +gfxPlatform::SetCompositorBackend(mozilla::layers::LayersBackend backend) +{ + // The compositor backend should always be the same after one has been chosen. + MOZ_ASSERT(mCompositorBackend == LAYERS_NONE || mCompositorBackend == backend); + if (mCompositorBackend == LAYERS_NONE) { + mCompositorBackend = backend; + } +} + +bool +gfxPlatform::UseDeprecatedTextures() const +{ + if (mCompositorBackend == LAYERS_BASIC) { + return false; + } + return mLayersUseDeprecated; +} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 27ff830fd51e..64d138698558 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -20,6 +20,7 @@ #include "mozilla/RefPtr.h" #include "GfxInfoCollector.h" +#include "mozilla/layers/LayersTypes.h" #include "mozilla/layers/CompositorTypes.h" #ifdef XP_OS2 @@ -619,7 +620,8 @@ public: * This method should not be called from the compositor thread. */ bool PreferMemoryOverShmem() const; - bool UseDeprecatedTextures() const { return mLayersUseDeprecated; } + bool UseDeprecatedTextures() const; + void SetCompositorBackend(mozilla::layers::LayersBackend backend); protected: gfxPlatform(); @@ -736,6 +738,7 @@ private: bool mDrawLayerBorders; bool mDrawTileBorders; bool mDrawBigImageBorders; + mozilla::layers::LayersBackend mCompositorBackend; }; #endif /* GFX_PLATFORM_H */ diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index 1916ced3277d..f8f4ce68d058 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -988,6 +988,8 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier); WindowUsesOMTC(); + gfxPlatform::GetPlatform()->SetCompositorBackend(textureFactoryIdentifier.mParentBackend); + mLayerManager = lm; return; } From db3e8ca04248c1ddfbc4b8e63cc130b44fb7b67b Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 13 Dec 2013 13:41:47 +0900 Subject: [PATCH 111/459] Bug 948245 part 1 - Rework and test repeat duration calculation; r=dholbert In order to implement the min attribute properly we need to distinguish between cases where the repeat duration is less than the active duration so we can apply the fill mode in the 'gap'. Currently the repeat duration calculation is mostly contained in GetRepeatDuration (but is hard to make sense of) and partly contained in the call site CalcActiveEnd. Furthermore, it does not have thorough tests (there were some unit tests but they were never converted to mochitests). This patch reworks the repeat duration calculation so it is contained in one place, easier to read, and thoroughly tested. --- content/smil/nsSMILRepeatCount.h | 6 +- content/smil/nsSMILTimedElement.cpp | 42 +++--- content/smil/test/mochitest.ini | 1 + .../smil/test/test_smilRepeatDuration.html | 139 ++++++++++++++++++ 4 files changed, 162 insertions(+), 26 deletions(-) create mode 100644 content/smil/test/test_smilRepeatDuration.html diff --git a/content/smil/nsSMILRepeatCount.h b/content/smil/nsSMILRepeatCount.h index 18c6d66db618..8561fe4d8bd4 100644 --- a/content/smil/nsSMILRepeatCount.h +++ b/content/smil/nsSMILRepeatCount.h @@ -27,7 +27,11 @@ public: explicit nsSMILRepeatCount(double aCount) : mCount(kNotSet) { SetCount(aCount); } - operator double() const { return mCount; } + operator double() const { + MOZ_ASSERT(IsDefinite(), + "Converting indefinite or unset repeat count to double"); + return mCount; + } bool IsDefinite() const { return mCount != kNotSet && mCount != kIndefinite; } diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index 3538ca5d78b2..4ffde26796ed 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -1803,11 +1803,7 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin, NS_ABORT_IF_FALSE(aBegin.IsDefinite(), "Indefinite or unresolved begin time in CalcActiveEnd"); - if (mRepeatDur.IsIndefinite()) { - result.SetIndefinite(); - } else { - result = GetRepeatDuration(); - } + result = GetRepeatDuration(); if (aEnd.IsDefinite()) { nsSMILTime activeDur = aEnd.GetMillis() - aBegin.GetMillis(); @@ -1832,29 +1828,25 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin, nsSMILTimeValue nsSMILTimedElement::GetRepeatDuration() const { - nsSMILTimeValue result; - - if (mRepeatCount.IsDefinite() && mRepeatDur.IsDefinite()) { - if (mSimpleDur.IsDefinite()) { - nsSMILTime activeDur = - nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis())); - result.SetMillis(std::min(activeDur, mRepeatDur.GetMillis())); - } else { - result = mRepeatDur; - } - } else if (mRepeatCount.IsDefinite() && mSimpleDur.IsDefinite()) { - nsSMILTime activeDur = - nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis())); - result.SetMillis(activeDur); - } else if (mRepeatDur.IsDefinite()) { - result = mRepeatDur; - } else if (mRepeatCount.IsIndefinite()) { - result.SetIndefinite(); + nsSMILTimeValue multipliedDuration; + if (mRepeatCount.IsDefinite() && mSimpleDur.IsDefinite()) { + multipliedDuration.SetMillis( + nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis()))); } else { - result = mSimpleDur; + multipliedDuration.SetIndefinite(); } - return result; + nsSMILTimeValue repeatDuration; + + if (mRepeatDur.IsResolved()) { + repeatDuration = std::min(multipliedDuration, mRepeatDur); + } else if (mRepeatCount.IsSet()) { + repeatDuration = multipliedDuration; + } else { + repeatDuration = mSimpleDur; + } + + return repeatDuration; } nsSMILTimeValue diff --git a/content/smil/test/mochitest.ini b/content/smil/test/mochitest.ini index 75d43a4371d7..f208c0e7a81f 100644 --- a/content/smil/test/mochitest.ini +++ b/content/smil/test/mochitest.ini @@ -38,6 +38,7 @@ support-files = [test_smilMappedAttrFromBy.xhtml] [test_smilMappedAttrFromTo.xhtml] [test_smilMappedAttrPaced.xhtml] +[test_smilRepeatDuration.html] [test_smilRepeatTiming.xhtml] [test_smilReset.xhtml] [test_smilRestart.xhtml] diff --git a/content/smil/test/test_smilRepeatDuration.html b/content/smil/test/test_smilRepeatDuration.html new file mode 100644 index 000000000000..99a95bb7b88e --- /dev/null +++ b/content/smil/test/test_smilRepeatDuration.html @@ -0,0 +1,139 @@ + + + + + + Test for repeat duration calculation (Bug 948245) + + + + +Mozilla Bug 948245 +

+ +
+
+
+ + From 11ce2f85048d55de49c7d7e49ff48f84ec0daece Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 13 Dec 2013 13:41:52 +0900 Subject: [PATCH 112/459] Bug 948245 part 2 - Allow the min attribute to extend the active duration; r=dholbert The min attribute on an animation element can extend the active duration making it longer than the "repeat duration" (the amount of the time the animation runs including all repeats). For the remaining time after the repeat duration has completed until the end of the active duration the animation should apply its fill behavior. Previously this was not implemented and min could not extend the active duration. Allowing this effectively introduces an additional kind of state where we are both within the active interval but not animating. In this case we set the animation function (referred to as the "client" for historical reasons) to inactive so that effectively the timing model is active but the animation model is inactive. (In the future we will come up with something a little easier to understand when we rework this in terms of Web Animations components.) --- content/smil/nsSMILAnimationFunction.h | 9 ++ content/smil/nsSMILTimedElement.cpp | 103 +++++++++++++++------- content/smil/nsSMILTimedElement.h | 1 + content/smil/test/mochitest.ini | 1 + content/smil/test/test_smilMinTiming.html | 93 +++++++++++++++++++ layout/reftests/svg/smil/min-1.svg | 24 +++++ layout/reftests/svg/smil/reftest.list | 2 + 7 files changed, 202 insertions(+), 31 deletions(-) create mode 100644 content/smil/test/test_smilMinTiming.html create mode 100644 layout/reftests/svg/smil/min-1.svg diff --git a/content/smil/nsSMILAnimationFunction.h b/content/smil/nsSMILAnimationFunction.h index 3f233758ac0c..27f4e6f20cb3 100644 --- a/content/smil/nsSMILAnimationFunction.h +++ b/content/smil/nsSMILAnimationFunction.h @@ -164,6 +164,15 @@ public: return (mIsActive || mIsFrozen); } + /** + * Indicates if the animation is active. + * + * @return true if the animation is active, false otherwise. + */ + bool IsActive() const { + return mIsActive; + } + /** * Indicates if this animation will replace the passed in result rather than * adding to it. Animations that replace the underlying value may be called diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index 4ffde26796ed..0da19cdbf412 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -667,19 +667,32 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly) NS_ASSERTION(aContainerTime >= beginTime, "Sample time should not precede current interval"); nsSMILTime activeTime = aContainerTime - beginTime; - SampleSimpleTime(activeTime); - // We register our repeat times as milestones (except when we're - // seeking) so we should get a sample at exactly the time we repeat. - // (And even when we are seeking we want to update - // mCurrentRepeatIteration so we do that first before testing the seek - // state.) - uint32_t prevRepeatIteration = mCurrentRepeatIteration; - if (ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 && + + // The 'min' attribute can cause the active interval to be longer than + // the 'repeating interval'. + // In that extended period we apply the fill mode. + if (GetRepeatDuration() <= nsSMILTimeValue(activeTime)) { + if (mClient && mClient->IsActive()) { + mClient->Inactivate(mFillMode == FILL_FREEZE); + } + SampleFillValue(); + } else { + SampleSimpleTime(activeTime); + + // We register our repeat times as milestones (except when we're + // seeking) so we should get a sample at exactly the time we repeat. + // (And even when we are seeking we want to update + // mCurrentRepeatIteration so we do that first before testing the + // seek state.) + uint32_t prevRepeatIteration = mCurrentRepeatIteration; + if ( + ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 && mCurrentRepeatIteration != prevRepeatIteration && mCurrentRepeatIteration && mSeekState == SEEK_NOT_SEEKING) { - FireTimeEventAsync(NS_SMIL_REPEAT, - static_cast(mCurrentRepeatIteration)); + FireTimeEventAsync(NS_SMIL_REPEAT, + static_cast(mCurrentRepeatIteration)); + } } } } @@ -1084,12 +1097,8 @@ nsSMILTimedElement::SetFillMode(const nsAString& aFillModeSpec) ? nsSMILFillMode(temp.GetEnumValue()) : FILL_REMOVE; - // Check if we're in a fill-able state: i.e. we've played at least one - // interval and are now between intervals or at the end of all intervals - bool isFillable = HasPlayed() && - (mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE); - - if (mClient && mFillMode != previousFillMode && isFillable) { + // Update fill mode of client + if (mFillMode != previousFillMode && HasClientInFillRange()) { mClient->Inactivate(mFillMode == FILL_FREEZE); SampleFillValue(); } @@ -1102,9 +1111,9 @@ nsSMILTimedElement::UnsetFillMode() { uint16_t previousFillMode = mFillMode; mFillMode = FILL_REMOVE; - if ((mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) && - previousFillMode == FILL_FREEZE && mClient && HasPlayed()) + if (previousFillMode == FILL_FREEZE && HasClientInFillRange()) { mClient->Inactivate(false); + } } void @@ -1865,8 +1874,7 @@ nsSMILTimedElement::ApplyMinAndMax(const nsSMILTimeValue& aDuration) const if (aDuration > mMax) { result = mMax; } else if (aDuration < mMin) { - nsSMILTimeValue repeatDur = GetRepeatDuration(); - result = mMin > repeatDur ? repeatDur : mMin; + result = mMin; } else { result = aDuration; } @@ -2060,16 +2068,35 @@ nsSMILTimedElement::SampleFillValue() if (mFillMode != FILL_FREEZE || !mClient) return; - const nsSMILInterval* prevInterval = GetPreviousInterval(); - NS_ABORT_IF_FALSE(prevInterval, - "Attempting to sample fill value but there is no previous interval"); - NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsDefinite() && - prevInterval->End()->IsFixedTime(), - "Attempting to sample fill value but the endpoint of the previous " - "interval is not resolved and fixed"); + nsSMILTime activeTime; - nsSMILTime activeTime = prevInterval->End()->Time().GetMillis() - - prevInterval->Begin()->Time().GetMillis(); + if (mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) { + const nsSMILInterval* prevInterval = GetPreviousInterval(); + NS_ABORT_IF_FALSE(prevInterval, + "Attempting to sample fill value but there is no previous interval"); + NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsDefinite() && + prevInterval->End()->IsFixedTime(), + "Attempting to sample fill value but the endpoint of the previous " + "interval is not resolved and fixed"); + + activeTime = prevInterval->End()->Time().GetMillis() - + prevInterval->Begin()->Time().GetMillis(); + + // If the interval's repeat duration was shorter than its active duration, + // use the end of the repeat duration to determine the frozen animation's + // state. + nsSMILTimeValue repeatDuration = GetRepeatDuration(); + if (repeatDuration.IsDefinite()) { + activeTime = std::min(repeatDuration.GetMillis(), activeTime); + } + } else if (mElementState == STATE_ACTIVE) { + // If we are being asked to sample the fill value while active we *must* + // have a repeat duration shorter than the active duration so use that. + MOZ_ASSERT(GetRepeatDuration().IsDefinite(), + "Attempting to sample fill value of an active animation with " + "an indefinite repeat duration"); + activeTime = GetRepeatDuration().GetMillis(); + } uint32_t repeatIteration; nsSMILTime simpleTime = @@ -2165,8 +2192,13 @@ nsSMILTimedElement::GetNextMilestone(nsSMILMilestone& aNextMilestone) const // Work out what comes next: the interval end or the next repeat iteration nsSMILTimeValue nextRepeat; if (mSeekState == SEEK_NOT_SEEKING && mSimpleDur.IsDefinite()) { - nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() + - (mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis()); + nsSMILTime nextRepeatActiveTime = + (mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis(); + // Check that the repeat fits within the repeat duration + if (nsSMILTimeValue(nextRepeatActiveTime) < GetRepeatDuration()) { + nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() + + nextRepeatActiveTime); + } } nsSMILTimeValue nextMilestone = std::min(mCurrentInterval->End()->Time(), nextRepeat); @@ -2275,6 +2307,15 @@ nsSMILTimedElement::GetPreviousInterval() const : mOldIntervals[mOldIntervals.Length()-1].get(); } +bool +nsSMILTimedElement::HasClientInFillRange() const +{ + // Returns true if we have a client that is in the range where it will fill + return mClient && + ((mElementState != STATE_ACTIVE && HasPlayed()) || + (mElementState == STATE_ACTIVE && !mClient->IsActive())); +} + bool nsSMILTimedElement::EndHasEventConditions() const { diff --git a/content/smil/nsSMILTimedElement.h b/content/smil/nsSMILTimedElement.h index c97ff7bd0b62..2b905499f96a 100644 --- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -512,6 +512,7 @@ protected: const nsSMILInstanceTime* GetEffectiveBeginInstance() const; const nsSMILInterval* GetPreviousInterval() const; bool HasPlayed() const { return !mOldIntervals.IsEmpty(); } + bool HasClientInFillRange() const; bool EndHasEventConditions() const; bool AreEndTimesDependentOn( const nsSMILInstanceTime* aBase) const; diff --git a/content/smil/test/mochitest.ini b/content/smil/test/mochitest.ini index f208c0e7a81f..02b724f0a50e 100644 --- a/content/smil/test/mochitest.ini +++ b/content/smil/test/mochitest.ini @@ -38,6 +38,7 @@ support-files = [test_smilMappedAttrFromBy.xhtml] [test_smilMappedAttrFromTo.xhtml] [test_smilMappedAttrPaced.xhtml] +[test_smilMinTiming.html] [test_smilRepeatDuration.html] [test_smilRepeatTiming.xhtml] [test_smilReset.xhtml] diff --git a/content/smil/test/test_smilMinTiming.html b/content/smil/test/test_smilMinTiming.html new file mode 100644 index 000000000000..e336d0f9aca6 --- /dev/null +++ b/content/smil/test/test_smilMinTiming.html @@ -0,0 +1,93 @@ + + + + + + Test for Bug 948245 + + + + +Mozilla Bug 948245 +

+ +
+
+
+ + diff --git a/layout/reftests/svg/smil/min-1.svg b/layout/reftests/svg/smil/min-1.svg new file mode 100644 index 000000000000..6ba576d45213 --- /dev/null +++ b/layout/reftests/svg/smil/min-1.svg @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/layout/reftests/svg/smil/reftest.list b/layout/reftests/svg/smil/reftest.list index e58d9273220a..9f1ba4e80c28 100644 --- a/layout/reftests/svg/smil/reftest.list +++ b/layout/reftests/svg/smil/reftest.list @@ -251,6 +251,8 @@ fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,2) == anim-gradient-attr-presence-0 # interaction between xml mapped attributes and their css equivalents == mapped-attr-vs-css-prop-1.svg lime.svg +== min-1.svg lime.svg + == smil-transitions-interaction-1a.svg lime.svg == smil-transitions-interaction-1b.svg lime.svg == smil-transitions-interaction-2a.svg lime.svg From 0de483c99ecbe1cbbd1f72972227daf430142050 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 13 Dec 2013 13:41:56 +0900 Subject: [PATCH 113/459] Bug 941315 - Update the timing model even when invalid values are set; r=longsonr When an invalid dur, min, max, or repeatDur is set we were failing to call UpdateCurrentInterval to update the timing model. This patch makes sure we update the current interval in error cases too. Mochitests test the interval is being updated in these cases and, for completeness, the case of repeatCount as well. --- content/smil/nsSMILTimedElement.cpp | 54 +++++++-- content/smil/nsSMILTimedElement.h | 3 + content/smil/test/mochitest.ini | 1 + content/smil/test/test_smilInvalidValues.html | 113 ++++++++++++++++++ 4 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 content/smil/test/test_smilInvalidValues.html diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index 0da19cdbf412..db6f05975a9e 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -112,8 +112,9 @@ namespace //---------------------------------------------------------------------- // Helper class: AutoIntervalUpdateBatcher -// RAII helper to set the mDeferIntervalUpdates flag on an nsSMILTimedElement -// and perform the UpdateCurrentInterval when the object is destroyed. +// Stack-based helper class to set the mDeferIntervalUpdates flag on an +// nsSMILTimedElement and perform the UpdateCurrentInterval when the object is +// destroyed. // // If several of these objects are allocated on the stack, the update will not // be performed until the last object for a given nsSMILTimedElement is @@ -146,6 +147,31 @@ private: bool mDidSetFlag; }; +//---------------------------------------------------------------------- +// Helper class: AutoIntervalUpdater + +// Stack-based helper class to call UpdateCurrentInterval when it is destroyed +// which helps avoid bugs where we forget to call UpdateCurrentInterval in the +// case of early returns (e.g. due to parse errors). +// +// This can be safely used in conjunction with AutoIntervalUpdateBatcher; any +// calls to UpdateCurrentInterval made by this class will simply be deferred if +// there is an AutoIntervalUpdateBatcher on the stack. +class MOZ_STACK_CLASS nsSMILTimedElement::AutoIntervalUpdater +{ +public: + AutoIntervalUpdater(nsSMILTimedElement& aTimedElement) + : mTimedElement(aTimedElement) { } + + ~AutoIntervalUpdater() + { + mTimedElement.UpdateCurrentInterval(); + } + +private: + nsSMILTimedElement& mTimedElement; +}; + //---------------------------------------------------------------------- // Templated helper functions @@ -918,8 +944,10 @@ nsSMILTimedElement::UnsetEndSpec(RemovalTestFunction aRemove) nsresult nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec) { - nsSMILTimeValue duration; + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILTimeValue duration; const nsAString& dur = nsSMILParserUtils::TrimWhitespace(aDurSpec); // SVG-specific: "For SVG's animation elements, if "media" is specified, the @@ -939,7 +967,6 @@ nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec) "Setting unresolved simple duration"); mSimpleDur = duration; - UpdateCurrentInterval(); return NS_OK; } @@ -954,8 +981,10 @@ nsSMILTimedElement::UnsetSimpleDuration() nsresult nsSMILTimedElement::SetMin(const nsAString& aMinSpec) { - nsSMILTimeValue duration; + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILTimeValue duration; const nsAString& min = nsSMILParserUtils::TrimWhitespace(aMinSpec); if (min.EqualsLiteral("media")) { @@ -970,7 +999,6 @@ nsSMILTimedElement::SetMin(const nsAString& aMinSpec) NS_ABORT_IF_FALSE(duration.GetMillis() >= 0L, "Invalid duration"); mMin = duration; - UpdateCurrentInterval(); return NS_OK; } @@ -985,8 +1013,10 @@ nsSMILTimedElement::UnsetMin() nsresult nsSMILTimedElement::SetMax(const nsAString& aMaxSpec) { - nsSMILTimeValue duration; + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILTimeValue duration; const nsAString& max = nsSMILParserUtils::TrimWhitespace(aMaxSpec); if (max.EqualsLiteral("media") || max.EqualsLiteral("indefinite")) { @@ -1001,7 +1031,6 @@ nsSMILTimedElement::SetMax(const nsAString& aMaxSpec) } mMax = duration; - UpdateCurrentInterval(); return NS_OK; } @@ -1036,15 +1065,16 @@ nsSMILTimedElement::UnsetRestart() nsresult nsSMILTimedElement::SetRepeatCount(const nsAString& aRepeatCountSpec) { + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILRepeatCount newRepeatCount; if (nsSMILParserUtils::ParseRepeatCount(aRepeatCountSpec, newRepeatCount)) { mRepeatCount = newRepeatCount; - UpdateCurrentInterval(); return NS_OK; } mRepeatCount.Unset(); - UpdateCurrentInterval(); return NS_ERROR_FAILURE; } @@ -1058,6 +1088,9 @@ nsSMILTimedElement::UnsetRepeatCount() nsresult nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec) { + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILTimeValue duration; const nsAString& repeatDur = @@ -1073,7 +1106,6 @@ nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec) } mRepeatDur = duration; - UpdateCurrentInterval(); return NS_OK; } diff --git a/content/smil/nsSMILTimedElement.h b/content/smil/nsSMILTimedElement.h index 2b905499f96a..a8086acb8c52 100644 --- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -616,6 +616,9 @@ protected: bool mDoDeferredUpdate; // Set if an update to the current interval was // requested while mDeferIntervalUpdates was set + // Stack-based helper class to call UpdateCurrentInterval when it is destroyed + class AutoIntervalUpdater; + // Recursion depth checking uint8_t mDeleteCount; uint8_t mUpdateIntervalRecursionDepth; diff --git a/content/smil/test/mochitest.ini b/content/smil/test/mochitest.ini index 02b724f0a50e..c48b6f4330c7 100644 --- a/content/smil/test/mochitest.ini +++ b/content/smil/test/mochitest.ini @@ -32,6 +32,7 @@ support-files = [test_smilGetSimpleDuration.xhtml] [test_smilGetStartTime.xhtml] [test_smilHyperlinking.xhtml] +[test_smilInvalidValues.html] [test_smilKeySplines.xhtml] [test_smilKeyTimes.xhtml] [test_smilKeyTimesPacedMode.xhtml] diff --git a/content/smil/test/test_smilInvalidValues.html b/content/smil/test/test_smilInvalidValues.html new file mode 100644 index 000000000000..c24761c03efb --- /dev/null +++ b/content/smil/test/test_smilInvalidValues.html @@ -0,0 +1,113 @@ + + + + + + Test invalid values cause the model to be updated (bug 941315) + + + + +Mozilla Bug 941315 +

+ +
+
+
+ + From c2a5a16255ef8a7293281f8b390e4162fe92b34b Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 17:16:14 -0800 Subject: [PATCH 114/459] Bug 940765 - preference service can GC, r=terrence --HG-- rename : layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml => layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-1-ref.xhtml rename : layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3.xhtml => layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-1.xhtml extra : rebase_source : cad260c0819a3ced47e1ec612de2788e1e991670 --- js/src/devtools/rootAnalysis/annotations.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 1803eb87f5a2..08267463757e 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -160,10 +160,6 @@ var ignoreFunctions = { // FIXME! "NS_DebugBreak": true, - // Bug 940765 - fetching preferences should not GC - "PrefHashEntry* pref_HashTableLookup(void*)": true, - "uint8 mozilla::Preferences::InitStaticMembers()": true, // Temporary, see bug 940765 - // These are a little overzealous -- these destructors *can* GC if they end // up wrapping a pending exception. See bug 898815 for the heavyweight fix. "void js::AutoCompartment::~AutoCompartment(int32)" : true, From 6adc5e5233e69327afa3eb37610055ce53e63f03 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 17:16:47 -0800 Subject: [PATCH 115/459] Bug 948646 - AutoJSContext cannot GC, r=terrence --HG-- extra : rebase_source : f015f6bce00faa2c8e77cb861ed0770710e718ed --- js/src/devtools/rootAnalysis/annotations.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 08267463757e..530e489bdf3d 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -165,6 +165,13 @@ var ignoreFunctions = { "void js::AutoCompartment::~AutoCompartment(int32)" : true, "void JSAutoCompartment::~JSAutoCompartment(int32)" : true, + // Bug 948646 - the only thing AutoJSContext's constructor calls + // is an Init() routine whose entire body is covered with an + // AutoAssertNoGC. AutoSafeJSContext is the same thing, just with + // a different value for the 'aSafe' parameter. + "void mozilla::AutoJSContext::AutoJSContext(mozilla::detail::GuardObjectNotifier*)" : true, + "void mozilla::AutoSafeJSContext::~AutoSafeJSContext(int32)" : true, + // And these are workarounds to avoid even more analysis work, // which would sadly still be needed even with bug 898815. "void js::AutoCompartment::AutoCompartment(js::ExclusiveContext*, JSCompartment*)": true, From 89d34ffe587efae024fd1b1306ffc4149508560c Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 17:17:14 -0800 Subject: [PATCH 116/459] Bug 948753 - Annotate sane nsISupports virtual methods, r=bhackett --HG-- extra : rebase_source : 85695549d5d068bd393fc50e7190da00f95970ca --- js/src/devtools/rootAnalysis/annotations.js | 14 ++++++++++++++ .../devtools/rootAnalysis/computeCallgraph.js | 17 ++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 530e489bdf3d..d2e479eb018c 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -235,3 +235,17 @@ function isSuppressConstructor(name) || /::AutoEnterAnalysis/.test(name) || /::AutoAssertNoGC/.test(name); } + +// nsISupports subclasses' methods may be scriptable (or overridden +// via binary XPCOM), and so may GC. But some fields just aren't going +// to get overridden with something that can GC. +function isOverridableField(csu, field) +{ + if (csu != 'nsISupports') + return false; + if (field == 'GetCurrentJSContext') + return false; + if (field == 'IsOnCurrentThread') + return false; + return true; +} diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index 3b66de0a6045..493cb54e173a 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -61,13 +61,13 @@ function findVirtualFunctions(csu, field, suppressed) // which should never enter the JS engine (even when calling dtors). while (worklist.length) { var csu = worklist.pop(); - if (csu == "nsISupports") { - if (field == "AddRef" || field == "Release") { - suppressed[0] = true; - return []; - } - return null; + if (csu == "nsISupports" && (field == "AddRef" || field == "Release")) { + suppressed[0] = true; + return []; } + if (isOverridableField(csu, field)) + return null; + if (csu in superclasses) { for (var superclass of superclasses[csu]) worklist.push(superclass); @@ -258,6 +258,8 @@ for (var csuIndex = minStream; csuIndex <= maxStream; csuIndex++) { xdb.open("src_body.xdb"); +printErr("Finished loading data structures"); + var minStream = xdb.min_data_stream(); var maxStream = xdb.max_data_stream(); @@ -275,8 +277,9 @@ for (var nameIndex = minStream; nameIndex <= maxStream; nameIndex++) { seenCallees = {}; seenSuppressedCallees = {}; + var functionName = name.readString(); for (var body of functionBodies) - processBody(name.readString(), body); + processBody(functionName, body); xdb.free_string(name); xdb.free_string(data); From 2a86d8d7e6d707e37347fe2bc1e850b75c827e71 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 17:17:17 -0800 Subject: [PATCH 117/459] No bug. Remove noisy debugging printout. --HG-- extra : rebase_source : f5aa45dcb6c5d051f8888682d3aeab71b7df56d5 --- js/src/devtools/rootAnalysis/loadCallgraph.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/src/devtools/rootAnalysis/loadCallgraph.js b/js/src/devtools/rootAnalysis/loadCallgraph.js index 80216a1258b3..295d3f9ce7ce 100644 --- a/js/src/devtools/rootAnalysis/loadCallgraph.js +++ b/js/src/devtools/rootAnalysis/loadCallgraph.js @@ -148,9 +148,7 @@ function loadCallgraph(file) // Any field call that has been resolved to all possible callees can be // trusted to not GC if all of those callees are known to not GC. for (var name in resolvedFunctions) { - if (!(name in gcFunctions)) { + if (!(name in gcFunctions)) suppressedFunctions[name] = true; - printErr("Adding " + name); - } } } From 3e95adf082f60705ac613efdd57834f9af0acc6a Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 11 Dec 2013 21:24:13 -0800 Subject: [PATCH 118/459] Bug 949324 - Fix -Wunused-function warnings in js/. r=luke --- js/src/jit/IonMacroAssembler.cpp | 2 +- js/src/shell/js.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index c68c9de5ffa5..a0511b1c653c 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -1213,12 +1213,12 @@ IsCompilingAsmJS() IonContext *ictx = MaybeGetIonContext(); return ictx && ictx->compartment == nullptr; } -#endif static void AssumeUnreachable_(const char *output) { MOZ_ReportAssertionFailure(output, __FILE__, __LINE__); } +#endif void MacroAssembler::assumeUnreachable(const char *output) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 501bc02295ff..594de924ed93 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3591,13 +3591,17 @@ EscapeForShell(AutoCStringVector &argv) } #endif -Vector sPropagatedFlags; +static Vector sPropagatedFlags; +#ifdef DEBUG +#if (defined(JS_CPU_X86) || defined(JS_CPU_X64)) && defined(JS_ION) static bool PropagateFlagToNestedShells(const char *flag) { return sPropagatedFlags.append(flag); } +#endif +#endif static bool NestedShell(JSContext *cx, unsigned argc, jsval *vp) From 8c13061a4015001d2fd988359b8f2099e4191173 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 11 Dec 2013 21:13:24 -0800 Subject: [PATCH 119/459] Bug 949353 - Fix -Wsometimes-uninitialized warnings in dom/workers/WorkerPrivate.cpp. r=khuey --- dom/workers/WorkerPrivate.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 67a65c071ea7..f89f14a88165 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1208,7 +1208,7 @@ public: JS::Rooted target(aCx, JS::CurrentGlobalOrNull(aCx)); NS_ASSERTION(target, "This should never be null!"); - bool preventDefaultCalled; + nsEventStatus status = nsEventStatus_eIgnore; nsIScriptGlobalObject* sgo; if (aWorkerPrivate) { @@ -1222,15 +1222,12 @@ public: event.fileName = aFilename.get(); event.typeString = NS_LITERAL_STRING("error"); - nsEventStatus status = nsEventStatus_eIgnore; nsIDOMEventTarget* target = static_cast(globalTarget); if (NS_FAILED(nsEventDispatcher::Dispatch(target, nullptr, &event, nullptr, &status))) { NS_WARNING("Failed to dispatch worker thread error event!"); status = nsEventStatus_eIgnore; } - - preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault; } else if ((sgo = nsJSUtils::GetStaticScriptGlobal(target))) { // Icky, we have to fire an InternalScriptErrorEvent... @@ -1239,16 +1236,14 @@ public: event.errorMsg = aMessage.get(); event.fileName = aFilename.get(); - nsEventStatus status = nsEventStatus_eIgnore; if (NS_FAILED(sgo->HandleScriptError(&event, &status))) { NS_WARNING("Failed to dispatch main thread error event!"); status = nsEventStatus_eIgnore; } - - preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault; } - if (preventDefaultCalled) { + // Was preventDefault() called? + if (status == nsEventStatus_eConsumeNoDefault) { return true; } } From 41c9799518e68da8fd4c7af63db8f4ca914ff59d Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 12:15:16 -0800 Subject: [PATCH 120/459] Bug 940765 - Root before preference checking, r=bz --- dom/base/Navigator.cpp | 12 ++++++++---- dom/base/Navigator.h | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 908f1f936649..443deca78c45 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1703,14 +1703,16 @@ Navigator::HasMobileMessageSupport(JSContext* /* unused */, JSObject* aGlobal) /* static */ bool -Navigator::HasTelephonySupport(JSContext* /* unused */, JSObject* aGlobal) +Navigator::HasTelephonySupport(JSContext* cx, JSObject* aGlobal) { + JS::Rooted global(cx, aGlobal); + // First of all, the general pref has to be turned on. bool enabled = false; Preferences::GetBool("dom.telephony.enabled", &enabled); NS_ENSURE_TRUE(enabled, false); - nsCOMPtr win = GetWindowFromGlobal(aGlobal); + nsCOMPtr win = GetWindowFromGlobal(global); return win && CheckPermission(win, "telephony"); } @@ -1854,8 +1856,10 @@ bool Navigator::HasInputMethodSupport(JSContext* /* unused */, /* static */ bool -Navigator::HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal) +Navigator::HasDataStoreSupport(JSContext* cx, JSObject* aGlobal) { + JS::Rooted global(cx, aGlobal); + // First of all, the general pref has to be turned on. bool enabled = false; Preferences::GetBool("dom.datastore.enabled", &enabled); @@ -1866,7 +1870,7 @@ Navigator::HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal) return true; } - nsCOMPtr win = GetWindowFromGlobal(aGlobal); + nsCOMPtr win = GetWindowFromGlobal(global); if (!win) { return false; } diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 607b7086a960..282b3c51bd60 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -250,7 +250,7 @@ public: } static bool HasMobileMessageSupport(JSContext* /* unused */, JSObject* aGlobal); - static bool HasTelephonySupport(JSContext* /* unused */, + static bool HasTelephonySupport(JSContext* cx, JSObject* aGlobal); static bool HasCameraSupport(JSContext* /* unused */, JSObject* aGlobal); @@ -286,7 +286,7 @@ public: static bool HasInputMethodSupport(JSContext* /* unused */, JSObject* aGlobal); - static bool HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal); + static bool HasDataStoreSupport(JSContext* cx, JSObject* aGlobal); nsPIDOMWindow* GetParentObject() const { From 6f9caffe805cdf8a8b881e5bf7f221af679d26a4 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 13 Dec 2013 15:12:29 +0900 Subject: [PATCH 121/459] Bug 949875 - Delay import some WebIDL modules; r=glandium This prevents excessive dependencies on config.status. Those extra dependencies make WebIDL developer workflow inefficient. --HG-- extra : rebase_source : 37ba82773fe6d873e25fc8b61fef44ec9f191ebe extra : amend_source : 3fa0796f096efdbe6a74138ea0258f68cbcf4071 --- dom/bindings/mozwebidlcodegen/__init__.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py index 5516dbb3a1a0..cbd6312b23d4 100644 --- a/dom/bindings/mozwebidlcodegen/__init__.py +++ b/dom/bindings/mozwebidlcodegen/__init__.py @@ -24,14 +24,8 @@ from mozbuild.util import FileAvoidWrite import mozpack.path as mozpath -import WebIDL -from Codegen import ( - CGBindingRoot, - CGEventRoot, - CGExampleRoot, - GlobalGenRoots, -) -from Configuration import Configuration +# There are various imports in this file in functions to avoid adding +# dependencies to config.status. See bug 949875. class BuildResult(object): @@ -305,11 +299,16 @@ class WebIDLCodegenManager(LoggingMixin): def generate_example_files(self, interface): """Generates example files for a given interface.""" + from Codegen import CGExampleRoot + root = CGExampleRoot(self.config, interface) return self._maybe_write_codegen(root, *self._example_paths(interface)) def _parse_webidl(self): + import WebIDL + from Configuration import Configuration + self.log(logging.INFO, 'webidl_parse', {'count': len(self._input_paths)}, 'Parsing {count} WebIDL files.') @@ -328,6 +327,8 @@ class WebIDLCodegenManager(LoggingMixin): self._input_hashes = hashes def _write_global_derived(self): + from Codegen import GlobalGenRoots + things = [('declare', f) for f in self.GLOBAL_DECLARE_FILES] things.extend(('define', f) for f in self.GLOBAL_DEFINE_FILES) @@ -453,6 +454,11 @@ class WebIDLCodegenManager(LoggingMixin): return paths def _generate_build_files_for_webidl(self, filename): + from Codegen import ( + CGBindingRoot, + CGEventRoot, + ) + self.log(logging.INFO, 'webidl_generate_build_for_input', {'filename': filename}, 'Generating WebIDL files derived from {filename}') From 09c13814b1ee01c76e530aab1795c285281cdb74 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 13 Dec 2013 00:03:55 +0900 Subject: [PATCH 122/459] Bug 949304 - Print Makefile counts during config.status; r=glandium We now capture and print the number of Makefile.in and the number of generated Makefile as part of config.status. This should give us a nice, easy to extract metric going forward. --HG-- extra : rebase_source : 48a3e9f66975505dec76746703875b2364dedc87 --- .../mozbuild/backend/recursivemake.py | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index c9a4000dd46a..a909efff8ee4 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -268,17 +268,25 @@ class RecursiveMakeBackend(CommonBackend): self._ipdl_sources = set() def detailed(summary): - s = '{:d} total backend files. {:d} created; {:d} updated; {:d} unchanged'.format( + s = '{:d} total backend files; ' \ + '{:d} created; {:d} updated; {:d} unchanged; ' \ + '{:d} deleted; {:d} -> {:d} Makefile'.format( summary.created_count + summary.updated_count + - summary.unchanged_count, summary.created_count, - summary.updated_count, summary.unchanged_count) - if summary.deleted_count: - s+= '; {:d} deleted'.format(summary.deleted_count) + summary.unchanged_count, + summary.created_count, + summary.updated_count, + summary.unchanged_count, + summary.deleted_count, + summary.makefile_in_count, + summary.makefile_out_count) + return s # This is a little kludgy and could be improved with a better API. self.summary.backend_detailed_summary = types.MethodType(detailed, self.summary) + self.summary.makefile_in_count = 0 + self.summary.makefile_out_count = 0 self._test_manifests = {} @@ -664,6 +672,7 @@ class RecursiveMakeBackend(CommonBackend): if not stub: self.log(logging.DEBUG, 'substitute_makefile', {'path': makefile}, 'Substituting makefile: {path}') + self.summary.makefile_in_count += 1 for tier, skiplist in self._may_skip.items(): if tier in ('compile', 'binaries'): @@ -1104,6 +1113,8 @@ class RecursiveMakeBackend(CommonBackend): # the autogenerated one automatically. self.backend_input_files.add(obj.input_path) + self.summary.makefile_out_count += 1 + def _handle_webidl_collection(self, webidls): if not webidls.all_stems(): return From d917ee6f02beb60e1e5f0a6d55fc209325656710 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 12 Dec 2013 23:18:43 -0800 Subject: [PATCH 123/459] Bug 944074 - Remove the ParallelArray constructor. (r=nmatsakis) --- .../mochitest/general/test_interfaces.html | 1 - js/src/Makefile.in | 1 - js/src/builtin/Array.js | 4 +- js/src/builtin/ParallelArray.cpp | 247 ---- js/src/builtin/ParallelArray.h | 56 - js/src/builtin/ParallelArray.js | 1253 ----------------- js/src/builtin/Utilities.js | 30 + js/src/jit-test/lib/parallelarray-helpers.js | 98 +- .../jit-test/tests/auto-regress/bug755564.js | 9 - .../jit-test/tests/auto-regress/bug783923.js | 6 - .../jit-test/tests/auto-regress/bug784011.js | 8 - .../jit-test/tests/auto-regress/bug786106.js | 8 - .../jit-test/tests/auto-regress/bug789107.js | 7 - .../jit-test/tests/auto-regress/bug791445.js | 7 - js/src/jit-test/tests/baseline/bug846072.js | 7 - js/src/jit-test/tests/baseline/bug857579.js | 22 - js/src/jit-test/tests/basic/bug815652.js | 12 - .../tests/parallel/binary-arith-numbers.js | 5 +- js/src/jit-test/tests/parallel/bug783924.js | 7 - js/src/jit-test/tests/parallel/bug787282.js | 16 - js/src/jit-test/tests/parallel/bug853555.js | 12 - js/src/jit-test/tests/parallel/bug853573.js | 17 - js/src/jit-test/tests/parallel/bug854021.js | 3 - js/src/jit-test/tests/parallel/bug854050.js | 10 - js/src/jit-test/tests/parallel/bug854381.js | 20 - js/src/jit-test/tests/parallel/bug857846.js | 9 - js/src/jit-test/tests/parallel/bug858077.js | 5 - js/src/jit-test/tests/parallel/bug858582.js | 68 - js/src/jit-test/tests/parallel/bug890465.js | 6 - js/src/jit-test/tests/parallel/bug894782.js | 6 - js/src/jit-test/tests/parallel/bug895782.js | 12 - js/src/jit-test/tests/parallel/bug908939.js | 13 - js/src/jit-test/tests/parallel/bug909599.js | 6 - .../jit-test/tests/parallel/compare-values.js | 16 +- .../tests/parallel/comprehension-2.js | 13 - .../tests/parallel/comprehension-fn-args.js | 16 - .../tests/parallel/comprehension-scale.js | 22 - .../jit-test/tests/parallel/constructor-1.js | 15 - .../jit-test/tests/parallel/constructor-2.js | 15 - .../jit-test/tests/parallel/constructor-3.js | 14 - .../jit-test/tests/parallel/constructor-4.js | 16 - .../jit-test/tests/parallel/constructor-5.js | 18 - .../tests/parallel/constructor-throws.js | 10 - js/src/jit-test/tests/parallel/element-1.js | 14 - js/src/jit-test/tests/parallel/element-2.js | 26 - js/src/jit-test/tests/parallel/element-3.js | 12 - js/src/jit-test/tests/parallel/flatten-1.js | 14 - js/src/jit-test/tests/parallel/flatten-2.js | 10 - js/src/jit-test/tests/parallel/flatten-3.js | 25 - .../jit-test/tests/parallel/flatten-throws.js | 12 - js/src/jit-test/tests/parallel/get-1.js | 9 - js/src/jit-test/tests/parallel/get-2.js | 14 - js/src/jit-test/tests/parallel/get-3.js | 9 - js/src/jit-test/tests/parallel/get-4.js | 7 - js/src/jit-test/tests/parallel/get-6.js | 14 - js/src/jit-test/tests/parallel/holes-1.js | 31 - js/src/jit-test/tests/parallel/holes-2.js | 25 - js/src/jit-test/tests/parallel/index-1.js | 9 - js/src/jit-test/tests/parallel/index-2.js | 18 - js/src/jit-test/tests/parallel/index-3.js | 25 - js/src/jit-test/tests/parallel/index-4.js | 27 - .../tests/parallel/inline-new-bad-type.js | 18 - js/src/jit-test/tests/parallel/length-1.js | 12 - js/src/jit-test/tests/parallel/length-2.js | 13 - js/src/jit-test/tests/parallel/length-3.js | 11 - js/src/jit-test/tests/parallel/mandelbrot.js | 4 +- js/src/jit-test/tests/parallel/map-3.js | 12 - .../tests/parallel/overflow-throws.js | 12 - js/src/jit-test/tests/parallel/partition-1.js | 15 - .../tests/parallel/partition-throws.js | 10 - .../tests/parallel/reduce-higher-dim.js | 14 - .../jit-test/tests/parallel/reduce-throws.js | 17 - js/src/jit-test/tests/parallel/scan-3.js | 17 - js/src/jit-test/tests/parallel/scan-throws.js | 18 - js/src/jit-test/tests/parallel/scatter-1.js | 8 - js/src/jit-test/tests/parallel/scatter-10.js | 21 - js/src/jit-test/tests/parallel/scatter-11.js | 21 - js/src/jit-test/tests/parallel/scatter-12.js | 22 - js/src/jit-test/tests/parallel/scatter-13.js | 24 - js/src/jit-test/tests/parallel/scatter-2.js | 10 - js/src/jit-test/tests/parallel/scatter-3.js | 12 - js/src/jit-test/tests/parallel/scatter-4.js | 10 - js/src/jit-test/tests/parallel/scatter-5.js | 9 - js/src/jit-test/tests/parallel/scatter-6.js | 13 - js/src/jit-test/tests/parallel/scatter-7.js | 15 - js/src/jit-test/tests/parallel/scatter-8.js | 16 - js/src/jit-test/tests/parallel/scatter-9.js | 12 - .../tests/parallel/scatter-regression-1.js | 18 - .../jit-test/tests/parallel/scatter-throws.js | 31 - js/src/jit-test/tests/parallel/shape-1.js | 8 - js/src/jit-test/tests/parallel/shape-2.js | 15 - js/src/jit-test/tests/parallel/shape-3.js | 16 - js/src/jit-test/tests/parallel/shape-4.js | 13 - js/src/jit-test/tests/parallel/shape-5.js | 13 - .../jit-test/tests/parallel/stack-overflow.js | 4 +- js/src/jit-test/tests/parallel/toString-1.js | 6 - js/src/jit/BaselineIC.cpp | 13 - js/src/jit/CodeGenerator.cpp | 70 - js/src/jit/CodeGenerator.h | 4 - js/src/jit/IonBuilder.h | 10 - js/src/jit/LIR-Common.h | 10 - js/src/jit/LOpcodes.h | 1 - js/src/jit/Lowering.cpp | 7 - js/src/jit/Lowering.h | 1 - js/src/jit/MCallOptimize.cpp | 152 -- js/src/jit/MIR.h | 26 - js/src/jit/MOpcodes.h | 1 - js/src/jit/ParallelSafetyAnalysis.cpp | 7 - js/src/jit/VMFunctions.cpp | 16 - js/src/js.msg | 8 +- js/src/jsapi.cpp | 1 - js/src/jsinferinlines.h | 4 - js/src/jsprototypes.h | 7 +- js/src/moz.build | 1 - js/src/vm/ForkJoin.cpp | 2 +- js/src/vm/GlobalObject.cpp | 3 - js/src/vm/SelfHosting.cpp | 23 - 117 files changed, 58 insertions(+), 3246 deletions(-) delete mode 100644 js/src/builtin/ParallelArray.cpp delete mode 100644 js/src/builtin/ParallelArray.h delete mode 100644 js/src/builtin/ParallelArray.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug755564.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug783923.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug784011.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug786106.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug789107.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug791445.js delete mode 100644 js/src/jit-test/tests/baseline/bug846072.js delete mode 100644 js/src/jit-test/tests/baseline/bug857579.js delete mode 100644 js/src/jit-test/tests/basic/bug815652.js delete mode 100644 js/src/jit-test/tests/parallel/bug783924.js delete mode 100644 js/src/jit-test/tests/parallel/bug787282.js delete mode 100644 js/src/jit-test/tests/parallel/bug853555.js delete mode 100644 js/src/jit-test/tests/parallel/bug853573.js delete mode 100644 js/src/jit-test/tests/parallel/bug854021.js delete mode 100644 js/src/jit-test/tests/parallel/bug854050.js delete mode 100644 js/src/jit-test/tests/parallel/bug854381.js delete mode 100644 js/src/jit-test/tests/parallel/bug857846.js delete mode 100644 js/src/jit-test/tests/parallel/bug858077.js delete mode 100644 js/src/jit-test/tests/parallel/bug858582.js delete mode 100644 js/src/jit-test/tests/parallel/bug890465.js delete mode 100644 js/src/jit-test/tests/parallel/bug894782.js delete mode 100644 js/src/jit-test/tests/parallel/bug895782.js delete mode 100644 js/src/jit-test/tests/parallel/bug908939.js delete mode 100644 js/src/jit-test/tests/parallel/bug909599.js delete mode 100644 js/src/jit-test/tests/parallel/comprehension-2.js delete mode 100644 js/src/jit-test/tests/parallel/comprehension-fn-args.js delete mode 100644 js/src/jit-test/tests/parallel/comprehension-scale.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-1.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-2.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-3.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-4.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-5.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-throws.js delete mode 100644 js/src/jit-test/tests/parallel/element-1.js delete mode 100644 js/src/jit-test/tests/parallel/element-2.js delete mode 100644 js/src/jit-test/tests/parallel/element-3.js delete mode 100644 js/src/jit-test/tests/parallel/flatten-1.js delete mode 100644 js/src/jit-test/tests/parallel/flatten-2.js delete mode 100644 js/src/jit-test/tests/parallel/flatten-3.js delete mode 100644 js/src/jit-test/tests/parallel/flatten-throws.js delete mode 100644 js/src/jit-test/tests/parallel/get-1.js delete mode 100644 js/src/jit-test/tests/parallel/get-2.js delete mode 100644 js/src/jit-test/tests/parallel/get-3.js delete mode 100644 js/src/jit-test/tests/parallel/get-4.js delete mode 100644 js/src/jit-test/tests/parallel/get-6.js delete mode 100644 js/src/jit-test/tests/parallel/holes-1.js delete mode 100644 js/src/jit-test/tests/parallel/holes-2.js delete mode 100644 js/src/jit-test/tests/parallel/index-1.js delete mode 100644 js/src/jit-test/tests/parallel/index-2.js delete mode 100644 js/src/jit-test/tests/parallel/index-3.js delete mode 100644 js/src/jit-test/tests/parallel/index-4.js delete mode 100644 js/src/jit-test/tests/parallel/inline-new-bad-type.js delete mode 100644 js/src/jit-test/tests/parallel/length-1.js delete mode 100644 js/src/jit-test/tests/parallel/length-2.js delete mode 100644 js/src/jit-test/tests/parallel/length-3.js delete mode 100644 js/src/jit-test/tests/parallel/map-3.js delete mode 100644 js/src/jit-test/tests/parallel/overflow-throws.js delete mode 100644 js/src/jit-test/tests/parallel/partition-1.js delete mode 100644 js/src/jit-test/tests/parallel/partition-throws.js delete mode 100644 js/src/jit-test/tests/parallel/reduce-higher-dim.js delete mode 100644 js/src/jit-test/tests/parallel/reduce-throws.js delete mode 100644 js/src/jit-test/tests/parallel/scan-3.js delete mode 100644 js/src/jit-test/tests/parallel/scan-throws.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-1.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-10.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-11.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-12.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-13.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-2.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-3.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-4.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-5.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-6.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-7.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-8.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-9.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-regression-1.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-throws.js delete mode 100644 js/src/jit-test/tests/parallel/shape-1.js delete mode 100644 js/src/jit-test/tests/parallel/shape-2.js delete mode 100644 js/src/jit-test/tests/parallel/shape-3.js delete mode 100644 js/src/jit-test/tests/parallel/shape-4.js delete mode 100644 js/src/jit-test/tests/parallel/shape-5.js delete mode 100644 js/src/jit-test/tests/parallel/toString-1.js diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 3b2363f47d9f..0ec83523aa00 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -69,7 +69,6 @@ var ecmaGlobals = {name: "NaN", xbl: false}, "Number", "Object", - {name: "ParallelArray", nightly: true}, "Proxy", "RangeError", "ReferenceError", diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 1a7008f5c4c6..6999747d6d3d 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -579,7 +579,6 @@ selfhosting_srcs := \ $(srcdir)/builtin/Iterator.js \ $(srcdir)/builtin/Map.js \ $(srcdir)/builtin/Number.js \ - $(srcdir)/builtin/ParallelArray.js \ $(srcdir)/builtin/String.js \ $(srcdir)/builtin/Set.js \ $(srcdir)/builtin/TypedObject.js \ diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index b61c6c6a3192..d27f159ac295 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -556,6 +556,8 @@ function ArrayKeys() { return CreateArrayIterator(this, ITEM_KIND_KEY); } +#ifdef ENABLE_PARALLEL_JS + /* * Strawman spec: * http://wiki.ecmascript.org/doku.php?id=strawman:data_parallelism @@ -649,8 +651,6 @@ function ComputeAllSliceBounds(numItems, numSlices) { return info; } -#ifdef ENABLE_PARALLEL_JS - /** * Creates a new array by applying |func(e, i, self)| for each element |e| * with index |i|. diff --git a/js/src/builtin/ParallelArray.cpp b/js/src/builtin/ParallelArray.cpp deleted file mode 100644 index a046819a9a2e..000000000000 --- a/js/src/builtin/ParallelArray.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "builtin/ParallelArray.h" - -#include "jsobj.h" - -#include "vm/GlobalObject.h" -#include "vm/String.h" - -#include "jsobjinlines.h" - -using namespace js; - -// -// ParallelArrayObject -// - -FixedHeapPtr ParallelArrayObject::ctorNames[NumCtors]; - -const JSFunctionSpec ParallelArrayObject::methods[] = { - JS_SELF_HOSTED_FN("map", "ParallelArrayMap", 2, 0), - JS_SELF_HOSTED_FN("reduce", "ParallelArrayReduce", 2, 0), - JS_SELF_HOSTED_FN("scan", "ParallelArrayScan", 2, 0), - JS_SELF_HOSTED_FN("scatter", "ParallelArrayScatter", 5, 0), - JS_SELF_HOSTED_FN("filter", "ParallelArrayFilter", 2, 0), - JS_SELF_HOSTED_FN("partition", "ParallelArrayPartition", 1, 0), - JS_SELF_HOSTED_FN("flatten", "ParallelArrayFlatten", 0, 0), - - // FIXME #838906. Note that `get()` is not currently defined on this table but - // rather is assigned to each instance of ParallelArray as an own - // property. This is a bit of a hack designed to supply a - // specialized version of get() based on the dimensionality of the - // receiver. In the future we can improve this by (1) extending - // TI to track the dimensionality of the receiver and (2) using a - // hint to aggressively inline calls to get(). - // JS_SELF_HOSTED_FN("get", "ParallelArrayGet", 1, 0), - - JS_SELF_HOSTED_FN("toString", "ParallelArrayToString", 0, 0), - JS_FS_END -}; - -const JSPropertySpec ParallelArrayObject::properties[] = { - JS_SELF_HOSTED_GET("length", "ParallelArrayLength", JSPROP_PERMANENT), - JS_PS_END -}; - -const Class ParallelArrayObject::protoClass = { - "ParallelArray", - JSCLASS_HAS_CACHED_PROTO(JSProto_ParallelArray), - JS_PropertyStub, // addProperty - JS_DeletePropertyStub, // delProperty - JS_PropertyStub, // getProperty - JS_StrictPropertyStub, // setProperty - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub -}; - -const Class ParallelArrayObject::class_ = { - "ParallelArray", - JSCLASS_HAS_CACHED_PROTO(JSProto_ParallelArray), - JS_PropertyStub, // addProperty - JS_DeletePropertyStub, // delProperty - JS_PropertyStub, // getProperty - JS_StrictPropertyStub, // setProperty - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub -}; - -/*static*/ bool -ParallelArrayObject::initProps(JSContext *cx, HandleObject obj) -{ - RootedValue undef(cx, UndefinedValue()); - RootedValue zero(cx, Int32Value(0)); - - if (!JSObject::defineProperty(cx, obj, cx->names().buffer, undef)) - return false; - if (!JSObject::defineProperty(cx, obj, cx->names().offset, zero)) - return false; - if (!JSObject::defineProperty(cx, obj, cx->names().shape, undef)) - return false; - if (!JSObject::defineProperty(cx, obj, cx->names().get, undef)) - return false; - - return true; -} - -/*static*/ bool -ParallelArrayObject::construct(JSContext *cx, unsigned argc, Value *vp) -{ - RootedFunction ctor(cx, getConstructor(cx, argc)); - if (!ctor) - return false; - CallArgs args = CallArgsFromVp(argc, vp); - return constructHelper(cx, &ctor, args); -} - -/* static */ JSFunction * -ParallelArrayObject::maybeGetConstructor(GlobalObject *global, unsigned argc) -{ - PropertyName *ctorName = ctorNames[js::Min(argc, NumCtors - 1)]; - Value ctorValue; - if (!global->maybeGetIntrinsicValue(ctorName, &ctorValue)) - return nullptr; - JS_ASSERT(ctorValue.isObject() && ctorValue.toObject().is()); - return &ctorValue.toObject().as(); -} - -/* static */ JSFunction * -ParallelArrayObject::getConstructor(JSContext *cx, unsigned argc) -{ - RootedPropertyName ctorName(cx, ctorNames[js::Min(argc, NumCtors - 1)]); - RootedValue ctorValue(cx); - if (!cx->global()->getIntrinsicValue(cx, ctorName, &ctorValue)) - return nullptr; - JS_ASSERT(ctorValue.isObject() && ctorValue.toObject().is()); - return &ctorValue.toObject().as(); -} - -/*static*/ JSObject * -ParallelArrayObject::newInstance(JSContext *cx, NewObjectKind newKind /* = GenericObject */) -{ - gc::AllocKind kind = gc::GetGCObjectKind(NumFixedSlots); - RootedObject result(cx, NewBuiltinClassInstance(cx, &class_, kind, newKind)); - if (!result) - return nullptr; - - // Add in the basic PA properties now with default values: - if (!initProps(cx, result)) - return nullptr; - - return result; -} - -/*static*/ bool -ParallelArrayObject::constructHelper(JSContext *cx, MutableHandleFunction ctor, CallArgs &args0) -{ - RootedObject result(cx, newInstance(cx, TenuredObject)); - if (!result) - return false; - - if (cx->typeInferenceEnabled()) { - jsbytecode *pc; - RootedScript script(cx, cx->currentScript(&pc)); - if (script) { - if (ctor->nonLazyScript()->shouldCloneAtCallsite()) { - ctor.set(CloneFunctionAtCallsite(cx, ctor, script, pc)); - if (!ctor) - return false; - } - - // Create the type object for the PA. Add in the current - // properties as definite properties if this type object is newly - // created. To tell if it is newly created, we check whether it - // has any properties yet or not, since any returned type object - // must have been created by this same C++ code and hence would - // already have properties if it had been returned before. - types::TypeObject *paTypeObject = - types::TypeScript::InitObject(cx, script, pc, JSProto_ParallelArray); - if (!paTypeObject) - return false; - if (paTypeObject->getPropertyCount() == 0 && !paTypeObject->unknownProperties()) { - if (!paTypeObject->addDefiniteProperties(cx, result)) - return false; - - // addDefiniteProperties() above should have added one - // property for each of the fixed slots: - JS_ASSERT(paTypeObject->getPropertyCount() == NumFixedSlots); - } - result->setType(paTypeObject); - } - } - - InvokeArgs args(cx); - if (!args.init(args0.length())) - return false; - - args.setCallee(ObjectValue(*ctor)); - args.setThis(ObjectValue(*result)); - - for (uint32_t i = 0; i < args0.length(); i++) - args[i].set(args0[i]); - - if (!Invoke(cx, args)) - return false; - - args0.rval().setObject(*result); - return true; -} - -JSObject * -ParallelArrayObject::initClass(JSContext *cx, HandleObject obj) -{ - JS_ASSERT(obj->isNative()); - - // Cache constructor names. - { - static const char *const ctorStrs[NumCtors] = { - "ParallelArrayConstructEmpty", - "ParallelArrayConstructFromArray", - "ParallelArrayConstructFromFunction", - "ParallelArrayConstructFromFunctionMode" - }; - for (uint32_t i = 0; i < NumCtors; i++) { - JSAtom *atom = Atomize(cx, ctorStrs[i], strlen(ctorStrs[i]), InternAtom); - if (!atom) - return nullptr; - ctorNames[i].init(atom->asPropertyName()); - } - } - - Rooted global(cx, &obj->as()); - - RootedObject proto(cx, global->createBlankPrototype(cx, &protoClass)); - if (!proto) - return nullptr; - - JSProtoKey key = JSProto_ParallelArray; - RootedFunction ctor(cx, global->createConstructor(cx, construct, - cx->names().ParallelArray, 0)); - if (!ctor || - !LinkConstructorAndPrototype(cx, ctor, proto) || - !DefinePropertiesAndBrand(cx, proto, properties, methods) || - !DefineConstructorAndPrototype(cx, global, key, ctor, proto)) - { - return nullptr; - } - - return proto; -} - -bool -ParallelArrayObject::is(const Value &v) -{ - return v.isObject() && v.toObject().hasClass(&class_); -} - -JSObject * -js_InitParallelArrayClass(JSContext *cx, js::HandleObject obj) -{ - return ParallelArrayObject::initClass(cx, obj); -} diff --git a/js/src/builtin/ParallelArray.h b/js/src/builtin/ParallelArray.h deleted file mode 100644 index 3232ae70b2d0..000000000000 --- a/js/src/builtin/ParallelArray.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef builtin_ParallelArray_h -#define builtin_ParallelArray_h - -#include "jsobj.h" - -namespace js { - -class ParallelArrayObject : public JSObject -{ - static const Class protoClass; - static const JSFunctionSpec methods[]; - static const JSPropertySpec properties[]; - static const uint32_t NumFixedSlots = 4; - static const uint32_t NumCtors = 4; - static FixedHeapPtr ctorNames[NumCtors]; - - static bool initProps(JSContext *cx, HandleObject obj); - - public: - static const Class class_; - - static bool construct(JSContext *cx, unsigned argc, Value *vp); - static bool constructHelper(JSContext *cx, MutableHandleFunction ctor, CallArgs &args); - - // Creates a new ParallelArray instance with the correct number of slots - // and so forth. - // - // NOTE: This object will NOT have the correct type object! It is - // up to the caller to adjust the type object appropriately - // before releasing the object into the wild. You probably want - // to be calling construct() above, which will adjust the type - // object for you, since ParallelArray type objects must be setup - // in a rather particular way to interact well with the - // self-hosted code. See constructHelper() for details. - static JSObject *newInstance(JSContext *cx, NewObjectKind newKind = GenericObject); - - // Get the constructor function for argc number of arguments. - static JSFunction *maybeGetConstructor(GlobalObject *global, unsigned argc); - static JSFunction *getConstructor(JSContext *cx, unsigned argc); - - static JSObject *initClass(JSContext *cx, HandleObject obj); - static bool is(const Value &v); -}; - -} // namespace js - -extern JSObject * -js_InitParallelArrayClass(JSContext *cx, js::HandleObject obj); - -#endif /* builtin_ParallelArray_h */ diff --git a/js/src/builtin/ParallelArray.js b/js/src/builtin/ParallelArray.js deleted file mode 100644 index 4ae4c09c1850..000000000000 --- a/js/src/builtin/ParallelArray.js +++ /dev/null @@ -1,1253 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// FIXME(bug 844882): Parallel array properties should not be exposed. - -/** - * Compute the partial products in reverse order. - * e.g., if the shape is [A,B,C,D], then the - * array |products| will be [1,D,CD,BCD]. - */ -function ComputeProducts(shape) { - var product = 1; - var products = [1]; - var sdimensionality = shape.length; - for (var i = sdimensionality - 1; i > 0; i--) { - product *= shape[i]; - ARRAY_PUSH(products, product); - } - return products; -} - -/** - * Given a shape and some index |index1d|, computes and returns an - * array containing the N-dimensional index that maps to |index1d|. - */ -function ComputeIndices(shape, index1d) { - - var products = ComputeProducts(shape); - var l = shape.length; - - var result = []; - for (var i = 0; i < l; i++) { - // Obtain product of all higher dimensions. - // So if i == 0 and shape is [A,B,C,D], yields BCD. - var stride = products[l - i - 1]; - - // Compute how many steps of width stride we could take. - var index = (index1d / stride) | 0; - ARRAY_PUSH(result, index); - - // Adjust remaining indices for smaller dimensions. - index1d -= (index * stride); - } - - return result; -} - -function StepIndices(shape, indices) { - for (var i = shape.length - 1; i >= 0; i--) { - var indexi = indices[i] + 1; - if (indexi < shape[i]) { - indices[i] = indexi; - return; - } - indices[i] = 0; - } -} - -// Constructor -// -// We split the 3 construction cases so that we don't case on arguments. - -/** - * This is the function invoked for |new ParallelArray()| - */ -function ParallelArrayConstructEmpty() { - this.buffer = []; - this.offset = 0; - this.shape = [0]; - this.get = ParallelArrayGet1; -} - -/** - * This is the function invoked for |new ParallelArray(array)|. - * It copies the data from its array-like argument |array|. - */ -function ParallelArrayConstructFromArray(array) { - var buffer = ToObject(array); - var length = buffer.length >>> 0; - if (length !== buffer.length) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - - var buffer1 = []; - for (var i = 0; i < length; i++) - ARRAY_PUSH(buffer1, buffer[i]); - - this.buffer = buffer1; - this.offset = 0; - this.shape = [length]; - this.get = ParallelArrayGet1; -} - -/** - * Wrapper around |ParallelArrayConstructFromComprehension()| for the - * case where 2 arguments are supplied. This is typically what users will - * invoke. We provide an explicit two-argument version rather than - * relying on JS's semantics for absent arguments because it simplifies - * the ion code that does inlining of PA constructors. - */ -function ParallelArrayConstructFromFunction(shape, func) { - return ParallelArrayConstructFromComprehension(this, shape, func, undefined); -} - -/** - * Wrapper around |ParallelArrayConstructFromComprehension()| for the - * case where 3 arguments are supplied. - */ -function ParallelArrayConstructFromFunctionMode(shape, func, mode) { - return ParallelArrayConstructFromComprehension(this, shape, func, mode); -} - -/** - * "Comprehension form": This is the function invoked for |new - * ParallelArray(dim, fn)|. If |dim| is a number, then it creates a - * new 1-dimensional parallel array with shape |[dim]| where index |i| - * is equal to |fn(i)|. If |dim| is a vector, then it creates a new - * N-dimensional parallel array where index |a, b, ... z| is equal to - * |fn(a, b, ...z)|. - * - * The final |mode| argument is an internal argument used only - * during our unit-testing. - */ -function ParallelArrayConstructFromComprehension(self, shape, func, mode) { - // FIXME(bug 844887): Check |IsCallable(func)| - - if (typeof shape === "number") { - var length = shape >>> 0; - if (length !== shape) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - ParallelArrayBuild(self, [length], func, mode); - } else if (!shape || typeof shape.length !== "number") { - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - } else { - var shape1 = []; - for (var i = 0, l = shape.length; i < l; i++) { - var s0 = shape[i]; - var s1 = s0 >>> 0; - if (s1 !== s0) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - ARRAY_PUSH(shape1, s1); - } - ParallelArrayBuild(self, shape1, func, mode); - } -} - -/** - * Internal function used when constructing new parallel arrays. The - * NewParallelArray() intrinsic takes a ctor function which it invokes - * with the given shape, buffer, offset. The |this| parameter will be - * the newly constructed parallel array. - */ -function ParallelArrayView(shape, buffer, offset) { - this.shape = shape; - this.buffer = buffer; - this.offset = offset; - - switch (shape.length) { - case 1: this.get = ParallelArrayGet1; break; - case 2: this.get = ParallelArrayGet2; break; - case 3: this.get = ParallelArrayGet3; break; - default: this.get = ParallelArrayGetN; break; - } - - // Due to inlining of NewParallelArray, the return type of this function - // gets recorded as the return type of NewParallelArray at inlined sites, so - // we must take care to return the same thing. - return this; -} - -/** - * Helper for the comprehension form. Constructs an N-dimensional - * array where |N == shape.length|. |shape| must be an array of - * integers. The data for any given index vector |i| is determined by - * |func(...i)|. - */ -function ParallelArrayBuild(self, shape, func, mode) { - self.offset = 0; - self.shape = shape; - - var length; - var xDimension, yDimension, zDimension; - var computefunc; - - switch (shape.length) { - case 1: - length = shape[0]; - self.get = ParallelArrayGet1; - computefunc = fill1; - break; - case 2: - xDimension = shape[0]; - yDimension = shape[1]; - length = xDimension * yDimension; - self.get = ParallelArrayGet2; - computefunc = fill2; - break; - case 3: - xDimension = shape[0]; - yDimension = shape[1]; - zDimension = shape[2]; - length = xDimension * yDimension * zDimension; - self.get = ParallelArrayGet3; - computefunc = fill3; - break; - default: - length = 1; - for (var i = 0; i < shape.length; i++) - length *= shape[i]; - self.get = ParallelArrayGetN; - computefunc = fillN; - break; - } - - var buffer = self.buffer = NewDenseArray(length); - - parallel: for (;;) { - // Avoid parallel compilation if we are already nested in another - // parallel section or the user told us not to parallelize. The - // use of a for (;;) loop is working around some ion limitations: - // - // - Breaking out of named blocks does not currently work (bug 684384); - // - Unreachable Code Elim. can't properly handle if (a && b) (bug 669796) - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - if (computefunc === fillN) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - var info = ComputeAllSliceBounds(chunks, numSlices); - ForkJoin(constructSlice, ForkJoinMode(mode)); - return; - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - computefunc(0, length); - return; - - function constructSlice(sliceId, numSlices, warmup) { - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - if (warmup && chunkEnd > chunkPos) - chunkEnd = chunkPos + 1; - - while (chunkPos < chunkEnd) { - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - computefunc(indexStart, indexEnd); - UnsafePutElements(info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - function fill1(indexStart, indexEnd) { - for (var i = indexStart; i < indexEnd; i++) - UnsafePutElements(buffer, i, func(i)); - } - - function fill2(indexStart, indexEnd) { - var x = (indexStart / yDimension) | 0; - var y = indexStart - x * yDimension; - for (var i = indexStart; i < indexEnd; i++) { - UnsafePutElements(buffer, i, func(x, y)); - if (++y == yDimension) { - y = 0; - ++x; - } - } - } - - function fill3(indexStart, indexEnd) { - var x = (indexStart / (yDimension * zDimension)) | 0; - var r = indexStart - x * yDimension * zDimension; - var y = (r / zDimension) | 0; - var z = r - y * zDimension; - for (var i = indexStart; i < indexEnd; i++) { - UnsafePutElements(buffer, i, func(x, y, z)); - if (++z == zDimension) { - z = 0; - if (++y == yDimension) { - y = 0; - ++x; - } - } - } - } - - function fillN(indexStart, indexEnd) { - var indices = ComputeIndices(shape, indexStart); - for (var i = indexStart; i < indexEnd; i++) { - var result = callFunction(std_Function_apply, func, null, indices); - UnsafePutElements(buffer, i, result); - StepIndices(shape, indices); - } - } -} - -/** - * Creates a new parallel array by applying |func(e, i, self)| for each - * element |e| with index |i|. Note that - * this always operates on the outermost dimension only. - */ -function ParallelArrayMap(func, mode) { - // FIXME(bug 844887): Check |this instanceof ParallelArray| - // FIXME(bug 844887): Check |IsCallable(func)| - - var self = this; - var length = self.shape[0]; - var buffer = NewDenseArray(length); - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - var info = ComputeAllSliceBounds(chunks, numSlices); - ForkJoin(mapSlice, ForkJoinMode(mode)); - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - for (var i = 0; i < length; i++) { - // Note: Unlike JS arrays, parallel arrays cannot have holes. - var v = func(self.get(i), i, self); - UnsafePutElements(buffer, i, v); - } - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - - function mapSlice(sliceId, numSlices, warmup) { - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - if (warmup && chunkEnd > chunkPos + 1) - chunkEnd = chunkPos + 1; - - while (chunkPos < chunkEnd) { - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - - for (var i = indexStart; i < indexEnd; i++) - UnsafePutElements(buffer, i, func(self.get(i), i, self)); - - UnsafePutElements(info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - return undefined; -} - -/** - * Reduces the elements in a parallel array's outermost dimension - * using the given reduction function. - */ -function ParallelArrayReduce(func, mode) { - // FIXME(bug 844887): Check |this instanceof ParallelArray| - // FIXME(bug 844887): Check |IsCallable(func)| - - var self = this; - var length = self.shape[0]; - - if (length === 0) - ThrowError(JSMSG_PAR_ARRAY_REDUCE_EMPTY); - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - if (chunks < numSlices) - break parallel; - - var info = ComputeAllSliceBounds(chunks, numSlices); - var subreductions = NewDenseArray(numSlices); - ForkJoin(reduceSlice, ForkJoinMode(mode)); - var accumulator = subreductions[0]; - for (var i = 1; i < numSlices; i++) - accumulator = func(accumulator, subreductions[i]); - return accumulator; - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - var accumulator = self.get(0); - for (var i = 1; i < length; i++) - accumulator = func(accumulator, self.get(i)); - return accumulator; - - function reduceSlice(sliceId, numSlices, warmup) { - var chunkStart = info[SLICE_START(sliceId)]; - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - // (*) This function is carefully designed so that the warmup - // (which executes with chunkStart === chunkPos) will execute all - // potential loads and stores. In particular, the warmup run - // processes two chunks rather than one. Moreover, it stores - // accumulator into subreductions and then loads it again to - // ensure that the load is executed during the warmup, as it will - // certainly be executed during subsequent runs. - - if (warmup && chunkEnd > chunkPos + 2) - chunkEnd = chunkPos + 2; - - if (chunkStart === chunkPos) { - var indexPos = chunkStart << CHUNK_SHIFT; - var accumulator = reduceChunk(self.get(indexPos), indexPos + 1, indexPos + CHUNK_SIZE); - - UnsafePutElements(subreductions, sliceId, accumulator, // see (*) above - info, SLICE_POS(sliceId), ++chunkPos); - } - - var accumulator = subreductions[sliceId]; // see (*) above - - while (chunkPos < chunkEnd) { - var indexPos = chunkPos << CHUNK_SHIFT; - accumulator = reduceChunk(accumulator, indexPos, indexPos + CHUNK_SIZE); - UnsafePutElements(subreductions, sliceId, accumulator, info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - function reduceChunk(accumulator, from, to) { - to = std_Math_min(to, length); - for (var i = from; i < to; i++) - accumulator = func(accumulator, self.get(i)); - return accumulator; - } - - return undefined; -} - -/** - * |scan()| returns an array [s_0, ..., s_N] where - * |s_i| is equal to the reduction (as per |reduce()|) - * of elements |0..i|. This is the generalization - * of partial sum. - */ -function ParallelArrayScan(func, mode) { - // FIXME(bug 844887): Check |this instanceof ParallelArray| - // FIXME(bug 844887): Check |IsCallable(func)| - - var self = this; - var length = self.shape[0]; - - if (length === 0) - ThrowError(JSMSG_PAR_ARRAY_REDUCE_EMPTY); - - var buffer = NewDenseArray(length); - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - if (chunks < numSlices) - break parallel; - var info = ComputeAllSliceBounds(chunks, numSlices); - - // Scan slices individually (see comment on phase1()). - ForkJoin(phase1, ForkJoinMode(mode)); - - // Compute intermediates array (see comment on phase2()). - var intermediates = []; - var accumulator = buffer[finalElement(0)]; - ARRAY_PUSH(intermediates, accumulator); - for (var i = 1; i < numSlices - 1; i++) { - accumulator = func(accumulator, buffer[finalElement(i)]); - ARRAY_PUSH(intermediates, accumulator); - } - - // Reset the current position information for each slice, but - // convert from chunks to indices (see comment on phase2()). - for (var i = 0; i < numSlices; i++) { - info[SLICE_POS(i)] = info[SLICE_START(i)] << CHUNK_SHIFT; - info[SLICE_END(i)] = info[SLICE_END(i)] << CHUNK_SHIFT; - } - info[SLICE_END(numSlices - 1)] = std_Math_min(info[SLICE_END(numSlices - 1)], length); - - // Complete each slice using intermediates array (see comment on phase2()). - ForkJoin(phase2, ForkJoinMode(mode)); - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - scan(self.get(0), 0, length); - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - - function scan(accumulator, start, end) { - UnsafePutElements(buffer, start, accumulator); - for (var i = start + 1; i < end; i++) { - accumulator = func(accumulator, self.get(i)); - UnsafePutElements(buffer, i, accumulator); - } - return accumulator; - } - - /** - * In phase 1, we divide the source array into |numSlices| slices and - * compute scan on each slice sequentially as if it were the entire - * array. This function is responsible for computing one of those - * slices. - * - * So, if we have an array [A,B,C,D,E,F,G,H,I], |numSlices == 3|, - * and our function |func| is sum, then we would wind up computing a - * result array like: - * - * [A, A+B, A+B+C, D, D+E, D+E+F, G, G+H, G+H+I] - * ^~~~~~~~~~~~^ ^~~~~~~~~~~~^ ^~~~~~~~~~~~~^ - * Slice 0 Slice 1 Slice 2 - * - * Read on in phase2 to see what we do next! - */ - function phase1(sliceId, numSlices, warmup) { - var chunkStart = info[SLICE_START(sliceId)]; - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - if (warmup && chunkEnd > chunkPos + 2) - chunkEnd = chunkPos + 2; - - if (chunkPos == chunkStart) { - // For the first chunk, the accumulator begins as the value in - // the input at the start of the chunk. - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - scan(self.get(indexStart), indexStart, indexEnd); - UnsafePutElements(info, SLICE_POS(sliceId), ++chunkPos); - } - - while (chunkPos < chunkEnd) { - // For each subsequent chunk, the accumulator begins as the - // combination of the final value of prev chunk and the value in - // the input at the start of this chunk. Note that this loop is - // written as simple as possible, at the cost of an extra read - // from the buffer per iteration. - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - var accumulator = func(buffer[indexStart - 1], self.get(indexStart)); - scan(accumulator, indexStart, indexEnd); - UnsafePutElements(info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - /** - * Computes the index of the final element computed by the slice |sliceId|. - */ - function finalElement(sliceId) { - var chunkEnd = info[SLICE_END(sliceId)]; // last chunk written by |sliceId| is endChunk - 1 - var indexStart = std_Math_min(chunkEnd << CHUNK_SHIFT, length); - return indexStart - 1; - } - - /** - * After computing the phase1 results, we compute an - * |intermediates| array. |intermediates[i]| contains the result - * of reducing the final value from each preceding slice j> |length|, Divide-Scatter-Vector seems like - // a clear win over Divide-Output-Range, since for the latter, the - // expense of redundantly scanning the |targets| will diminish the - // gain from processing |length| in parallel, while for the former, - // the total expense of building separate output buffers and the - // merging post-process is small compared to the gain from - // processing |targets| in parallel. - // - // If |targets.length| << |length|, then Divide-Output-Range seems - // like it *could* win over Divide-Scatter-Vector. (But when is - // |targets.length| << |length| or even |targets.length| < |length|? - // Seems like an odd situation and an uncommon case at best.) - // - // The unanswered question is which strategy performs better when - // |targets.length| approximately equals |length|, especially for - // special cases like collision-free scatters and permutations. - - var targetsLength = std_Math_min(targets.length, self.shape[0]); - - if (targetsLength >>> 0 !== targetsLength) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ".prototype.scatter length"); - - if (length >>> 0 !== length) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ".prototype.scatter length"); - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - if (forceDivideScatterVector()) - return parDivideScatterVector(); - else if (forceDivideOutputRange()) - return parDivideOutputRange(); - else if (conflictFunc === undefined && targetsLength < length) - return parDivideOutputRange(); - return parDivideScatterVector(); - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - return seq(); - - function forceDivideScatterVector() { - return mode && mode.strategy && mode.strategy == "divide-scatter-vector"; - } - - function forceDivideOutputRange() { - return mode && mode.strategy && mode.strategy == "divide-output-range"; - } - - function collide(elem1, elem2) { - if (conflictFunc === undefined) - ThrowError(JSMSG_PAR_ARRAY_SCATTER_CONFLICT); - - return conflictFunc(elem1, elem2); - } - - - function parDivideOutputRange() { - var chunks = ComputeNumChunks(targetsLength); - var numSlices = ForkJoinSlices(); - var checkpoints = NewDenseArray(numSlices); - for (var i = 0; i < numSlices; i++) - UnsafePutElements(checkpoints, i, 0); - - var buffer = NewDenseArray(length); - var conflicts = NewDenseArray(length); - - for (var i = 0; i < length; i++) { - UnsafePutElements(buffer, i, defaultValue); - UnsafePutElements(conflicts, i, false); - } - - ForkJoin(fill, ForkJoinMode(mode)); - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - - function fill(sliceId, numSlices, warmup) { - var indexPos = checkpoints[sliceId]; - var indexEnd = targetsLength; - if (warmup) - indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE); - - // Range in the output for which we are responsible: - var [outputStart, outputEnd] = ComputeSliceBounds(length, sliceId, numSlices); - - for (; indexPos < indexEnd; indexPos++) { - var x = self.get(indexPos); - var t = checkTarget(indexPos, targets[indexPos]); - if (t < outputStart || t >= outputEnd) - continue; - if (conflicts[t]) - x = collide(x, buffer[t]); - UnsafePutElements(buffer, t, x, conflicts, t, true, checkpoints, sliceId, indexPos + 1); - } - - return indexEnd == targetsLength; - } - - return undefined; - } - - function parDivideScatterVector() { - // Subtle: because we will be mutating the localBuffers and - // conflict arrays in place, we can never replay an entry in the - // target array for fear of inducing a conflict where none existed - // before. Therefore, we must proceed not by chunks but rather by - // individual indices. - var numSlices = ForkJoinSlices(); - var info = ComputeAllSliceBounds(targetsLength, numSlices); - - // FIXME(bug 844890): Use typed arrays here. - var localBuffers = NewDenseArray(numSlices); - for (var i = 0; i < numSlices; i++) - UnsafePutElements(localBuffers, i, NewDenseArray(length)); - var localConflicts = NewDenseArray(numSlices); - for (var i = 0; i < numSlices; i++) { - var conflicts_i = NewDenseArray(length); - for (var j = 0; j < length; j++) - UnsafePutElements(conflicts_i, j, false); - UnsafePutElements(localConflicts, i, conflicts_i); - } - - // Initialize the 0th buffer, which will become the output. For - // the other buffers, we track which parts have been written to - // using the conflict buffer so they do not need to be - // initialized. - var outputBuffer = localBuffers[0]; - for (var i = 0; i < length; i++) - UnsafePutElements(outputBuffer, i, defaultValue); - - ForkJoin(fill, ForkJoinMode(mode)); - mergeBuffers(); - return NewParallelArray(ParallelArrayView, [length], outputBuffer, 0); - - function fill(sliceId, numSlices, warmup) { - var indexPos = info[SLICE_POS(sliceId)]; - var indexEnd = info[SLICE_END(sliceId)]; - if (warmup) - indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE); - - var localbuffer = localBuffers[sliceId]; - var conflicts = localConflicts[sliceId]; - while (indexPos < indexEnd) { - var x = self.get(indexPos); - var t = checkTarget(indexPos, targets[indexPos]); - if (conflicts[t]) - x = collide(x, localbuffer[t]); - UnsafePutElements(localbuffer, t, x, conflicts, t, true, - info, SLICE_POS(sliceId), ++indexPos); - } - - return indexEnd == info[SLICE_END(sliceId)]; - } - - /** - * Merge buffers 1..NUMSLICES into buffer 0. In principle, we could - * parallelize the merge work as well. But for this first cut, - * just do the merge sequentially. - */ - function mergeBuffers() { - var buffer = localBuffers[0]; - var conflicts = localConflicts[0]; - for (var i = 1; i < numSlices; i++) { - var otherbuffer = localBuffers[i]; - var otherconflicts = localConflicts[i]; - for (var j = 0; j < length; j++) { - if (otherconflicts[j]) { - if (conflicts[j]) { - buffer[j] = collide(otherbuffer[j], buffer[j]); - } else { - buffer[j] = otherbuffer[j]; - conflicts[j] = true; - } - } - } - } - } - - return undefined; - } - - function seq() { - var buffer = NewDenseArray(length); - var conflicts = NewDenseArray(length); - - for (var i = 0; i < length; i++) { - UnsafePutElements(buffer, i, defaultValue); - UnsafePutElements(conflicts, i, false); - } - - for (var i = 0; i < targetsLength; i++) { - var x = self.get(i); - var t = checkTarget(i, targets[i]); - if (conflicts[t]) - x = collide(x, buffer[t]); - - UnsafePutElements(buffer, t, x, conflicts, t, true); - } - - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - } - - function checkTarget(i, t) { - if (TO_INT32(t) !== t) - ThrowError(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, i); - - if (t < 0 || t >= length) - ThrowError(JSMSG_PAR_ARRAY_SCATTER_BOUNDS); - - // It's not enough to return t, as -0 | 0 === -0. - return TO_INT32(t); - } - - return undefined; -} - -/** - * The familiar filter() operation applied across the outermost - * dimension. - */ -function ParallelArrayFilter(func, mode) { - // FIXME(bug 844887): Check |this instanceof ParallelArray| - // FIXME(bug 844887): Check |IsCallable(func)| - - var self = this; - var length = self.shape[0]; - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - if (chunks < numSlices * 2) - break parallel; - - var info = ComputeAllSliceBounds(chunks, numSlices); - - // Step 1. Compute which items from each slice of the result - // buffer should be preserved. When we're done, we have an array - // |survivors| containing a bitset for each chunk, indicating - // which members of the chunk survived. We also keep an array - // |counts| containing the total number of items that are being - // preserved from within one slice. - // - // FIXME(bug 844890): Use typed arrays here. - var counts = NewDenseArray(numSlices); - for (var i = 0; i < numSlices; i++) - UnsafePutElements(counts, i, 0); - var survivors = NewDenseArray(chunks); - ForkJoin(findSurvivorsInSlice, ForkJoinMode(mode)); - - // Step 2. Compress the slices into one contiguous set. - var count = 0; - for (var i = 0; i < numSlices; i++) - count += counts[i]; - var buffer = NewDenseArray(count); - if (count > 0) - ForkJoin(copySurvivorsInSlice, ForkJoinMode(mode)); - - return NewParallelArray(ParallelArrayView, [count], buffer, 0); - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - var buffer = []; - for (var i = 0; i < length; i++) { - var elem = self.get(i); - if (func(elem, i, self)) - ARRAY_PUSH(buffer, elem); - } - return NewParallelArray(ParallelArrayView, [buffer.length], buffer, 0); - - /** - * As described above, our goal is to determine which items we - * will preserve from a given slice. We do this one chunk at a - * time. When we finish a chunk, we record our current count and - * the next chunk sliceId, lest we should bail. - */ - function findSurvivorsInSlice(sliceId, numSlices, warmup) { - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - if (warmup && chunkEnd > chunkPos) - chunkEnd = chunkPos + 1; - - var count = counts[sliceId]; - while (chunkPos < chunkEnd) { - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - var chunkBits = 0; - - for (var bit = 0; indexStart + bit < indexEnd; bit++) { - var keep = !!func(self.get(indexStart + bit), indexStart + bit, self); - chunkBits |= keep << bit; - count += keep; - } - - UnsafePutElements(survivors, chunkPos, chunkBits, - counts, sliceId, count, - info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - function copySurvivorsInSlice(sliceId, numSlices, warmup) { - // Copies the survivors from this slice into the correct position. - // Note that this is an idempotent operation that does not invoke - // user code. Therefore, we don't expect bailouts and make an - // effort to proceed chunk by chunk or avoid duplicating work. - - // Total up the items preserved by previous slices. - var count = 0; - if (sliceId > 0) { // FIXME(#819219)---work around a bug in Ion's range checks - for (var i = 0; i < sliceId; i++) - count += counts[i]; - } - - // Compute the final index we expect to write. - var total = count + counts[sliceId]; - if (count == total) - return true; - - // Iterate over the chunks assigned to us. Read the bitset for - // each chunk. Copy values where a 1 appears until we have - // written all the values that we expect to. We can just iterate - // from 0...CHUNK_SIZE without fear of a truncated final chunk - // because we are already checking for when count==total. - var chunkStart = info[SLICE_START(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - for (var chunk = chunkStart; chunk < chunkEnd; chunk++) { - var chunkBits = survivors[chunk]; - if (!chunkBits) - continue; - - var indexStart = chunk << CHUNK_SHIFT; - for (var i = 0; i < CHUNK_SIZE; i++) { - if (chunkBits & (1 << i)) { - UnsafePutElements(buffer, count++, self.get(indexStart + i)); - if (count == total) - break; - } - } - } - - return true; - } - - return undefined; -} - -/** - * Divides the outermost dimension into two dimensions. Does not copy - * or affect the underlying data, just how it is divided amongst - * dimensions. So if we had a vector with shape [N, ...] and you - * partition with amount=4, you get a [N/4, 4, ...] vector. Note that - * N must be evenly divisible by 4 in that case. - */ -function ParallelArrayPartition(amount) { - if (amount >>> 0 !== amount) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - - var length = this.shape[0]; - var partitions = (length / amount) | 0; - - if (partitions * amount !== length) - ThrowError(JSMSG_PAR_ARRAY_BAD_PARTITION); - - var shape = [partitions, amount]; - for (var i = 1; i < this.shape.length; i++) - ARRAY_PUSH(shape, this.shape[i]); - return NewParallelArray(ParallelArrayView, shape, this.buffer, this.offset); -} - -/** - * Collapses two outermost dimensions into one. So if you had - * a [X, Y, ...] vector, you get a [X*Y, ...] vector. - */ -function ParallelArrayFlatten() { - if (this.shape.length < 2) - ThrowError(JSMSG_PAR_ARRAY_ALREADY_FLAT); - - var shape = [this.shape[0] * this.shape[1]]; - for (var i = 2; i < this.shape.length; i++) - ARRAY_PUSH(shape, this.shape[i]); - return NewParallelArray(ParallelArrayView, shape, this.buffer, this.offset); -} - -// -// Accessors and utilities. -// - -/** - * Specialized variant of get() for one-dimensional case - */ -function ParallelArrayGet1(i) { - if (i === undefined) - return undefined; - return this.buffer[this.offset + i]; -} - -/** - * Specialized variant of get() for two-dimensional case - */ -function ParallelArrayGet2(x, y) { - var xDimension = this.shape[0]; - var yDimension = this.shape[1]; - if (x === undefined) - return undefined; - if (x >= xDimension) - return undefined; - if (y === undefined) - return NewParallelArray(ParallelArrayView, [yDimension], this.buffer, this.offset + x * yDimension); - if (y >= yDimension) - return undefined; - var offset = y + x * yDimension; - return this.buffer[this.offset + offset]; -} - -/** - * Specialized variant of get() for three-dimensional case - */ -function ParallelArrayGet3(x, y, z) { - var xDimension = this.shape[0]; - var yDimension = this.shape[1]; - var zDimension = this.shape[2]; - if (x === undefined) - return undefined; - if (x >= xDimension) - return undefined; - if (y === undefined) - return NewParallelArray(ParallelArrayView, [yDimension, zDimension], - this.buffer, this.offset + x * yDimension * zDimension); - if (y >= yDimension) - return undefined; - if (z === undefined) - return NewParallelArray(ParallelArrayView, [zDimension], - this.buffer, this.offset + y * zDimension + x * yDimension * zDimension); - if (z >= zDimension) - return undefined; - var offset = z + y*zDimension + x * yDimension * zDimension; - return this.buffer[this.offset + offset]; -} - -/** - * Generalized version of get() for N-dimensional case - */ -function ParallelArrayGetN(...coords) { - if (coords.length == 0) - return undefined; - - var products = ComputeProducts(this.shape); - - // Compute the offset of the given coordinates. Each index is - // multipled by its corresponding entry in the |products| - // array, counting in reverse. So if |coords| is [a,b,c,d], - // then you get |a*BCD + b*CD + c*D + d|. - var offset = this.offset; - var sDimensionality = this.shape.length; - var cDimensionality = coords.length; - for (var i = 0; i < cDimensionality; i++) { - if (coords[i] >= this.shape[i]) - return undefined; - offset += coords[i] * products[sDimensionality - i - 1]; - } - - if (cDimensionality < sDimensionality) { - var shape = callFunction(std_Array_slice, this.shape, cDimensionality); - return NewParallelArray(ParallelArrayView, shape, this.buffer, offset); - } - return this.buffer[offset]; -} - -/** The length property yields the outermost dimension */ -function ParallelArrayLength() { - return this.shape[0]; -} - -function ParallelArrayToString() { - var l = this.length; - if (l == 0) - return ""; - - var open, close; - if (this.shape.length > 1) { - open = "<"; - close = ">"; - } else { - open = close = ""; - } - - var result = ""; - for (var i = 0; i < l - 1; i++) { - result += open + String(this.get(i)) + close; - result += ","; - } - result += open + String(this.get(l - 1)) + close; - return result; -} - -/** - * Internal debugging tool: checks that the given `mode` permits - * sequential execution - */ -function AssertSequentialIsOK(mode) { - if (mode && mode.mode && mode.mode !== "seq" && ParallelTestsShouldPass()) - ThrowError(JSMSG_WRONG_VALUE, "parallel execution", "sequential was forced"); -} - -function ForkJoinMode(mode) { - // WARNING: this must match the enum ForkJoinMode in ForkJoin.cpp - if (!mode || !mode.mode) { - return 0; - } else if (mode.mode === "compile") { - return 1; - } else if (mode.mode === "par") { - return 2; - } else if (mode.mode === "recover") { - return 3; - } else if (mode.mode === "bailout") { - return 4; - } - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - return undefined; -} - -/* - * Mark the main operations as clone-at-callsite for better precision. - * This is slightly overkill, as all that we really need is to - * specialize to the receiver and the elemental function, but in - * practice this is likely not so different, since element functions - * are often used in exactly one place. - */ -SetScriptHints(ParallelArrayConstructEmpty, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayConstructFromArray, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayConstructFromFunction, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayConstructFromFunctionMode, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayConstructFromComprehension, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayView, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayBuild, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayMap, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayReduce, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayScan, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayScatter, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayFilter, { cloneAtCallsite: true }); - -/* - * Mark the common getters as clone-at-callsite and inline. This is - * overkill as we should only clone per receiver, but we have no - * mechanism for that right now. Bug 804767 might permit another - * alternative by specializing the inlined gets. - */ -SetScriptHints(ParallelArrayGet1, { cloneAtCallsite: true, inline: true }); -SetScriptHints(ParallelArrayGet2, { cloneAtCallsite: true, inline: true }); -SetScriptHints(ParallelArrayGet3, { cloneAtCallsite: true, inline: true }); diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index 77873cf58acf..8dceb836a257 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -161,3 +161,33 @@ function IsObject(v) { typeof v === "function" || (typeof v === "undefined" && v !== undefined); } + +#ifdef ENABLE_PARALLEL_JS + +/** + * Internal debugging tool: checks that the given `mode` permits + * sequential execution + */ +function AssertSequentialIsOK(mode) { + if (mode && mode.mode && mode.mode !== "seq" && ParallelTestsShouldPass()) + ThrowError(JSMSG_WRONG_VALUE, "parallel execution", "sequential was forced"); +} + +function ForkJoinMode(mode) { + // WARNING: this must match the enum ForkJoinMode in ForkJoin.cpp + if (!mode || !mode.mode) { + return 0; + } else if (mode.mode === "compile") { + return 1; + } else if (mode.mode === "par") { + return 2; + } else if (mode.mode === "recover") { + return 3; + } else if (mode.mode === "bailout") { + return 4; + } + ThrowError(JSMSG_PAR_ARRAY_BAD_ARG); + return undefined; +} + +#endif diff --git a/js/src/jit-test/lib/parallelarray-helpers.js b/js/src/jit-test/lib/parallelarray-helpers.js index a6afc03b07ef..9fdc4cea0b1f 100644 --- a/js/src/jit-test/lib/parallelarray-helpers.js +++ b/js/src/jit-test/lib/parallelarray-helpers.js @@ -79,13 +79,7 @@ function assertAlmostEq(v1, v2) { } function assertStructuralEq(e1, e2) { - if (e1 instanceof ParallelArray && e2 instanceof ParallelArray) { - assertEqParallelArray(e1, e2); - } else if (e1 instanceof Array && e2 instanceof ParallelArray) { - assertEqParallelArrayArray(e2, e1); - } else if (e1 instanceof ParallelArray && e2 instanceof Array) { - assertEqParallelArrayArray(e1, e2); - } else if (e1 instanceof Array && e2 instanceof Array) { + if (e1 instanceof Array && e2 instanceof Array) { assertEqArray(e1, e2); } else if (e1 instanceof Object && e2 instanceof Object) { assertEq(e1.__proto__, e2.__proto__); @@ -100,19 +94,6 @@ function assertStructuralEq(e1, e2) { } } -function assertEqParallelArrayArray(a, b) { - assertEq(a.shape.length, 1); - assertEq(a.length, b.length); - for (var i = 0, l = a.length; i < l; i++) { - try { - assertStructuralEq(a.get(i), b[i]); - } catch (e) { - print("...in index ", i, " of ", l); - throw e; - } - } -} - function assertEqArray(a, b) { assertEq(a.length, b.length); for (var i = 0, l = a.length; i < l; i++) { @@ -125,37 +106,6 @@ function assertEqArray(a, b) { } } -function assertEqParallelArray(a, b) { - assertEq(a instanceof ParallelArray, true); - assertEq(b instanceof ParallelArray, true); - - var shape = a.shape; - assertEqArray(shape, b.shape); - - function bump(indices) { - var d = indices.length - 1; - while (d >= 0) { - if (++indices[d] < shape[d]) - break; - indices[d] = 0; - d--; - } - return d >= 0; - } - - var iv = shape.map(function () { return 0; }); - do { - try { - var e1 = a.get.apply(a, iv); - var e2 = b.get.apply(b, iv); - assertStructuralEq(e1, e2); - } catch (e) { - print("...in indices ", iv, " of ", shape); - throw e; - } - } while (bump(iv)); -} - // Checks that whenever we execute this in parallel mode, // it bails out. `opFunction` should be a closure that takes a // mode parameter and performs some parallel array operation. @@ -164,10 +114,10 @@ function assertEqParallelArray(a, b) { // Here is an example of the expected usage: // // assertParallelExecWillBail(function(m) { -// new ParallelArray(..., m) +// Array.buildPar(..., m) // }); // -// where the `new ParallelArray(...)` is a stand-in +// where the `Array.buildPar(...)` is a stand-in // for some parallel array operation. function assertParallelExecWillBail(opFunction) { opFunction({mode:"compile"}); // get the script compiled @@ -236,30 +186,6 @@ function assertArraySeqParResultsEq(arr, op, func, cmpFunc) { function (r) { cmpFunc(expected, r); }); } -// Compares a ParallelArray function against its equivalent on the -// `Array` prototype. `func` should be the closure to provide as -// argument. For example: -// -// compareAgainstArray([1, 2, 3], "map", i => i + 1) -// -// would check that `[1, 2, 3].map(i => i+1)` and `new -// ParallelArray([1, 2, 3]).map(i => i+1)` yield the same result. -// -// Based on `assertParallelExecSucceeds` -function compareAgainstArray(jsarray, opname, func, cmpFunction) { - if (!cmpFunction) - cmpFunction = assertStructuralEq; - var expected = jsarray[opname].apply(jsarray, [func]); - var parray = new ParallelArray(jsarray); - assertParallelExecSucceeds( - function(m) { - return parray[opname].apply(parray, [func, m]); - }, - function(r) { - cmpFunction(expected, r); - }); -} - // Similar to `compareAgainstArray`, but for the `scan` method which // does not appear on array. function testArrayScanPar(jsarray, func, cmpFunction) { @@ -281,24 +207,6 @@ function testArrayScanPar(jsarray, func, cmpFunction) { }); } -// Similar to `compareAgainstArray`, but for the `scatter` method. -// In this case, because scatter is so complex, we do not attempt -// to compute the expected result and instead simply invoke -// `cmpFunction(r)` with the result `r` of the scatter operation. -function testScatter(opFunction, cmpFunction) { - var strategies = ["divide-scatter-version", "divide-output-range"]; - for (var i in strategies) { - assertParallelExecSucceeds( - function(m) { - var m1 = {mode: m.mode, - strategy: strategies[i]}; - print(JSON.stringify(m1)); - return opFunction(m1); - }, - cmpFunction); - } -} - // Checks that `opFunction`, when run with each of the modes // in `modes`, returns the same value each time. function assertParallelModesCommute(modes, opFunction) { diff --git a/js/src/jit-test/tests/auto-regress/bug755564.js b/js/src/jit-test/tests/auto-regress/bug755564.js deleted file mode 100644 index e692508ce1b1..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug755564.js +++ /dev/null @@ -1,9 +0,0 @@ -// Binary: cache/js-dbg-64-50177d59c0e1-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }); - assertEq(r.toString( 5 ? r : 0, gc()) ,[4,2,9,4,5].join(",")); -} diff --git a/js/src/jit-test/tests/auto-regress/bug783923.js b/js/src/jit-test/tests/auto-regress/bug783923.js deleted file mode 100644 index 661c77b47972..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug783923.js +++ /dev/null @@ -1,6 +0,0 @@ -// Binary: cache/js-dbg-64-35b8d6ef5d46-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) - print(ParallelArray()); diff --git a/js/src/jit-test/tests/auto-regress/bug784011.js b/js/src/jit-test/tests/auto-regress/bug784011.js deleted file mode 100644 index e419fcb9f6c6..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug784011.js +++ /dev/null @@ -1,8 +0,0 @@ -// Binary: cache/js-dbg-64-c676b554c7bb-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - var p2 = new ParallelArray([2,2], function(i,j) { return i+j; }); - p2.get({ 0: 1, 1: 0, testGet: 2 }) -} diff --git a/js/src/jit-test/tests/auto-regress/bug786106.js b/js/src/jit-test/tests/auto-regress/bug786106.js deleted file mode 100644 index ab0f84f18080..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug786106.js +++ /dev/null @@ -1,8 +0,0 @@ -// Binary: cache/js-dbg-64-92b9b2840a79-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - var p = new ParallelArray([2, 3,, 4, 5, 6]); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }); -} diff --git a/js/src/jit-test/tests/auto-regress/bug789107.js b/js/src/jit-test/tests/auto-regress/bug789107.js deleted file mode 100644 index 9998513cd19e..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug789107.js +++ /dev/null @@ -1,7 +0,0 @@ -// Binary: cache/js-dbg-64-5d63594c05a9-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - ParallelArray().watch("shape", function() {}) -} diff --git a/js/src/jit-test/tests/auto-regress/bug791445.js b/js/src/jit-test/tests/auto-regress/bug791445.js deleted file mode 100644 index a7a3a3a10931..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug791445.js +++ /dev/null @@ -1,7 +0,0 @@ -// Binary: cache/js-dbg-64-9fff2012b66c-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - ParallelArray(0, Proxy.createFunction(function(){}, function(){})) -} diff --git a/js/src/jit-test/tests/baseline/bug846072.js b/js/src/jit-test/tests/baseline/bug846072.js deleted file mode 100644 index 368e0f03c6e3..000000000000 --- a/js/src/jit-test/tests/baseline/bug846072.js +++ /dev/null @@ -1,7 +0,0 @@ -// |jit-test| error: TypeError -if (getBuildConfiguration().parallelJS) { - toString = undefined; - if (!(this in ParallelArray)) {} -} else { - throw new TypeError(); -} diff --git a/js/src/jit-test/tests/baseline/bug857579.js b/js/src/jit-test/tests/baseline/bug857579.js deleted file mode 100644 index 117e76c25747..000000000000 --- a/js/src/jit-test/tests/baseline/bug857579.js +++ /dev/null @@ -1,22 +0,0 @@ -// |jit-test| ion-eager; error: TypeError - -function testMonitorIntrinsic() { - var N = 2; - // Make an NxN array of zeros. - var p = new ParallelArray([N,N], function () 0); - // |i| will go out of bounds! - for (var i = 0; i < N+1; i++) { - for (var j = 0; j < 2; j++) { - // When |i| goes out of bounds, we will bail from Ion to BC on an - // 'undefined' result inside parallel array's .get, tripping a type - // barrier on GETINTRINSIC. - p.get(i).get(j); - } - } -} - -if (getBuildConfiguration().parallelJS) { - testMonitorIntrinsic(); -} else { - throw new TypeError(); -} diff --git a/js/src/jit-test/tests/basic/bug815652.js b/js/src/jit-test/tests/basic/bug815652.js deleted file mode 100644 index ad2b9ad86c65..000000000000 --- a/js/src/jit-test/tests/basic/bug815652.js +++ /dev/null @@ -1,12 +0,0 @@ - -gczeal(9, 2) -function testScatterConflict() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,0,3,(0)], 9, function (a,b) { return a+b; }); - function assertEqParallelArray(a, b) - assertEq(a instanceof ParallelArray, true); - assertEqParallelArray(r, new ParallelArray([4,2,(false),4,5])); -} -if (getBuildConfiguration().parallelJS) { - testScatterConflict(); -} diff --git a/js/src/jit-test/tests/parallel/binary-arith-numbers.js b/js/src/jit-test/tests/parallel/binary-arith-numbers.js index 72d3a069f717..cc3a2e5a6397 100644 --- a/js/src/jit-test/tests/parallel/binary-arith-numbers.js +++ b/js/src/jit-test/tests/parallel/binary-arith-numbers.js @@ -18,12 +18,11 @@ function theTest() { jsarray0.map(op); // this version will never actually touch the strings: - var jsarray1 = range(0, 1024).map(i => i % 10); - compareAgainstArray(jsarray1, "map", op); + assertArraySeqParResultsEq(range(0, 1024), "map", function (i) { return i % 10; }); // but if we try against the original we get bailouts: assertParallelExecWillBail(function (mode) { - new ParallelArray(jsarray0).map(op, mode); + jsarray0.mapPar(op, mode); }); } diff --git a/js/src/jit-test/tests/parallel/bug783924.js b/js/src/jit-test/tests/parallel/bug783924.js deleted file mode 100644 index 2ca003d4a0c0..000000000000 --- a/js/src/jit-test/tests/parallel/bug783924.js +++ /dev/null @@ -1,7 +0,0 @@ -function bug783924() { - // Shouldn't throw. - Function("ParallelArray([])")(); -} - -if (getBuildConfiguration().parallelJS) - bug783924(); diff --git a/js/src/jit-test/tests/parallel/bug787282.js b/js/src/jit-test/tests/parallel/bug787282.js deleted file mode 100644 index e8848c618501..000000000000 --- a/js/src/jit-test/tests/parallel/bug787282.js +++ /dev/null @@ -1,16 +0,0 @@ -// |jit-test| error: TypeError - -var protoArr = Proxy.create({}, null); -void (Array.prototype.__proto__ = protoArr); -gczeal(2); -function testCopyBigArray() { - var a = new Array(10000); - for (var cnt = 0; cnt < a.length; cnt+=2) { - var p = new ParallelArray(a); - } -} - -if (getBuildConfiguration().parallelJS) - testCopyBigArray(); -else - throw new TypeError(); diff --git a/js/src/jit-test/tests/parallel/bug853555.js b/js/src/jit-test/tests/parallel/bug853555.js deleted file mode 100644 index 49338c2c3298..000000000000 --- a/js/src/jit-test/tests/parallel/bug853555.js +++ /dev/null @@ -1,12 +0,0 @@ -function test() { - Object.prototype[0] = /a/; - function getterFunction(v) { return "getter"; } - Object.defineProperty(Array.prototype, 1, { get: getterFunction }); - gczeal(4); - var p = new ParallelArray([1,2,3,4,5]); - p.scatter([0,1,0,3,01], 9, function (a,b) { return a+b; }); -} - -if (getBuildConfiguration().parallelJS) - test(); - diff --git a/js/src/jit-test/tests/parallel/bug853573.js b/js/src/jit-test/tests/parallel/bug853573.js deleted file mode 100644 index 8024a4269416..000000000000 --- a/js/src/jit-test/tests/parallel/bug853573.js +++ /dev/null @@ -1,17 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - var p = Proxy.create({ - has : function(id) {} - }); - Object.prototype.__proto__ = p; - var pa0 = new ParallelArray(range(0, 256)); - var pa1 = new ParallelArray(256, function (x) { - return pa0.map(function(y) {}); - }); -} - -function range(n, m) { - var result = []; - for (var i = n; i < m; i++) - result.push(i); - return result; -} diff --git a/js/src/jit-test/tests/parallel/bug854021.js b/js/src/jit-test/tests/parallel/bug854021.js deleted file mode 100644 index 6b73e0bc9600..000000000000 --- a/js/src/jit-test/tests/parallel/bug854021.js +++ /dev/null @@ -1,3 +0,0 @@ -// Don't crash. -if (getBuildConfiguration().parallelJS) - ParallelArray(7, function ([y]) {}) diff --git a/js/src/jit-test/tests/parallel/bug854050.js b/js/src/jit-test/tests/parallel/bug854050.js deleted file mode 100644 index a839b3634b7a..000000000000 --- a/js/src/jit-test/tests/parallel/bug854050.js +++ /dev/null @@ -1,10 +0,0 @@ -function bug854050() { - // Shouldn't crash. Tests Ion's argumentsRectifier loading the right - // IonScript depending on execution mode. - for (z = 0; z < 1; z++) - function x(b, x) {} - ParallelArray(47, x); -} - -if (getBuildConfiguration().parallelJS) - bug854050(); diff --git a/js/src/jit-test/tests/parallel/bug854381.js b/js/src/jit-test/tests/parallel/bug854381.js deleted file mode 100644 index 99c0b1239820..000000000000 --- a/js/src/jit-test/tests/parallel/bug854381.js +++ /dev/null @@ -1,20 +0,0 @@ -function bug854381() { - // Don't crash. - function toString(r) { - var l = 2; - var result = ""; - for (var i = 0; i < l; i++) - result += r.get(i); - return result; - } - - var p = new ParallelArray(['x', 'x']); - var r = new ParallelArray([toString(p), 42]); - - gc(); - print(toString(r)); -} - -if (getBuildConfiguration().parallelJS) { - bug854381(); -} diff --git a/js/src/jit-test/tests/parallel/bug857846.js b/js/src/jit-test/tests/parallel/bug857846.js deleted file mode 100644 index 19ef69e25cd1..000000000000 --- a/js/src/jit-test/tests/parallel/bug857846.js +++ /dev/null @@ -1,9 +0,0 @@ -function testNegativeZeroScatter() { - // Don't crash. - var p = new ParallelArray([0]); - var r = p.scatter([-0], 0, undefined, 1); -} - -if (getBuildConfiguration().parallelJS) { - testNegativeZeroScatter(); -} diff --git a/js/src/jit-test/tests/parallel/bug858077.js b/js/src/jit-test/tests/parallel/bug858077.js deleted file mode 100644 index 4af7e467fb34..000000000000 --- a/js/src/jit-test/tests/parallel/bug858077.js +++ /dev/null @@ -1,5 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - var a = new ParallelArray([1,2,3,4]); - for (var i = 0; i < 9 && i > -1000; i-- ) - a[i] += [0]; -} diff --git a/js/src/jit-test/tests/parallel/bug858582.js b/js/src/jit-test/tests/parallel/bug858582.js deleted file mode 100644 index da3f9df98a32..000000000000 --- a/js/src/jit-test/tests/parallel/bug858582.js +++ /dev/null @@ -1,68 +0,0 @@ -// |jit-test| error: TypeError -// Don't crash. -if (getBuildConfiguration().parallelJS) { -gczeal(2); -evaluate("\ -function assertAlmostEq(v1, v2) {\ - print(\"v2 = \" + v2);\ - print(\"% diff = \" + percent);\ -function assertStructuralEq(e1, e2) {}\ -function assertEqParallelArrayArray(a, b) {\ - try {} catch (e) {\ - print(\"...in index \", i, \" of \", l);\ - }\ -}\ - function assertEqArray(a, b) {\ - try {} catch (e) {}\ -}\ -function assertEqParallelArray(a, b) {\ - var shape = a.shape;\ - function bump(indices) {\ - var iv = shape.map(function () { return 0; });\ - print(\"...in indices \", iv, \" of \", shape);\ - }\ - } while (bump(iv));\ -}\ -function assertParallelArrayModesEq(modes, acc, opFunction, cmpFunction) {\ - modes.forEach(function (mode) {\ - var result = opFunction({ mode: mode, expect: \"success\" });\ - cmpFunction(acc, result);\ - });\ -function assertParallelArrayModesCommute(modes, opFunction) {\ - var acc = opFunction({ mode: modes[0], expect: \"success\" });\ -}\ -function comparePerformance(opts) {\ - print(\"Option \" + opts[i].name + \" took \" + diff + \"ms\");\ - print(\"Option \" + opts[i].name + \" relative to option \" +\ - opts[0].name + \": \" + (rel|0) + \"%\");\ - }\ -}\ -function compareAgainstArray(jsarray, opname, func, cmpFunction) {\ - var expected = jsarray[opname].apply(jsarray, [func]);\ - var parray = new ParallelArray(jsarray);\ - assertParallelArrayModesEq([\"seq\", \"par\", \"par\"], expected, function(m) {\ - var result = parray[opname].apply(parray, [func, m]);\ - }, cmpFunction);\ -}\ -function testFilter(jsarray, func, cmpFunction) {}\ -", { noScriptRval : true }); - compareAgainstArray([ - "a", - "b", - ('captures: 1,1; RegExp.leftContext: ""; RegExp.rightContext: "123456"'), - "d", "e", - "f", "g", "h", - "i", "j", "k", "l", - "m", "n", "o", "p", - "q", "r", "s", "t", - (.6 ), "v", "w", "x", "y", "z" - ], "map", function(e) { - return e != "u" - && - (function b ( ) { - } ) - != "x"; - }); -} else { - throw new TypeError(); -} diff --git a/js/src/jit-test/tests/parallel/bug890465.js b/js/src/jit-test/tests/parallel/bug890465.js deleted file mode 100644 index e47a157c0102..000000000000 --- a/js/src/jit-test/tests/parallel/bug890465.js +++ /dev/null @@ -1,6 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - x = Uint8ClampedArray() - ParallelArray([320], function() { - return x[8] - }) -} diff --git a/js/src/jit-test/tests/parallel/bug894782.js b/js/src/jit-test/tests/parallel/bug894782.js deleted file mode 100644 index f25875b28320..000000000000 --- a/js/src/jit-test/tests/parallel/bug894782.js +++ /dev/null @@ -1,6 +0,0 @@ -// Don't crash - -if (getBuildConfiguration().parallelJS) { - print(ParallelArray()) - String(Object.create(ParallelArray(8077, function() {}))) -} diff --git a/js/src/jit-test/tests/parallel/bug895782.js b/js/src/jit-test/tests/parallel/bug895782.js deleted file mode 100644 index f8538fa6a918..000000000000 --- a/js/src/jit-test/tests/parallel/bug895782.js +++ /dev/null @@ -1,12 +0,0 @@ -// Don't crash - -if (getBuildConfiguration().parallelJS) { - Object.defineProperty(this, "y", { - get: function() { - return Object.create(x) - } - }) - x = ParallelArray([1142], function() {}) - x = x.partition(2) - y + y -} diff --git a/js/src/jit-test/tests/parallel/bug908939.js b/js/src/jit-test/tests/parallel/bug908939.js deleted file mode 100644 index d70a27858ad7..000000000000 --- a/js/src/jit-test/tests/parallel/bug908939.js +++ /dev/null @@ -1,13 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - x = ParallelArray([9937], function() { - return /x/ - }) - Object.defineProperty(this, "y", { - get: function() { - return Object.create(x) - } - }) - y + x - x = x.scatter([]); - + y -} diff --git a/js/src/jit-test/tests/parallel/bug909599.js b/js/src/jit-test/tests/parallel/bug909599.js deleted file mode 100644 index 1dee7caaf61c..000000000000 --- a/js/src/jit-test/tests/parallel/bug909599.js +++ /dev/null @@ -1,6 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - x = Math.tan(5) + [] - ParallelArray([764], (function() {})).filter(function(e) { - return e << x - }) -} diff --git a/js/src/jit-test/tests/parallel/compare-values.js b/js/src/jit-test/tests/parallel/compare-values.js index 398df363b37a..0da90f4fd5e0 100644 --- a/js/src/jit-test/tests/parallel/compare-values.js +++ b/js/src/jit-test/tests/parallel/compare-values.js @@ -15,29 +15,29 @@ function theTest() { return doubles[i] == e; } print("doubles"); - compareAgainstArray(doubles, "map", looselyCompareToDoubles) + assertArraySeqParResultsEq(doubles, "map", looselyCompareToDoubles); print("bools"); - compareAgainstArray(bools, "map", looselyCompareToDoubles) + assertArraySeqParResultsEq(bools, "map", looselyCompareToDoubles); // ion bails out when converting a string to a double right now, // so par exec cannot proceed print("strings"); assertParallelExecWillBail(function (mode) { - new ParallelArray(strings).map(looselyCompareToDoubles, mode) + strings.mapPar(looselyCompareToDoubles, mode) }); print("ints"); - compareAgainstArray(ints, "map", looselyCompareToDoubles) + assertArraySeqParResultsEq(ints, "map", looselyCompareToDoubles); function strictlyCompareToDoubles(e, i) { return doubles[i] === e; } print("doubles, strict"); - compareAgainstArray(doubles, "map", strictlyCompareToDoubles) + assertArraySeqParResultsEq(doubles, "map", strictlyCompareToDoubles); print("bools, strict"); - compareAgainstArray(bools, "map", strictlyCompareToDoubles) + assertArraySeqParResultsEq(bools, "map", strictlyCompareToDoubles); print("strings, strict"); - compareAgainstArray(strings, "map", strictlyCompareToDoubles) + assertArraySeqParResultsEq(strings, "map", strictlyCompareToDoubles); print("ints, strict"); - compareAgainstArray(ints, "map", strictlyCompareToDoubles) + assertArraySeqParResultsEq(ints, "map", strictlyCompareToDoubles); } if (getBuildConfiguration().parallelJS) diff --git a/js/src/jit-test/tests/parallel/comprehension-2.js b/js/src/jit-test/tests/parallel/comprehension-2.js deleted file mode 100644 index 4c61faffb271..000000000000 --- a/js/src/jit-test/tests/parallel/comprehension-2.js +++ /dev/null @@ -1,13 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); -load(libdir + "eqArrayHelper.js"); - -function buildMultidim() { - // 2D comprehension - var p = new ParallelArray([2,2], function (i,j) { return i + j; }); - var a = new ParallelArray([0,1,1,2]).partition(2); - assertEqArray(p.shape, [2,2]); - assertEqParallelArray(p, a); -} - -if (getBuildConfiguration().parallelJS) - buildMultidim(); diff --git a/js/src/jit-test/tests/parallel/comprehension-fn-args.js b/js/src/jit-test/tests/parallel/comprehension-fn-args.js deleted file mode 100644 index bb2004d6f46d..000000000000 --- a/js/src/jit-test/tests/parallel/comprehension-fn-args.js +++ /dev/null @@ -1,16 +0,0 @@ - -function buildComprehension() { - // Test kernel function arguments - var shape = []; - for (var i = 0; i < 8; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { - assertEq(arguments.length, shape.length); - for (var j = 0; j < shape.length; j++) - assertEq(arguments[j] >= 0 && arguments[j] < shape[j], true); - }); - } -} - -if (getBuildConfiguration().parallelJS) - buildComprehension(); diff --git a/js/src/jit-test/tests/parallel/comprehension-scale.js b/js/src/jit-test/tests/parallel/comprehension-scale.js deleted file mode 100644 index d10d01ea8552..000000000000 --- a/js/src/jit-test/tests/parallel/comprehension-scale.js +++ /dev/null @@ -1,22 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function buildComprehension() { - var H = 96; - var W = 96; - var d = 4; - // 3D 96x96x4 texture-like PA - var p = new ParallelArray([H,W,d], function (i,j,k) { return i + j + k; }); - var a = []; - for (var i = 0; i < H; i++) { - for (var j = 0; j < W; j++) { - for (var k = 0; k < d; k++) { - a.push(i+j+k); - } - } - } - var p2 = new ParallelArray(a).partition(d).partition(W); - assertEqParallelArray(p, p2); -} - -if (getBuildConfiguration().parallelJS) - buildComprehension(); diff --git a/js/src/jit-test/tests/parallel/constructor-1.js b/js/src/jit-test/tests/parallel/constructor-1.js deleted file mode 100644 index d5d5160344ad..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-1.js +++ /dev/null @@ -1,15 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function buildSimple() { - // Simple constructor - var a = [1,2,3,4,5]; - var p = new ParallelArray(a); - assertEqParallelArrayArray(p, a); - var a2 = a.slice(); - a[0] = 9; - // No sharing - assertEqParallelArrayArray(p, a2); -} - -if (getBuildConfiguration().parallelJS) - buildSimple(); diff --git a/js/src/jit-test/tests/parallel/constructor-2.js b/js/src/jit-test/tests/parallel/constructor-2.js deleted file mode 100644 index 38a69b51f2cf..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-2.js +++ /dev/null @@ -1,15 +0,0 @@ - -function buildWithHoles() { - // Test holes - var a = new Array(5); - for (var cnt = 0; cnt < a.length; cnt+=2) { - a[cnt] = cnt; - } - var b = [0,1,2,3,4]; - var p = new ParallelArray(a); - assertEq(Object.keys(p).join(","), Object.keys(b).join(",")); -} - -// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties -// if (getBuildConfiguration().parallelJS) -// buildWithHoles(); diff --git a/js/src/jit-test/tests/parallel/constructor-3.js b/js/src/jit-test/tests/parallel/constructor-3.js deleted file mode 100644 index 6fc501650a36..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-3.js +++ /dev/null @@ -1,14 +0,0 @@ -function bracket(s) { - return "<" + s + ">"; -} - -function buildArrayLike() { - // Construct copying from array-like - var a = { 0: 1, 1: 2, 2: 3, 3: 4, length: 4 }; - var p = new ParallelArray(a); - var e = Array.prototype.join.call(a, ","); - assertEq(p.toString(), e); -} - -if (getBuildConfiguration().parallelJS) - buildArrayLike(); diff --git a/js/src/jit-test/tests/parallel/constructor-4.js b/js/src/jit-test/tests/parallel/constructor-4.js deleted file mode 100644 index 7b435c2eb9df..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-4.js +++ /dev/null @@ -1,16 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function buildPA() { - // Construct copying from PA - var p1 = new ParallelArray([1,2,3,4]); - var p2 = new ParallelArray(p1); - assertEqParallelArray(p1, p2); - - var p1d = new ParallelArray([2,2], function(i,j) { return i + j; }); - var p2d = new ParallelArray(p1d); - assertEq(p1d.toString(), p2d.toString()); -} - -// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties -// if (getBuildConfiguration().parallelJS) -// buildPA(); diff --git a/js/src/jit-test/tests/parallel/constructor-5.js b/js/src/jit-test/tests/parallel/constructor-5.js deleted file mode 100644 index 929c80fdabb4..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-5.js +++ /dev/null @@ -1,18 +0,0 @@ -// |jit-test| slow; -// ^^ This test is slow when --no-ion is used, specifically, -// as part of TBPL. - -function testCopyBigArray() { - // Don't crash - var a = new Array(1000 * 1000); - var p = new ParallelArray(a); - // Holes! - var a = new Array(10000); - for (var cnt = 0; cnt < a.length; cnt+=2) { - a[cnt] = cnt; - var p = new ParallelArray(a); - } -} - -if (getBuildConfiguration().parallelJS) - testCopyBigArray(); diff --git a/js/src/jit-test/tests/parallel/constructor-throws.js b/js/src/jit-test/tests/parallel/constructor-throws.js deleted file mode 100644 index 20e760a8c0d8..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-throws.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "asserts.js"); - -function testThrows() { - assertThrowsInstanceOf(function () { - new ParallelArray({ length: 0xffffffff + 1 }); - }, RangeError); -} - -if (getBuildConfiguration().parallelJS) - testThrows(); diff --git a/js/src/jit-test/tests/parallel/element-1.js b/js/src/jit-test/tests/parallel/element-1.js deleted file mode 100644 index a26843aed7fc..000000000000 --- a/js/src/jit-test/tests/parallel/element-1.js +++ /dev/null @@ -1,14 +0,0 @@ -function testElement() { - // Test getting element from 1D - var a = [1,{},"a",false] - var p = new ParallelArray(a); - for (var i = 0; i < a.length; i++) { - assertEq(p.get(i), p.get(i)); - assertEq(p.get(i), a[i]); - } - // Test out of bounds - assertEq(p.get(42), undefined); -} - -if (getBuildConfiguration().parallelJS) - testElement(); diff --git a/js/src/jit-test/tests/parallel/element-2.js b/js/src/jit-test/tests/parallel/element-2.js deleted file mode 100644 index bb23b95f42eb..000000000000 --- a/js/src/jit-test/tests/parallel/element-2.js +++ /dev/null @@ -1,26 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testElement() { - // Test getting element from higher dimension - var p = new ParallelArray([2,2,2], function () { return 0; }); - var p0 = new ParallelArray([2,2], function () { return 0; }); - print("0"); - assertEqParallelArray(p.get(0), p0); - - // Should create new wrapper - print("1"); - assertEq(p.get(0) !== p.get(0), true); - - // Test out of bounds - print("2"); - assertEq(p.get(42), undefined); - - // Test getting element from 0-lengthed higher dimension - var pp = new ParallelArray([0,0], function() { return 0; }); - assertEq(pp.get(2), undefined); - var pp2 = new ParallelArray([2,0], function() { return 0; }); - assertEqParallelArray(pp2.get(0), new ParallelArray()); -} - -if (getBuildConfiguration().parallelJS) - testElement(); diff --git a/js/src/jit-test/tests/parallel/element-3.js b/js/src/jit-test/tests/parallel/element-3.js deleted file mode 100644 index 7872d9d001c8..000000000000 --- a/js/src/jit-test/tests/parallel/element-3.js +++ /dev/null @@ -1,12 +0,0 @@ -function testElement() { - var p = new ParallelArray([9]); - var desc = Object.getOwnPropertyDescriptor(p, "0"); - assertEq(desc.enumerable, true); - assertEq(desc.configurable, false); - assertEq(desc.writable, false); - assertEq(desc.value, 9); -} - -// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties -// if (getBuildConfiguration().parallelJS) -// testElement(); diff --git a/js/src/jit-test/tests/parallel/flatten-1.js b/js/src/jit-test/tests/parallel/flatten-1.js deleted file mode 100644 index d7d68923e802..000000000000 --- a/js/src/jit-test/tests/parallel/flatten-1.js +++ /dev/null @@ -1,14 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testFlatten() { - var shape = [5]; - for (var i = 0; i < 7; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function(i,j) { return i+j; }); - var flatShape = ([shape[0] * shape[1]]).concat(shape.slice(2)); - assertEqArray(p.flatten().shape, flatShape); - } -} - -if (getBuildConfiguration().parallelJS) - testFlatten(); diff --git a/js/src/jit-test/tests/parallel/flatten-2.js b/js/src/jit-test/tests/parallel/flatten-2.js deleted file mode 100644 index 7a7249b15e68..000000000000 --- a/js/src/jit-test/tests/parallel/flatten-2.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testFlatten() { - var p = new ParallelArray([2,2], function(i,j) { return i+j; }); - var p2 = new ParallelArray([0,1,1,2]); - assertEqParallelArray(p.flatten(), p2); -} - -if (getBuildConfiguration().parallelJS) - testFlatten(); diff --git a/js/src/jit-test/tests/parallel/flatten-3.js b/js/src/jit-test/tests/parallel/flatten-3.js deleted file mode 100644 index 82967567c12b..000000000000 --- a/js/src/jit-test/tests/parallel/flatten-3.js +++ /dev/null @@ -1,25 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testFlatten() { - var p0 = new ParallelArray([0,1]); - var p1 = new ParallelArray([2,3]); - var p = new ParallelArray([p0, p1]); - var p2 = new ParallelArray([0,1,2,3]); - assertEqParallelArray(p.flatten(), p2); - - // Test flatten crossing packed boundary with non-shape uniform elements - var blah = new ParallelArray([2,2], function() { return 0; }); - var pp = new ParallelArray([p0, p1, blah]); - var p2 = new ParallelArray([0,1,2,3,blah[0],blah[1]]); - assertEqParallelArray(pp.flatten(), p2); - - var p0 = new ParallelArray([2,2], function() { return 1; }); - var p1 = new ParallelArray([2,2], function() { return 2; }); - var p = new ParallelArray([p0, p1]); - var p2 = new ParallelArray([p0[0],p0[1],p1[0],p1[1]]); - assertEqParallelArray(p.flatten(), p2); -} - -// FIXME(bug 844991) logical shape not implemented -// if (getBuildConfiguration().parallelJS) -// testFlatten(); diff --git a/js/src/jit-test/tests/parallel/flatten-throws.js b/js/src/jit-test/tests/parallel/flatten-throws.js deleted file mode 100644 index 47fd1ce3fc5f..000000000000 --- a/js/src/jit-test/tests/parallel/flatten-throws.js +++ /dev/null @@ -1,12 +0,0 @@ -// |jit-test| error: Error; - -function testFlattenFlat() { - // Throw on flattening flat array - var p = new ParallelArray([1]); - var f = p.flatten(); -} - -if (getBuildConfiguration().parallelJS) - testFlattenFlat(); -else - throw new Error(); diff --git a/js/src/jit-test/tests/parallel/get-1.js b/js/src/jit-test/tests/parallel/get-1.js deleted file mode 100644 index 5a0c5cfc5146..000000000000 --- a/js/src/jit-test/tests/parallel/get-1.js +++ /dev/null @@ -1,9 +0,0 @@ -function testGet() { - var a = [1,2,3,4,5]; - var p = new ParallelArray(a); - for (var i = 0; i < a.length; i++) - assertEq(p.get(i), a[i]); -} - -if (getBuildConfiguration().parallelJS) - testGet(); diff --git a/js/src/jit-test/tests/parallel/get-2.js b/js/src/jit-test/tests/parallel/get-2.js deleted file mode 100644 index 2601f4e1d967..000000000000 --- a/js/src/jit-test/tests/parallel/get-2.js +++ /dev/null @@ -1,14 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testGet() { - var p = new ParallelArray([2,2,2], function(i,j,k) { return i+j+k; }); - assertEq(p.get(1,1,1), 1+1+1); - var p2 = new ParallelArray([2], function(i) { return 1+1+i; }); - assertEqParallelArray(p.get(1,1), p2); - var p3 = new ParallelArray([2,2], function(i,j) { return 1+i+j; }); - assertEqParallelArray(p.get(1), p3); - assertEq(p.get(5,5), undefined); -} - -if (getBuildConfiguration().parallelJS) - testGet(); diff --git a/js/src/jit-test/tests/parallel/get-3.js b/js/src/jit-test/tests/parallel/get-3.js deleted file mode 100644 index f548db7fed80..000000000000 --- a/js/src/jit-test/tests/parallel/get-3.js +++ /dev/null @@ -1,9 +0,0 @@ -function testGetNoCraziness() { - // .get shouldn't do prototype walks - ParallelArray.prototype[42] = "foo"; - var p = new ParallelArray([1,2,3,4]); - assertEq(p.get(42), undefined); -} - -if (getBuildConfiguration().parallelJS) - testGetNoCraziness(); diff --git a/js/src/jit-test/tests/parallel/get-4.js b/js/src/jit-test/tests/parallel/get-4.js deleted file mode 100644 index 96568978e4bf..000000000000 --- a/js/src/jit-test/tests/parallel/get-4.js +++ /dev/null @@ -1,7 +0,0 @@ -function testGetBounds() { - var p = new ParallelArray([1,2,3,4]); - assertEq(p.get(42), undefined); -} - -if (getBuildConfiguration().parallelJS) - testGetBounds(); diff --git a/js/src/jit-test/tests/parallel/get-6.js b/js/src/jit-test/tests/parallel/get-6.js deleted file mode 100644 index 8d652193612c..000000000000 --- a/js/src/jit-test/tests/parallel/get-6.js +++ /dev/null @@ -1,14 +0,0 @@ -function testGet() { - // Test getting higher dimension inferred shape - var p0 = new ParallelArray([0,1]); - var p1 = new ParallelArray([2,3]); - var p = new ParallelArray([p0, p1]); - assertEq(p.get(0,0), 0); - assertEq(p.get(0,1), 1); - assertEq(p.get(1,0), 2); - assertEq(p.get(1,1), 3); -} - -// FIXME(bug 844991) logical shape not implemented -// if (getBuildConfiguration().parallelJS) -// testGet(); diff --git a/js/src/jit-test/tests/parallel/holes-1.js b/js/src/jit-test/tests/parallel/holes-1.js deleted file mode 100644 index d6b21e71ea9b..000000000000 --- a/js/src/jit-test/tests/parallel/holes-1.js +++ /dev/null @@ -1,31 +0,0 @@ -function testHoles() { - function f1(a) { return a * 42; } - function f2(a,b) { return a * b; } - // Don't crash when getting holes out. - // - // We do the multiplications below instead of asserting against undefined to - // force the VM to call a conversion function, which will crash if the value - // we got out is a JS_ARRAY_HOLE. - var p = new ParallelArray([,1]); - assertEq(p.get(0) * 42, NaN); - var m = p.map(f1); - assertEq(m.get(0), NaN); - assertEq(m.get(1), 42); - var r = p.reduce(f2); - assertEq(r, NaN); - var s = p.scan(f2); - assertEq(s.get(0) * 42, NaN); - assertEq(s.get(1), NaN); - var k = p.scatter([1,0]); - assertEq(k.get(0), 1); - assertEq(k[1] * 42, NaN); - var l = p.filter(function (e, i) { return i == 0; }); - assertEq(l.get(0) * 42, NaN); - var p2 = p.partition(1); - assertEq(p2.get(0).get(0) * 42, NaN); - var g = p.get(0); - assertEq(g * 42, NaN); -} - -if (getBuildConfiguration().parallelJS) - testHoles(); diff --git a/js/src/jit-test/tests/parallel/holes-2.js b/js/src/jit-test/tests/parallel/holes-2.js deleted file mode 100644 index 161a0001e948..000000000000 --- a/js/src/jit-test/tests/parallel/holes-2.js +++ /dev/null @@ -1,25 +0,0 @@ -function testElement() { - // No crazy prototype walking for indexed properties - ParallelArray.prototype[42] = "foo"; - ParallelArray.prototype.bar = "bar"; - var p = new ParallelArray([1,2,3,4]); - assertEq(p[42], undefined); - assertEq(42 in p, false); - assertEq("bar" in p, true); - // Don't inherit any indexed properties - for (var i in p) - assertEq(i !== 42, true); - for (var i in p) { - if (i % 1 !== 0) - assertEq(i, "bar"); - } - ParallelArray.prototype[0] = "foo"; - var p2 = new ParallelArray([,2]); - // ParallelArrays have no holes, so 0 must be 'in' p2 - assertEq(0 in p2, true); - assertEq(p2[0], undefined); -} - -// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties -// if (getBuildConfiguration().parallelJS) -// testElement(); diff --git a/js/src/jit-test/tests/parallel/index-1.js b/js/src/jit-test/tests/parallel/index-1.js deleted file mode 100644 index c264c7b68dff..000000000000 --- a/js/src/jit-test/tests/parallel/index-1.js +++ /dev/null @@ -1,9 +0,0 @@ -function test() { - var N = 22; - var p = new ParallelArray([N], function(i) { return i; }); - for (var i = 0; i < N; i++) - assertEq(p.get(i), i); -} - -if (getBuildConfiguration().parallelJS) - test(); diff --git a/js/src/jit-test/tests/parallel/index-2.js b/js/src/jit-test/tests/parallel/index-2.js deleted file mode 100644 index 385f07157353..000000000000 --- a/js/src/jit-test/tests/parallel/index-2.js +++ /dev/null @@ -1,18 +0,0 @@ -function test() { - function mk(i, j) { return i*100+j; } - - var N = 22; - var M = 44; - - var p = new ParallelArray([N,M], mk); - for (var i = 0; i < N; i++) { - for (var j = 0; j < M; j++) { - print([i, j]); - assertEq(p.get(i, j), mk(i, j)); - assertEq(p.get(i).get(j), mk(i, j)); - } - } -} - -if (getBuildConfiguration().parallelJS) - test(); diff --git a/js/src/jit-test/tests/parallel/index-3.js b/js/src/jit-test/tests/parallel/index-3.js deleted file mode 100644 index 9606c24a3831..000000000000 --- a/js/src/jit-test/tests/parallel/index-3.js +++ /dev/null @@ -1,25 +0,0 @@ -function test() { - function mk(i, j, k) { return i*10000+j*100+k; } - - var N = 10; - var M = 20; - var O = 30; - - print("Computing"); - var p = new ParallelArray([N,M,O], mk); - - print("Checking"); - for (var i = 0; i < N; i++) { - for (var j = 0; j < M; j++) { - for (var k = 0; k < O; k++) { - assertEq(p.get(i, j, k), mk(i, j, k)); - assertEq(p.get(i, j).get(k), mk(i, j, k)); - assertEq(p.get(i).get(j).get(k), mk(i, j, k)); - assertEq(p.get(i).get(j, k), mk(i, j, k)); - } - } - } -} - -if (getBuildConfiguration().parallelJS) - test(); diff --git a/js/src/jit-test/tests/parallel/index-4.js b/js/src/jit-test/tests/parallel/index-4.js deleted file mode 100644 index 6a1696483139..000000000000 --- a/js/src/jit-test/tests/parallel/index-4.js +++ /dev/null @@ -1,27 +0,0 @@ -function test() { - function mk(i, j, k, l) { return i*1000000+j*10000+k*100+l; } - - var N = 2; - var M = 4; - var O = 6; - var P = 8; - - print("Computing"); - var p = new ParallelArray([N,M,O,P], mk); - - print("Checking"); - for (var i = 0; i < N; i++) { - for (var j = 0; j < M; j++) { - for (var k = 0; k < O; k++) { - for (var l = 0; l < P; l++) { - assertEq(p.get(i, j, k, l), mk(i, j, k, l)); - assertEq(p.get(i, j).get(k, l), mk(i, j, k, l)); - assertEq(p.get(i).get(j).get(k).get(l), mk(i, j, k, l)); - } - } - } - } -} - -if (getBuildConfiguration().parallelJS) - test(); diff --git a/js/src/jit-test/tests/parallel/inline-new-bad-type.js b/js/src/jit-test/tests/parallel/inline-new-bad-type.js deleted file mode 100644 index df9d1cbc62f7..000000000000 --- a/js/src/jit-test/tests/parallel/inline-new-bad-type.js +++ /dev/null @@ -1,18 +0,0 @@ -// |jit-test| error: RangeError -// -// Run with --ion-eager. -if (getBuildConfiguration().parallelJS) { - function TestCase(n, d, e, a) {}; - function reportCompare() { - var testcase = new TestCase("x", 0); - } - reportCompare(); - TestCase = ParallelArray; - gczeal(6); - try { - reportCompare(); - } catch(exc1) {} - reportCompare(); -} else { - throw new RangeError(); -} diff --git a/js/src/jit-test/tests/parallel/length-1.js b/js/src/jit-test/tests/parallel/length-1.js deleted file mode 100644 index 20d0656b808f..000000000000 --- a/js/src/jit-test/tests/parallel/length-1.js +++ /dev/null @@ -1,12 +0,0 @@ -function testLength() { - var a = [1,2,3]; - var p = new ParallelArray(a); - assertEq(p.length, a.length); - // Test holes - var a = [1,,3]; - var p = new ParallelArray(a); - assertEq(p.length, a.length); -} - -if (getBuildConfiguration().parallelJS) - testLength(); diff --git a/js/src/jit-test/tests/parallel/length-2.js b/js/src/jit-test/tests/parallel/length-2.js deleted file mode 100644 index 8e569abaebc0..000000000000 --- a/js/src/jit-test/tests/parallel/length-2.js +++ /dev/null @@ -1,13 +0,0 @@ -function testLength() { - // Test higher dimension shape up to 8D - var shape = []; - for (var i = 0; i < 8; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { return 0; }); - // Length should be outermost dimension - assertEq(p.length, shape[0]); - } -} - -if (getBuildConfiguration().parallelJS) - testLength(); diff --git a/js/src/jit-test/tests/parallel/length-3.js b/js/src/jit-test/tests/parallel/length-3.js deleted file mode 100644 index cb9d9ce519dc..000000000000 --- a/js/src/jit-test/tests/parallel/length-3.js +++ /dev/null @@ -1,11 +0,0 @@ -function testLength() { - // Test length immutability. - var p = new ParallelArray([1,2,3,4]); - p.length = 0; - assertEq(p[0], 1); - assertEq(p.length, 4); -} - -// FIXME(bug 844988) immutability not enforced -// if (getBuildConfiguration().parallelJS) -// testLength(); diff --git a/js/src/jit-test/tests/parallel/mandelbrot.js b/js/src/jit-test/tests/parallel/mandelbrot.js index 04306bb3dad3..8764399dfb88 100644 --- a/js/src/jit-test/tests/parallel/mandelbrot.js +++ b/js/src/jit-test/tests/parallel/mandelbrot.js @@ -46,6 +46,8 @@ var cols = 4; if (getBuildConfiguration().parallelJS) { var expected = computeSequentially(); assertParallelExecSucceeds( - function (m) new ParallelArray([rows, cols], computeSetByRow, m).flatten(), + function (m) Array.buildPar(rows * cols, function (xy) { + return computeSetByRow((xy/cols)|0,(xy%cols)) + }, m), function (r) assertStructuralEq(expected, r)); } diff --git a/js/src/jit-test/tests/parallel/map-3.js b/js/src/jit-test/tests/parallel/map-3.js deleted file mode 100644 index f92817fb7706..000000000000 --- a/js/src/jit-test/tests/parallel/map-3.js +++ /dev/null @@ -1,12 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testMap() { - // Test mapping higher dimensional - var p = new ParallelArray([2,2], function (i,j) { return i+j; }); - var m = p.map(function(x) { return x; }); - var p2 = new ParallelArray(p.shape[0], function (i) { return p.get(i); }); - assertEqParallelArray(m, p2); -} - -if (getBuildConfiguration().parallelJS) testMap(); - diff --git a/js/src/jit-test/tests/parallel/overflow-throws.js b/js/src/jit-test/tests/parallel/overflow-throws.js deleted file mode 100644 index 60f5e4264951..000000000000 --- a/js/src/jit-test/tests/parallel/overflow-throws.js +++ /dev/null @@ -1,12 +0,0 @@ -load(libdir + "asserts.js"); - -function testOverflow() { - assertThrowsInstanceOf(function () { - new ParallelArray([0xffffffff + 1], function() { return 0; }); - }, RangeError); - assertThrowsInstanceOf(function () { - new ParallelArray({ length: 0xffffffff + 1 }); - }, RangeError); -} - -if (getBuildConfiguration().parallelJS) testOverflow(); diff --git a/js/src/jit-test/tests/parallel/partition-1.js b/js/src/jit-test/tests/parallel/partition-1.js deleted file mode 100644 index 56b5a64d3af9..000000000000 --- a/js/src/jit-test/tests/parallel/partition-1.js +++ /dev/null @@ -1,15 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testPartition() { - var p = new ParallelArray([1,2,3,4,5,6,7,8]); - var pp = p.partition(2); - var ppp = pp.partition(2); - var ppShape = [p.shape[0] / 2, 2].concat(p.shape.slice(1)); - var pppShape = [pp.shape[0] / 2, 2].concat(pp.shape.slice(1)); - assertEqArray(pp.shape, ppShape); - assertEq(pp.toString(), "<1,2>,<3,4>,<5,6>,<7,8>"); - assertEqArray(ppp.shape, pppShape); - assertEq(ppp.toString(), "<<1,2>,<3,4>>,<<5,6>,<7,8>>"); -} - -if (getBuildConfiguration().parallelJS) testPartition(); diff --git a/js/src/jit-test/tests/parallel/partition-throws.js b/js/src/jit-test/tests/parallel/partition-throws.js deleted file mode 100644 index 7126b3a39002..000000000000 --- a/js/src/jit-test/tests/parallel/partition-throws.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "asserts.js"); - -function testPartitionDivisible() { - var p = new ParallelArray([1,2,3,4]); - var pp; - assertThrowsInstanceOf(function () { pp = p.partition(3); }, Error); - assertThrowsInstanceOf(function () { pp = p.partition(.34); }, Error); -} - -if (getBuildConfiguration().parallelJS) testPartitionDivisible(); diff --git a/js/src/jit-test/tests/parallel/reduce-higher-dim.js b/js/src/jit-test/tests/parallel/reduce-higher-dim.js deleted file mode 100644 index efe8d60fbab2..000000000000 --- a/js/src/jit-test/tests/parallel/reduce-higher-dim.js +++ /dev/null @@ -1,14 +0,0 @@ - -function testReduce() { - // Test reduce on higher dimensional - // XXX Can we test this better? - var shape = [2]; - for (var i = 0; i < 7; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { return i+1; }); - var r = p.reduce(function (a, b) { return a; }); - assertEq(r.shape.length, i + 1); - } -} - -if (getBuildConfiguration().parallelJS) testReduce(); diff --git a/js/src/jit-test/tests/parallel/reduce-throws.js b/js/src/jit-test/tests/parallel/reduce-throws.js deleted file mode 100644 index bc2b45546281..000000000000 --- a/js/src/jit-test/tests/parallel/reduce-throws.js +++ /dev/null @@ -1,17 +0,0 @@ -load(libdir + "asserts.js"); - -function testReduceThrows() { - // Throw on empty - assertThrowsInstanceOf(function () { - var p = new ParallelArray([]); - p.reduce(function (v, p) { return v*p; }); - }, Error); - // Throw on not function - assertThrowsInstanceOf(function () { - var p = new ParallelArray([1]); - p.reduce(42); - }, TypeError); -} - -// FIXME(bug 844886) sanity check argument types -// if (getBuildConfiguration().parallelJS) testReduceThrows(); diff --git a/js/src/jit-test/tests/parallel/scan-3.js b/js/src/jit-test/tests/parallel/scan-3.js deleted file mode 100644 index 9d2f812c97f4..000000000000 --- a/js/src/jit-test/tests/parallel/scan-3.js +++ /dev/null @@ -1,17 +0,0 @@ -function testScan() { - // Test reduce on higher dimensional - // XXX Can we test this better? - function f(a, b) { return a; } - var shape = [2]; - for (var i = 0; i < 7; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { return i+1; }); - var r = p.reduce(f); - var s = p.scan(f) - for (var j = 0; j < s.length; j++) - assertEq(s.get(j).shape.length, i + 1); - assertEq(r.shape.length, i + 1); - } -} - -if (getBuildConfiguration().parallelJS) testScan(); diff --git a/js/src/jit-test/tests/parallel/scan-throws.js b/js/src/jit-test/tests/parallel/scan-throws.js deleted file mode 100644 index 4cdcceb095b5..000000000000 --- a/js/src/jit-test/tests/parallel/scan-throws.js +++ /dev/null @@ -1,18 +0,0 @@ -load(libdir + "asserts.js"); - -function testScanThrows() { - // Throw on empty - assertThrowsInstanceOf(function () { - var p = new ParallelArray([]); - p.scan(function (v, p) { return v*p; }); - }, Error); - - // Throw on not function - assertThrowsInstanceOf(function () { - var p = new ParallelArray([1]); - p.scan(42); - }, TypeError); -} - -// FIXME(bug 844886) sanity check argument types -// if (getBuildConfiguration().parallelJS) testScanThrows(); diff --git a/js/src/jit-test/tests/parallel/scatter-1.js b/js/src/jit-test/tests/parallel/scatter-1.js deleted file mode 100644 index 69e963982a06..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-1.js +++ /dev/null @@ -1,8 +0,0 @@ - -function testScatter() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, 10); - assertEq(r.length, 10); -} - -if (getBuildConfiguration().parallelJS) testScatter(); diff --git a/js/src/jit-test/tests/parallel/scatter-10.js b/js/src/jit-test/tests/parallel/scatter-10.js deleted file mode 100644 index 01f3bacbec9c..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-10.js +++ /dev/null @@ -1,21 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// Test specific scatter implementation strategies, and compare them -// each against the sequential version. -// -// This is just a simple reverse permutation of the input: -// [A, B, ..., Y, Z] ==> [Z, Y, ..., B, A] - -function testDivideScatterVector() { - var len = 1024; - function add1(x) { return x+1; } - function id(x) { return x; } - var p = new ParallelArray(len, add1); - var revidx = build(len, id).reverse(); - var p2 = new ParallelArray(revidx.map(add1)); - testScatter( - m => p.scatter(revidx, 0, undefined, len, m), - r => assertEqParallelArray(r, p2)); -} - -if (getBuildConfiguration().parallelJS) testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallel/scatter-11.js b/js/src/jit-test/tests/parallel/scatter-11.js deleted file mode 100644 index e7b443bdd735..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-11.js +++ /dev/null @@ -1,21 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// Test specific scatter implementation strategies, and compare them -// each against the sequential version. -// -// This is a reverse permutation that has a gap at the end. -// [A, B, ..., Y, Z] ==> [Z, Y, ..., B, A, 0] - -function testDivideScatterVector() { - var len = 1024; - function add1(x) { return x+1; } - function id(x) { return x; } - var p = new ParallelArray(len, add1); - var revidx = build(len, id).reverse(); - var p2 = new ParallelArray(revidx.map(add1).concat([0])); - testScatter( - m => p.scatter(revidx, 0, undefined, len+1, m), - r => assertEqParallelArray(r, p2)); -} - -if (getBuildConfiguration().parallelJS) testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallel/scatter-12.js b/js/src/jit-test/tests/parallel/scatter-12.js deleted file mode 100644 index 1cf9bd90e7d4..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-12.js +++ /dev/null @@ -1,22 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// Test specific scatter implementation strategies, and compare them -// each against the sequential version. -// -// This is a reverse permutation that has a gap at the start and at the end. -// [A, B, ..., Y, Z] ==> [0, Z, Y, ..., B, A, 0] - -function testDivideScatterVector() { - var len = 1024; - function add1(x) { return x+1; } - function id(x) { return x; } - var p = new ParallelArray(len, add1); - var revidx = build(len, add1).reverse(); - var p2 = new ParallelArray([0].concat(revidx).concat([0])); - - testScatter( - m => p.scatter(revidx, 0, undefined, len+2, m), - r => assertEqParallelArray(r, p2)); -} - -if (getBuildConfiguration().parallelJS) testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallel/scatter-13.js b/js/src/jit-test/tests/parallel/scatter-13.js deleted file mode 100644 index 20d0c19c2d9d..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-13.js +++ /dev/null @@ -1,24 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// Test specific scatter implementation strategies, and compare them -// each against the sequential version. -// -// This is a reverse permutation of the input with collisions at front and end -// [A, B, C, D, ..., W, X, Y, Z] ==> [Z+Y, X, W, ..., D, C, B+A] - -function testDivideScatterVector() { - var len = 1024; - function add1(x) { return x+1; } - function add3(x) { return x+3; } - function id(x) { return x; } - var p = new ParallelArray(len, add1); - var idx = [0,0].concat(build(len-4, add1)).concat([len-3,len-3]); - var revidx = idx.reverse(); - var p2 = [3].concat(build(len-4, add3)).concat([2*len-1]); - var expect = new ParallelArray(p2.reverse()); - testScatter( - m => p.scatter(revidx, 0, function (x,y) { return x+y; }, len-2, m), - r => assertEqParallelArray(r, expect)); -} - -if (getBuildConfiguration().parallelJS) testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallel/scatter-2.js b/js/src/jit-test/tests/parallel/scatter-2.js deleted file mode 100644 index 07cfb5006a04..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-2.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatterIdentity() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,2,3,4]); - assertEqParallelArray(p, r); -} - -if (getBuildConfiguration().parallelJS) testScatterIdentity(); - diff --git a/js/src/jit-test/tests/parallel/scatter-3.js b/js/src/jit-test/tests/parallel/scatter-3.js deleted file mode 100644 index 06a4a22ce8de..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-3.js +++ /dev/null @@ -1,12 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatter3() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([1,0,3,2,4]); - var p2 = new ParallelArray([2,1,4,3,5]); - assertStructuralEq(r, p2); -} - -if (getBuildConfiguration().parallelJS) - testScatter3(); - diff --git a/js/src/jit-test/tests/parallel/scatter-4.js b/js/src/jit-test/tests/parallel/scatter-4.js deleted file mode 100644 index 6180397b92a1..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-4.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatterDefault() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,2,4], 9); - assertEqParallelArray(r, new ParallelArray([1,9,2,9,3])); -} - -if (getBuildConfiguration().parallelJS) testScatterDefault(); - diff --git a/js/src/jit-test/tests/parallel/scatter-5.js b/js/src/jit-test/tests/parallel/scatter-5.js deleted file mode 100644 index ccb88684f45b..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-5.js +++ /dev/null @@ -1,9 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatterConflict() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }); - assertEqParallelArray(r, new ParallelArray([4,2,9,4,5])); -} - -if (getBuildConfiguration().parallelJS) testScatterConflict(); diff --git a/js/src/jit-test/tests/parallel/scatter-6.js b/js/src/jit-test/tests/parallel/scatter-6.js deleted file mode 100644 index 35996d791f36..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-6.js +++ /dev/null @@ -1,13 +0,0 @@ - -function testScatter() { - // Test scatter on higher dimension - var shape = [5]; - for (var i = 0; i < 7; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function(k) { return k; }); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, 10); - assertEq(r.length, 10); - } -} - -if (getBuildConfiguration().parallelJS) testScatter(); diff --git a/js/src/jit-test/tests/parallel/scatter-7.js b/js/src/jit-test/tests/parallel/scatter-7.js deleted file mode 100644 index f266fe2e2d24..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-7.js +++ /dev/null @@ -1,15 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatterIdentity() { - var shape = [5]; - for (var i = 0; i < 7; i++) { - shape.push(2); - var p = new ParallelArray(shape, function(k) { return k; }); - var r = p.scatter([0,1,2,3,4]); - var p2 = new ParallelArray([p.get(0), p.get(1), p.get(2), p.get(3), p.get(4)]); - assertEqParallelArray(p2, r); - } -} - -if (getBuildConfiguration().parallelJS) testScatterIdentity(); - diff --git a/js/src/jit-test/tests/parallel/scatter-8.js b/js/src/jit-test/tests/parallel/scatter-8.js deleted file mode 100644 index d735df6af2c3..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-8.js +++ /dev/null @@ -1,16 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatter8() { - var shape = [5]; - for (var i = 0; i < 7; i++) { - shape.push(2); - var p = new ParallelArray(shape, function(k) { return k; }); - var r = p.scatter([1,0,3,2,4]); - var p2 = new ParallelArray([p.get(1), p.get(0), p.get(3), p.get(2), p.get(4)]); - assertEqParallelArray(p2, r); - } -} - -if (getBuildConfiguration().parallelJS) - testScatter8(); - diff --git a/js/src/jit-test/tests/parallel/scatter-9.js b/js/src/jit-test/tests/parallel/scatter-9.js deleted file mode 100644 index 22be34e50056..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-9.js +++ /dev/null @@ -1,12 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatter9() { - // Ignore the rest of the scatter vector if longer than source - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([1,0,3,2,4,1,2,3]); - var p2 = new ParallelArray([2,1,4,3,5]); - assertEqParallelArray(r, p2); -} - -if (getBuildConfiguration().parallelJS) - testScatter9(); diff --git a/js/src/jit-test/tests/parallel/scatter-regression-1.js b/js/src/jit-test/tests/parallel/scatter-regression-1.js deleted file mode 100644 index 4a6fc5173a20..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-regression-1.js +++ /dev/null @@ -1,18 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// The bug this is testing for: -// the input p and the scatter vector have length 4 -// and the output p2 has length 3. Even though p2 -// is shorter than the input lengths, we still need -// to scan the entirety of the scatter vector, because -// it may hold valid targets at distant indices. -function test() { - var p = new ParallelArray([2,3,5,17]); - var r = p.scatter([0,0,2,1], 9, function (x,y) { return x * y; }, 3); - var p2 = new ParallelArray([6,17,5]); - assertEqParallelArray(r, p2); -} - -if (getBuildConfiguration().parallelJS) - test(); - diff --git a/js/src/jit-test/tests/parallel/scatter-throws.js b/js/src/jit-test/tests/parallel/scatter-throws.js deleted file mode 100644 index 61c37b9a0f24..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-throws.js +++ /dev/null @@ -1,31 +0,0 @@ -load(libdir + "asserts.js"); - -function testScatterThrows() { - var p = new ParallelArray([1,2,3,4,5]); - - // Throw on conflict with no resolution function - assertThrowsInstanceOf(function () { - var r = p.scatter([0,1,0,3,4]); - }, Error); - // Throw on out of bounds - assertThrowsInstanceOf(function () { - var r = p.scatter([0,1,0,3,11]); - }, Error); - - assertThrowsInstanceOf(function () { - p.scatter([-1,1,0,3,4], 9, function (a,b) { return a+b; }, 10); - }, TypeError); - assertThrowsInstanceOf(function () { - p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, -1); - }, RangeError); - assertThrowsInstanceOf(function () { - p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, 0xffffffff + 1); - }, RangeError); - assertThrowsInstanceOf(function () { - p.scatter({ length: 0xffffffff + 1 }, 9, function (a,b) { return a+b; }, 10); - }, RangeError); - -} - -// FIXME(bug 844886) sanity check argument types -// if (getBuildConfiguration().parallelJS) testScatterThrows(); diff --git a/js/src/jit-test/tests/parallel/shape-1.js b/js/src/jit-test/tests/parallel/shape-1.js deleted file mode 100644 index bb455da812b1..000000000000 --- a/js/src/jit-test/tests/parallel/shape-1.js +++ /dev/null @@ -1,8 +0,0 @@ -function testShape() { - var a = [1,2,3]; - var p = new ParallelArray(a); - assertEq(p.shape.length, 1); - assertEq(p.shape[0], a.length); -} - -if (getBuildConfiguration().parallelJS) testShape(); diff --git a/js/src/jit-test/tests/parallel/shape-2.js b/js/src/jit-test/tests/parallel/shape-2.js deleted file mode 100644 index 8ab7efada68b..000000000000 --- a/js/src/jit-test/tests/parallel/shape-2.js +++ /dev/null @@ -1,15 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testShape() { - // Test higher dimension shape up to 8D - var shape = []; - for (var i = 0; i < 8; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { return 0; }); - // Test shape identity and shape - assertEqArray(p.shape, shape); - assertEq(p.shape !== shape, true); - } -} - -if (getBuildConfiguration().parallelJS) testShape(); diff --git a/js/src/jit-test/tests/parallel/shape-3.js b/js/src/jit-test/tests/parallel/shape-3.js deleted file mode 100644 index e8af4033b67d..000000000000 --- a/js/src/jit-test/tests/parallel/shape-3.js +++ /dev/null @@ -1,16 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testInfer() { - // Test that shapes are inferred - var p0 = new ParallelArray([0,1]); - var p1 = new ParallelArray([2,3]); - var p = new ParallelArray([p0, p1]); - assertEqArray(p.shape, [2,2]); - var p0 = new ParallelArray([0,1]); - var p1 = new ParallelArray([2]); - var p = new ParallelArray([p0, p1]); - assertEqArray(p.shape, [2]); -} - -// FIXME(bug 844991) logical shape not implemented -// if (parallelEnabled) testInfer(); diff --git a/js/src/jit-test/tests/parallel/shape-4.js b/js/src/jit-test/tests/parallel/shape-4.js deleted file mode 100644 index ddd47bb9c12f..000000000000 --- a/js/src/jit-test/tests/parallel/shape-4.js +++ /dev/null @@ -1,13 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testShape() { - // Test shape immutability. - var p = new ParallelArray([1,2,3,4]); - p.shape[0] = 0; - p.shape[1] = 42; - assertEq(p[0], 1); - assertEqArray(p.shape, [4]); -} - -// FIXME(bug 844988) immutability not enforced -// if (getBuildConfiguration().parallelJS) testShape(); diff --git a/js/src/jit-test/tests/parallel/shape-5.js b/js/src/jit-test/tests/parallel/shape-5.js deleted file mode 100644 index ddd47bb9c12f..000000000000 --- a/js/src/jit-test/tests/parallel/shape-5.js +++ /dev/null @@ -1,13 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testShape() { - // Test shape immutability. - var p = new ParallelArray([1,2,3,4]); - p.shape[0] = 0; - p.shape[1] = 42; - assertEq(p[0], 1); - assertEqArray(p.shape, [4]); -} - -// FIXME(bug 844988) immutability not enforced -// if (getBuildConfiguration().parallelJS) testShape(); diff --git a/js/src/jit-test/tests/parallel/stack-overflow.js b/js/src/jit-test/tests/parallel/stack-overflow.js index 8993ff5703d9..c62f1de071c7 100644 --- a/js/src/jit-test/tests/parallel/stack-overflow.js +++ b/js/src/jit-test/tests/parallel/stack-overflow.js @@ -9,9 +9,9 @@ function kernel(n) { } function testMap() { - var p = new ParallelArray(range(0, 2048)); + var p = range(0, 2048); assertParallelExecWillBail( - m => p.map(kernel, m)); + m => p.mapPar(kernel, m)); } if (getBuildConfiguration().parallelJS) diff --git a/js/src/jit-test/tests/parallel/toString-1.js b/js/src/jit-test/tests/parallel/toString-1.js deleted file mode 100644 index 96514373e399..000000000000 --- a/js/src/jit-test/tests/parallel/toString-1.js +++ /dev/null @@ -1,6 +0,0 @@ -function testToString() { - var p = new ParallelArray(); - assertEq(p.toString(), ""); -} - -if (getBuildConfiguration().parallelJS) testToString(); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index dc8b1231cd60..e12f63ccfe7c 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -7868,19 +7868,6 @@ GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc, return true; } - if (native == intrinsic_NewParallelArray || native == ParallelArrayObject::construct) { - res.set(ParallelArrayObject::newInstance(cx, TenuredObject)); - if (!res) - return false; - - types::TypeObject *type = - types::TypeScript::InitObject(cx, script, pc, JSProto_ParallelArray); - if (!type) - return false; - res->setType(type); - return true; - } - return true; } diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 7cbe48d16495..b9c2d6bd1525 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2937,50 +2937,6 @@ CodeGenerator::generateBody() return true; } -// Out-of-line object allocation for LNewParallelArray. -class OutOfLineNewParallelArray : public OutOfLineCodeBase -{ - LNewParallelArray *lir_; - - public: - OutOfLineNewParallelArray(LNewParallelArray *lir) - : lir_(lir) - { } - - bool accept(CodeGenerator *codegen) { - return codegen->visitOutOfLineNewParallelArray(this); - } - - LNewParallelArray *lir() const { - return lir_; - } -}; - -typedef JSObject *(*NewInitParallelArrayFn)(JSContext *, HandleObject); -static const VMFunction NewInitParallelArrayInfo = - FunctionInfo(NewInitParallelArray); - -bool -CodeGenerator::visitNewParallelArrayVMCall(LNewParallelArray *lir) -{ - JS_ASSERT(gen->info().executionMode() == SequentialExecution); - - Register objReg = ToRegister(lir->output()); - - JS_ASSERT(!lir->isCall()); - saveLive(lir); - - pushArg(ImmGCPtr(lir->mir()->templateObject())); - if (!callVM(NewInitParallelArrayInfo, lir)) - return false; - - if (ReturnReg != objReg) - masm.movePtr(ReturnReg, objReg); - - restoreLive(lir); - return true; -} - // Out-of-line object allocation for LNewArray. class OutOfLineNewArray : public OutOfLineCodeBase { @@ -3104,32 +3060,6 @@ bool CodeGenerator::visitHypot(LHypot *lir) return true; } -bool -CodeGenerator::visitNewParallelArray(LNewParallelArray *lir) -{ - Register objReg = ToRegister(lir->output()); - JSObject *templateObject = lir->mir()->templateObject(); - - OutOfLineNewParallelArray *ool = new(alloc()) OutOfLineNewParallelArray(lir); - if (!addOutOfLineCode(ool)) - return false; - - masm.newGCThing(objReg, templateObject, ool->entry(), gc::DefaultHeap); - masm.initGCThing(objReg, templateObject); - - masm.bind(ool->rejoin()); - return true; -} - -bool -CodeGenerator::visitOutOfLineNewParallelArray(OutOfLineNewParallelArray *ool) -{ - if (!visitNewParallelArrayVMCall(ool->lir())) - return false; - masm.jump(ool->rejoin()); - return true; -} - bool CodeGenerator::visitNewArray(LNewArray *lir) { diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index e522f27da234..7f264c981c8c 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -25,7 +25,6 @@ namespace js { namespace jit { -class OutOfLineNewParallelArray; class OutOfLineTestObject; class OutOfLineNewArray; class OutOfLineNewObject; @@ -127,9 +126,6 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitDoubleToInt32(LDoubleToInt32 *lir); bool visitFloat32ToInt32(LFloat32ToInt32 *lir); bool visitNewSlots(LNewSlots *lir); - bool visitNewParallelArrayVMCall(LNewParallelArray *lir); - bool visitNewParallelArray(LNewParallelArray *lir); - bool visitOutOfLineNewParallelArray(OutOfLineNewParallelArray *ool); bool visitNewArrayCallVM(LNewArray *lir); bool visitNewArray(LNewArray *lir); bool visitOutOfLineNewArray(OutOfLineNewArray *ool); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 2404e147d2df..b3094b74e461 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -645,16 +645,6 @@ class IonBuilder : public MIRGenerator InliningStatus inlineUnsafeSetReservedSlot(CallInfo &callInfo); InliningStatus inlineUnsafeGetReservedSlot(CallInfo &callInfo); - // Parallel intrinsics. - InliningStatus inlineNewParallelArray(CallInfo &callInfo); - InliningStatus inlineParallelArray(CallInfo &callInfo); - InliningStatus inlineParallelArrayTail(CallInfo &callInfo, - JSFunction *target, - MDefinition *ctor, - types::TemporaryTypeSet *ctorTypes, - uint32_t discards, - Native native); - // Utility intrinsics. InliningStatus inlineIsCallable(CallInfo &callInfo); InliningStatus inlineHaveSameClass(CallInfo &callInfo); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 13c803502d6c..8f94028624a1 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -302,16 +302,6 @@ class LNewSlots : public LCallInstructionHelper<1, 0, 3> } }; -class LNewParallelArray : public LInstructionHelper<1, 0, 0> -{ - public: - LIR_HEADER(NewParallelArray); - - MNewParallelArray *mir() const { - return mir_->toNewParallelArray(); - } -}; - class LNewArray : public LInstructionHelper<1, 0, 0> { public: diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 44d9db360964..9e731b593006 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -22,7 +22,6 @@ _(TableSwitch) \ _(TableSwitchV) \ _(Goto) \ - _(NewParallelArray) \ _(NewArray) \ _(NewObject) \ _(NewSlots) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 92ceb8941332..54b3a02c0a76 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -160,13 +160,6 @@ LIRGenerator::visitNewSlots(MNewSlots *ins) return defineReturn(lir, ins); } -bool -LIRGenerator::visitNewParallelArray(MNewParallelArray *ins) -{ - LNewParallelArray *lir = new(alloc()) LNewParallelArray(); - return define(lir, ins) && assignSafepoint(lir, ins); -} - bool LIRGenerator::visitNewArray(MNewArray *ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 8302b9f16d68..9e772343fd30 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -81,7 +81,6 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitGoto(MGoto *ins); bool visitTableSwitch(MTableSwitch *tableswitch); bool visitNewSlots(MNewSlots *ins); - bool visitNewParallelArray(MNewParallelArray *ins); bool visitNewArray(MNewArray *ins); bool visitNewObject(MNewObject *ins); bool visitNewDeclEnvObject(MNewDeclEnvObject *ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 6292fc1ab510..4b010bb50650 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -6,7 +6,6 @@ #include "jsmath.h" -#include "builtin/ParallelArray.h" #include "builtin/TestingFunctions.h" #include "jit/BaselineInspector.h" #include "jit/IonBuilder.h" @@ -137,10 +136,6 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) // Parallel intrinsics. if (native == intrinsic_ShouldForceSequential) return inlineForceSequentialOrInParallelSection(callInfo); - if (native == intrinsic_NewParallelArray) - return inlineNewParallelArray(callInfo); - if (native == ParallelArrayObject::construct) - return inlineParallelArray(callInfo); // Utility intrinsics. if (native == intrinsic_IsCallable) @@ -1244,153 +1239,6 @@ IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo) MOZ_ASSUME_UNREACHABLE("Invalid execution mode"); } -IonBuilder::InliningStatus -IonBuilder::inlineNewParallelArray(CallInfo &callInfo) -{ - // Rewrites a call like - // - // NewParallelArray(ParallelArrayView, arg0, ..., argN) - // - // to - // - // x = MNewParallelArray() - // ParallelArrayView(x, arg0, ..., argN) - - uint32_t argc = callInfo.argc(); - if (argc < 1 || callInfo.constructing()) - return InliningStatus_NotInlined; - - types::TemporaryTypeSet *ctorTypes = callInfo.getArg(0)->resultTypeSet(); - JSObject *targetObj = ctorTypes ? ctorTypes->getSingleton() : nullptr; - JSFunction *target = nullptr; - if (targetObj && targetObj->is()) - target = &targetObj->as(); - if (target && target->isInterpreted() && target->nonLazyScript()->shouldCloneAtCallsite()) { - if (JSFunction *clone = ExistingCloneFunctionAtCallsite(compartment->callsiteClones(), target, script(), pc)) - target = clone; - } - MDefinition *ctor = makeCallsiteClone( - target, - callInfo.getArg(0)->toPassArg()->getArgument()); - - // Discard the function. - return inlineParallelArrayTail(callInfo, target, ctor, - target ? nullptr : ctorTypes, 1, - intrinsic_NewParallelArray); -} - -IonBuilder::InliningStatus -IonBuilder::inlineParallelArray(CallInfo &callInfo) -{ - if (!callInfo.constructing()) - return InliningStatus_NotInlined; - - uint32_t argc = callInfo.argc(); - JSFunction *target = ParallelArrayObject::maybeGetConstructor(&script()->global(), argc); - if (!target) - return InliningStatus_NotInlined; - - JS_ASSERT(target->nonLazyScript()->shouldCloneAtCallsite()); - if (JSFunction *clone = ExistingCloneFunctionAtCallsite(compartment->callsiteClones(), target, script(), pc)) - target = clone; - - MConstant *ctor = MConstant::New(alloc(), ObjectValue(*target)); - current->add(ctor); - - return inlineParallelArrayTail(callInfo, target, ctor, nullptr, 0, - ParallelArrayObject::construct); -} - -IonBuilder::InliningStatus -IonBuilder::inlineParallelArrayTail(CallInfo &callInfo, - JSFunction *target, - MDefinition *ctor, - types::TemporaryTypeSet *ctorTypes, - uint32_t discards, - Native native) -{ - // Rewrites either NewParallelArray(...) or new ParallelArray(...) from a - // call to a native ctor into a call to the relevant function in the - // self-hosted code. - - uint32_t argc = callInfo.argc() - discards; - - // Create the new parallel array object. Parallel arrays have specially - // constructed type objects, so we can only perform the inlining if we - // already have one of these type objects. - types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); - if (returnTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) - return InliningStatus_NotInlined; - if (returnTypes->unknownObject() || returnTypes->getObjectCount() != 1) - return InliningStatus_NotInlined; - types::TypeObject *typeObject = returnTypes->getTypeObject(0); - if (!typeObject || typeObject->clasp != &ParallelArrayObject::class_) - return InliningStatus_NotInlined; - - JSObject *templateObject = inspector->getTemplateObjectForNative(pc, native); - if (!templateObject || templateObject->type() != typeObject) - return InliningStatus_NotInlined; - - // Create the call and add in the non-this arguments. - uint32_t targetArgs = argc; - if (target && !target->isNative()) - targetArgs = Max(target->nargs, argc); - - MCall *call = MCall::New(alloc(), target, targetArgs + 1, argc, false); - if (!call) - return InliningStatus_Error; - - callInfo.unwrapArgs(); - - // Explicitly pad any missing arguments with |undefined|. - // This permits skipping the argumentsRectifier. - for (uint32_t i = targetArgs; i > argc; i--) { - JS_ASSERT_IF(target, !target->isNative()); - MConstant *undef = MConstant::New(alloc(), UndefinedValue()); - current->add(undef); - MPassArg *pass = MPassArg::New(alloc(), undef); - current->add(pass); - call->addArg(i, pass); - } - - MPassArg *oldThis = MPassArg::New(alloc(), callInfo.thisArg()); - current->add(oldThis); - - // Add explicit arguments. - // Skip addArg(0) because it is reserved for this - for (uint32_t i = 0; i < argc; i++) { - MDefinition *arg = callInfo.getArg(i + discards); - MPassArg *passArg = MPassArg::New(alloc(), arg); - current->add(passArg); - call->addArg(i + 1, passArg); - } - - // Place an MPrepareCall before the first passed argument, before we - // potentially perform rearrangement. - MPrepareCall *start = MPrepareCall::New(alloc()); - oldThis->block()->insertBefore(oldThis, start); - call->initPrepareCall(start); - - // Create the MIR to allocate the new parallel array. Take the type - // object is taken from the prediction set. - MNewParallelArray *newObject = MNewParallelArray::New(alloc(), templateObject); - current->add(newObject); - MPassArg *newThis = MPassArg::New(alloc(), newObject); - current->add(newThis); - call->addArg(0, newThis); - - // Set the new callee. - call->initFunction(ctor); - - current->add(call); - current->push(newObject); - - if (!resumeAfter(call)) - return InliningStatus_Error; - - return InliningStatus_Inlined; -} - IonBuilder::InliningStatus IonBuilder::inlineNewDenseArray(CallInfo &callInfo) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 23713460447c..97207eed77b7 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1363,32 +1363,6 @@ class MThrow } }; -class MNewParallelArray : public MNullaryInstruction -{ - CompilerRootObject templateObject_; - - MNewParallelArray(JSObject *templateObject) - : templateObject_(templateObject) - { - setResultType(MIRType_Object); - } - - public: - INSTRUCTION_HEADER(NewParallelArray); - - static MNewParallelArray *New(TempAllocator &alloc, JSObject *templateObject) { - return new(alloc) MNewParallelArray(templateObject); - } - - AliasSet getAliasSet() const { - return AliasSet::None(); - } - - JSObject *templateObject() const { - return templateObject_; - } -}; - // Fabricate a type set containing only the type of the specified object. types::TemporaryTypeSet * MakeSingletonTypeSet(JSObject *obj); diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 12cee893cefa..e2d348dc6a28 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -87,7 +87,6 @@ namespace jit { _(TruncateToInt32) \ _(ToString) \ _(NewSlots) \ - _(NewParallelArray) \ _(NewArray) \ _(NewObject) \ _(NewDeclEnvObject) \ diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index 446e0f159f6c..40494356146c 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -184,7 +184,6 @@ class ParallelSafetyVisitor : public MInstructionVisitor CUSTOM_OP(NewArray) CUSTOM_OP(NewObject) CUSTOM_OP(NewCallObject) - CUSTOM_OP(NewParallelArray) UNSAFE_OP(NewDerivedTypedObject) UNSAFE_OP(InitElem) UNSAFE_OP(InitElemGetterSetter) @@ -517,12 +516,6 @@ ParallelSafetyVisitor::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins) return replaceWithNewPar(ins, ins->templateObject()); } -bool -ParallelSafetyVisitor::visitNewParallelArray(MNewParallelArray *ins) -{ - return replaceWithNewPar(ins, ins->templateObject()); -} - bool ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins) { diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 7e76e8857473..8c8ad1c9c753 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -6,7 +6,6 @@ #include "jit/VMFunctions.h" -#include "builtin/ParallelArray.h" #include "builtin/TypedObject.h" #include "frontend/BytecodeCompiler.h" #include "jit/BaselineIC.h" @@ -277,21 +276,6 @@ IteratorMore(JSContext *cx, HandleObject obj, bool *res) return true; } -JSObject * -NewInitParallelArray(JSContext *cx, HandleObject templateObject) -{ - JS_ASSERT(templateObject->getClass() == &ParallelArrayObject::class_); - JS_ASSERT(!templateObject->hasSingletonType()); - - RootedObject obj(cx, ParallelArrayObject::newInstance(cx, TenuredObject)); - if (!obj) - return nullptr; - - obj->setType(templateObject->type()); - - return obj; -} - JSObject* NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *typeArg) { diff --git a/js/src/js.msg b/js/src/js.msg index 5a8344163bab..072f28a8a3e6 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -354,10 +354,10 @@ MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "paramet MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 301, 0, JSEXN_SYNTAXERR, "yield in default expression") MSG_DEF(JSMSG_INTRINSIC_NOT_DEFINED, 302, 1, JSEXN_REFERENCEERR, "no intrinsic function {0}") MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 303, 2, JSEXN_ERR, "{0} is being assigned a {1}, but already has one") -MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 1, JSEXN_RANGEERR, "invalid ParallelArray{0} argument") -MSG_DEF(JSMSG_PAR_ARRAY_BAD_PARTITION, 305, 0, JSEXN_ERR, "argument must be divisible by outermost dimension") -MSG_DEF(JSMSG_PAR_ARRAY_REDUCE_EMPTY, 306, 0, JSEXN_ERR, "cannot reduce ParallelArray object whose outermost dimension is empty") -MSG_DEF(JSMSG_PAR_ARRAY_ALREADY_FLAT, 307, 0, JSEXN_ERR, "cannot flatten 1-dimensional ParallelArray object") +MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 0, JSEXN_RANGEERR, "invalid parallel method argument") +MSG_DEF(JSMSG_UNUSED305, 305, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_UNUSED306, 306, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_UNUSED307, 307, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds") MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 310, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 38996d6f9ae1..882c65b08b8e 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -52,7 +52,6 @@ #include "builtin/Eval.h" #include "builtin/Intl.h" #include "builtin/MapObject.h" -#include "builtin/ParallelArray.h" #include "builtin/RegExp.h" #include "builtin/TypedObject.h" #include "frontend/BytecodeCompiler.h" diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index c4e07abd3271..0166b368d659 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -15,7 +15,6 @@ #include "jsanalyze.h" -#include "builtin/ParallelArray.h" #include "jit/ExecutionModeInlines.h" #include "vm/ArrayObject.h" #include "vm/BooleanObject.h" @@ -354,9 +353,6 @@ GetClassForProtoKey(JSProtoKey key) case JSProto_DataView: return &DataViewObject::class_; - case JSProto_ParallelArray: - return &ParallelArrayObject::class_; - default: MOZ_ASSUME_UNREACHABLE("Bad proto key"); } diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index bc4a767033de..447e080ca5fa 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -91,10 +91,9 @@ real(Map, 33, js_InitMapClass, OCLASP(Map)) \ real(Set, 34, js_InitSetClass, OCLASP(Set)) \ real(DataView, 35, js_InitTypedArrayClasses, OCLASP(DataView)) \ -IF_PJS(real,imaginary) (ParallelArray, 36, js_InitParallelArrayClass, OCLASP(ParallelArray)) \ -IF_INTL(real,imaginary) (Intl, 37, js_InitIntlClass, CLASP(Intl)) \ -IF_BDATA(real,imaginary)(TypedObject, 38, js_InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \ - imaginary(GeneratorFunction, 39, js_InitIteratorClasses, dummy) \ +IF_INTL(real,imaginary) (Intl, 36, js_InitIntlClass, CLASP(Intl)) \ +IF_BDATA(real,imaginary)(TypedObject, 37, js_InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \ + imaginary(GeneratorFunction, 38, js_InitIteratorClasses, dummy) \ #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro) diff --git a/js/src/moz.build b/js/src/moz.build index 31769c7439b5..73d1083e9a30 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -96,7 +96,6 @@ UNIFIED_SOURCES += [ 'builtin/Intl.cpp', 'builtin/MapObject.cpp', 'builtin/Object.cpp', - 'builtin/ParallelArray.cpp', 'builtin/Profilers.cpp', 'builtin/TestingFunctions.cpp', 'builtin/TypedObject.cpp', diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index 1f2e41df78fe..c60745b0cd1d 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -191,7 +191,7 @@ namespace js { // of operation. enum ForkJoinMode { // WARNING: If you change this enum, you MUST update - // ForkJoinMode() in ParallelArray.js + // ForkJoinMode() in Utilities.js // The "normal" behavior: attempt parallel, fallback to // sequential. If compilation is ongoing in a helper thread, then diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 6ec8721d960a..51948545ca95 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -516,9 +516,6 @@ GlobalObject::initStandardClasses(JSContext *cx, Handle global) GlobalObject::initSetIteratorProto(cx, global) && #if EXPOSE_INTL_API js_InitIntlClass(cx, global) && -#endif -#if ENABLE_PARALLEL_JS - js_InitParallelArrayClass(cx, global) && #endif true; } diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 62f86a4795d6..55335eacfa05 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -12,7 +12,6 @@ #include "selfhosted.out.h" #include "builtin/Intl.h" -#include "builtin/ParallelArray.h" #include "builtin/TypedObject.h" #include "gc/Marking.h" #include "vm/ForkJoin.h" @@ -311,27 +310,6 @@ intrinsic_ForkJoinSlices(JSContext *cx, unsigned argc, Value *vp) return true; } -/* - * NewParallelArray(init, ...args): Creates a new parallel array using - * an initialization function |init|. All subsequent arguments are - * passed to |init|. The new instance will be passed as the |this| - * argument. - */ -bool -js::intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - JS_ASSERT(args[0].isObject() && args[0].toObject().is()); - - RootedFunction init(cx, &args[0].toObject().as()); - CallArgs args0 = CallArgsFromVp(argc - 1, vp + 1); - if (!js::ParallelArrayObject::constructHelper(cx, &init, args0)) - return false; - args.rval().set(args0.rval()); - return true; -} - /* * NewDenseArray(length): Allocates and returns a new dense array with * the given length where all values are initialized to holes. @@ -640,7 +618,6 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("ForkJoin", intrinsic_ForkJoin, 2,0), JS_FN("ForkJoinSlices", intrinsic_ForkJoinSlices, 0,0), - JS_FN("NewParallelArray", intrinsic_NewParallelArray, 3,0), JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0), JS_FN("ShouldForceSequential", intrinsic_ShouldForceSequential, 0,0), JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0), From 36b2bcec465709fcbdbb7b8bbc30f3e3f42402ac Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 12 Dec 2013 23:19:55 -0800 Subject: [PATCH 124/459] Bug 944074 - Followup: undefined min and max from to prevent conflicts. (r=jandem) --- js/src/jswin.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/jswin.h b/js/src/jswin.h index acab9e74a821..7cff6c84cb2d 100644 --- a/js/src/jswin.h +++ b/js/src/jswin.h @@ -14,6 +14,8 @@ #ifdef XP_WIN # include +# undef min +# undef max # undef GetProp # undef SetProp # undef CONST From 86b703757e390287ad38146b8ec63c97a6ba98b4 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 12 Dec 2013 23:23:57 -0800 Subject: [PATCH 125/459] Bug 949916 - Fix race between off-main-thread-compilation and PJS. (r=bhackett) --- js/src/jit/Ion.cpp | 10 +++++----- js/src/jit/Ion.h | 1 + js/src/jsworkers.cpp | 6 +++--- js/src/vm/ForkJoin.cpp | 13 ++++++++++++- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index ba3ee46a154d..3ca40db2aad5 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2454,8 +2454,8 @@ InvalidateActivation(FreeOp *fop, uint8_t *ionTop, bool invalidateAll) IonSpew(IonSpew_Invalidate, "END invalidating activation"); } -static void -StopOffThreadCompilation(JSCompartment *comp) +void +jit::StopAllOffThreadCompilations(JSCompartment *comp) { if (!comp->jitCompartment()) return; @@ -2467,7 +2467,7 @@ void jit::InvalidateAll(FreeOp *fop, Zone *zone) { for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) - StopOffThreadCompilation(comp); + StopAllOffThreadCompilations(comp); for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) { if (iter.activation()->compartment()->zone() == zone) { @@ -2845,10 +2845,10 @@ AutoDebugModeInvalidation::~AutoDebugModeInvalidation() FreeOp *fop = rt->defaultFreeOp(); if (comp_) { - StopOffThreadCompilation(comp_); + StopAllOffThreadCompilations(comp_); } else { for (CompartmentsInZoneIter comp(zone_); !comp.done(); comp.next()) - StopOffThreadCompilation(comp); + StopAllOffThreadCompilations(comp); } if (invalidateStack) { diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index 37ad4e778387..bf495ccf0e43 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -391,6 +391,7 @@ CodeGenerator *CompileBackEnd(MIRGenerator *mir, MacroAssembler *maybeMasm = nul void AttachFinishedCompilations(JSContext *cx); void FinishOffThreadBuilder(IonBuilder *builder); +void StopAllOffThreadCompilations(JSCompartment *comp); static inline bool IsIonEnabled(JSContext *cx) diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index 61cf70e8eca8..0136601f6a2e 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -767,14 +767,14 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state) FinishOffThreadIonCompile(ionBuilder); ionBuilder = nullptr; - // Notify the main thread in case it is waiting for the compilation to finish. - state.notifyAll(WorkerThreadState::CONSUMER); - // Ping the main thread so that the compiled code can be incorporated // at the next operation callback. Don't interrupt Ion code for this, as // this incorporation can be delayed indefinitely without affecting // performance as long as the main thread is actually executing Ion code. runtime->triggerOperationCallback(JSRuntime::TriggerCallbackAnyThreadDontStopIon); + + // Notify the main thread in case it is waiting for the compilation to finish. + state.notifyAll(WorkerThreadState::CONSUMER); } void diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index c60745b0cd1d..007d729c9218 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -867,8 +867,19 @@ js::ParallelDo::compileForParallelExecution(ExecutionStatus *status) } } } - if (allScriptsPresent) + + if (allScriptsPresent) { + // For testing modes, we want to make sure that all off thread + // compilation tasks are finished, so we don't race with + // off-main-thread-compilation setting an interrupt flag while we + // are in the middle of a test, causing unexpected bailouts. + if (mode_ != ForkJoinModeNormal) { + StopAllOffThreadCompilations(cx_->compartment()); + if (!js_HandleExecutionInterrupt(cx_)) + return fatalError(status); + } break; + } } Spew(SpewCompile, "Compilation complete (final worklist length %d)", From ef063fb0d4a6eecae13ecd2b915d1137bb60f4db Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 12 Dec 2013 23:33:50 -0800 Subject: [PATCH 126/459] Back out b6f9dbc91dc4 (bug 947038) for Cipc crashes --- dom/ipc/TabChild.cpp | 5 - gfx/layers/basic/BasicCompositor.cpp | 155 ++++++++++++++++++ gfx/layers/client/ContentClient.cpp | 5 - gfx/layers/composite/TextureHost.cpp | 8 + gfx/layers/composite/ThebesLayerComposite.cpp | 1 - gfx/thebes/gfxPlatform.cpp | 20 --- gfx/thebes/gfxPlatform.h | 5 +- widget/xpwidgets/nsBaseWidget.cpp | 2 - 8 files changed, 164 insertions(+), 37 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 7e88a30e1b88..8306641297ff 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -72,7 +72,6 @@ #include "APZCCallbackHelper.h" #include "nsILoadContext.h" #include "ipc/nsGUIEventIPC.h" -#include "gfxPlatform.h" #ifdef DEBUG #include "PCOMContentPermissionRequestChild.h" @@ -2282,10 +2281,6 @@ TabChild::InitRenderingState() NS_WARNING("failed to properly allocate layer transaction"); return false; } - - // Track which compositor backend is in use (see bug 947038). This can - // be removed when deprecated textures are removed. - gfxPlatform::GetPlatform()->SetCompositorBackend(mTextureFactoryIdentifier.mParentBackend); } else { // Pushing transactions to the parent content. shadowManager = remoteFrame->SendPLayerTransactionConstructor(); diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index 0bc966af4163..cc934831b3b8 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -63,6 +63,161 @@ public: RefPtr mSurface; }; +/** + * Texture source and host implementaion for software compositing. + */ +class DeprecatedTextureHostBasic : public DeprecatedTextureHost + , public TextureSourceBasic +{ +public: + DeprecatedTextureHostBasic() + : mCompositor(nullptr) + {} + + SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; } + + virtual IntSize GetSize() const MOZ_OVERRIDE { return mSize; } + + virtual TextureSourceBasic* AsSourceBasic() MOZ_OVERRIDE { return this; } + + SourceSurface *GetSurface() MOZ_OVERRIDE { return mSurface; } + + virtual void SetCompositor(Compositor* aCompositor) + { + mCompositor = static_cast(aCompositor); + } + + virtual const char *Name() { return "DeprecatedTextureHostBasic"; } + +protected: + virtual void UpdateImpl(const SurfaceDescriptor& aImage, + nsIntRegion *aRegion, + nsIntPoint*) MOZ_OVERRIDE + { + AutoOpenSurface surf(OPEN_READ_ONLY, aImage); + nsRefPtr surface = ShadowLayerForwarder::OpenDescriptor(OPEN_READ_ONLY, aImage); + nsRefPtr image = surface->GetAsImageSurface(); + mFormat = ImageFormatToSurfaceFormat(image->Format()); + mSize = IntSize(image->Width(), image->Height()); + mSurface = Factory::CreateWrappingDataSourceSurface(image->Data(), + image->Stride(), + mSize, + mFormat); + } + + virtual bool EnsureSurface() { + return true; + } + + virtual bool Lock() MOZ_OVERRIDE { + return EnsureSurface(); + } + + virtual TemporaryRef GetAsSurface() MOZ_OVERRIDE { + if (!mSurface) { + return nullptr; + } + return mSurface->GetDataSurface(); + } + + BasicCompositor *mCompositor; + RefPtr mSurface; + IntSize mSize; + SurfaceFormat mFormat; +}; + +void +DeserializerToPlanarYCbCrImageData(YCbCrImageDataDeserializer& aDeserializer, PlanarYCbCrData& aData) +{ + aData.mYChannel = aDeserializer.GetYData(); + aData.mYStride = aDeserializer.GetYStride(); + aData.mYSize = aDeserializer.GetYSize(); + aData.mCbChannel = aDeserializer.GetCbData(); + aData.mCrChannel = aDeserializer.GetCrData(); + aData.mCbCrStride = aDeserializer.GetCbCrStride(); + aData.mCbCrSize = aDeserializer.GetCbCrSize(); + aData.mPicSize = aDeserializer.GetYSize(); +} + +class YCbCrDeprecatedTextureHostBasic : public DeprecatedTextureHostBasic +{ +public: + virtual void UpdateImpl(const SurfaceDescriptor& aImage, + nsIntRegion *aRegion, + nsIntPoint*) MOZ_OVERRIDE + { + MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TYCbCrImage); + mSurface = nullptr; + ConvertImageToRGB(aImage); + } + + virtual void SwapTexturesImpl(const SurfaceDescriptor& aImage, + nsIntRegion* aRegion) MOZ_OVERRIDE + { + MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TYCbCrImage); + mSurface = nullptr; + } + + virtual bool EnsureSurface() MOZ_OVERRIDE + { + if (mSurface) { + return true; + } + if (!mBuffer) { + return false; + } + return ConvertImageToRGB(*mBuffer); + } + + bool ConvertImageToRGB(const SurfaceDescriptor& aImage) + { + YCbCrImageDataDeserializer deserializer(aImage.get_YCbCrImage().data().get()); + PlanarYCbCrData data; + DeserializerToPlanarYCbCrImageData(deserializer, data); + + gfxImageFormat format = gfxImageFormatRGB24; + gfxIntSize size; + gfxUtils::GetYCbCrToRGBDestFormatAndSize(data, format, size); + if (size.width > PlanarYCbCrImage::MAX_DIMENSION || + size.height > PlanarYCbCrImage::MAX_DIMENSION) { + NS_ERROR("Illegal image dest width or height"); + return false; + } + + mSize = ToIntSize(size); + mFormat = (format == gfxImageFormatRGB24) + ? FORMAT_B8G8R8X8 + : FORMAT_B8G8R8A8; + + RefPtr surface = Factory::CreateDataSourceSurface(mSize, mFormat); + gfxUtils::ConvertYCbCrToRGB(data, format, size, + surface->GetData(), + surface->Stride()); + + mSurface = surface; + return true; + } +}; + +TemporaryRef +CreateBasicDeprecatedTextureHost(SurfaceDescriptorType aDescriptorType, + uint32_t aTextureHostFlags, + uint32_t aTextureFlags) +{ + RefPtr result = nullptr; + if (aDescriptorType == SurfaceDescriptor::TYCbCrImage) { + result = new YCbCrDeprecatedTextureHostBasic(); + } else { + MOZ_ASSERT(aDescriptorType == SurfaceDescriptor::TShmem || + aDescriptorType == SurfaceDescriptor::TMemoryImage, + "We can only support Shmem currently"); + result = new DeprecatedTextureHostBasic(); + } + + result->SetFlags(aTextureFlags); + return result.forget(); +} + BasicCompositor::BasicCompositor(nsIWidget *aWidget) : mWidget(aWidget) { diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index 157c01970fcf..544784812d91 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -58,11 +58,6 @@ ContentClient::CreateContentClient(CompositableForwarder* aForwarder) useDeprecatedTextures = gfxPlatform::GetPlatform()->UseDeprecatedTextures(); #endif - // Always use new textures for the basic compositor. - if (backend == LAYERS_BASIC) { - useDeprecatedTextures = false; - } - #ifdef XP_WIN if (backend == LAYERS_D3D11) { useDoubleBuffering = !!gfxWindowsPlatform::GetPlatform()->GetD2DDevice(); diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index 4caeb0b8030a..5e4da901d9ac 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -84,6 +84,10 @@ TextureHost::AsTextureHost(PTextureParent* actor) TemporaryRef CreateDeprecatedTextureHostOGL(SurfaceDescriptorType aDescriptorType, uint32_t aDeprecatedTextureHostFlags, uint32_t aTextureFlags); +// implemented in BasicCompositor.cpp +TemporaryRef CreateBasicDeprecatedTextureHost(SurfaceDescriptorType aDescriptorType, + uint32_t aDeprecatedTextureHostFlags, + uint32_t aTextureFlags); #ifdef XP_WIN TemporaryRef CreateDeprecatedTextureHostD3D9(SurfaceDescriptorType aDescriptorType, @@ -123,6 +127,10 @@ DeprecatedTextureHost::CreateDeprecatedTextureHost(SurfaceDescriptorType aDescri aDeprecatedTextureHostFlags, aTextureFlags); #endif + case LAYERS_BASIC: + return CreateBasicDeprecatedTextureHost(aDescriptorType, + aDeprecatedTextureHostFlags, + aTextureFlags); default: MOZ_CRASH("Couldn't create texture host"); } diff --git a/gfx/layers/composite/ThebesLayerComposite.cpp b/gfx/layers/composite/ThebesLayerComposite.cpp index 7a9cc3f00c45..1f35533634d0 100644 --- a/gfx/layers/composite/ThebesLayerComposite.cpp +++ b/gfx/layers/composite/ThebesLayerComposite.cpp @@ -138,7 +138,6 @@ ThebesLayerComposite::RenderLayer(const nsIntRect& aClipRect) mBuffer->SetPaintWillResample(MayResample()); - mBuffer->SetCompositor(mCompositeManager->GetCompositor()); mBuffer->Composite(effectChain, GetEffectiveOpacity(), transform, diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 89c74cc2c52e..b563f75429e4 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -281,7 +281,6 @@ gfxPlatform::gfxPlatform() , mDrawLayerBorders(false) , mDrawTileBorders(false) , mDrawBigImageBorders(false) - , mCompositorBackend(LAYERS_NONE) { mUseHarfBuzzScripts = UNINITIALIZED_VALUE; mAllowDownloadableFonts = UNINITIALIZED_VALUE; @@ -2214,22 +2213,3 @@ gfxPlatform::ComponentAlphaEnabled() InitLayersAccelerationPrefs(); return sComponentAlphaEnabled; } - -void -gfxPlatform::SetCompositorBackend(mozilla::layers::LayersBackend backend) -{ - // The compositor backend should always be the same after one has been chosen. - MOZ_ASSERT(mCompositorBackend == LAYERS_NONE || mCompositorBackend == backend); - if (mCompositorBackend == LAYERS_NONE) { - mCompositorBackend = backend; - } -} - -bool -gfxPlatform::UseDeprecatedTextures() const -{ - if (mCompositorBackend == LAYERS_BASIC) { - return false; - } - return mLayersUseDeprecated; -} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 64d138698558..27ff830fd51e 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -20,7 +20,6 @@ #include "mozilla/RefPtr.h" #include "GfxInfoCollector.h" -#include "mozilla/layers/LayersTypes.h" #include "mozilla/layers/CompositorTypes.h" #ifdef XP_OS2 @@ -620,8 +619,7 @@ public: * This method should not be called from the compositor thread. */ bool PreferMemoryOverShmem() const; - bool UseDeprecatedTextures() const; - void SetCompositorBackend(mozilla::layers::LayersBackend backend); + bool UseDeprecatedTextures() const { return mLayersUseDeprecated; } protected: gfxPlatform(); @@ -738,7 +736,6 @@ private: bool mDrawLayerBorders; bool mDrawTileBorders; bool mDrawBigImageBorders; - mozilla::layers::LayersBackend mCompositorBackend; }; #endif /* GFX_PLATFORM_H */ diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index f8f4ce68d058..1916ced3277d 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -988,8 +988,6 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier); WindowUsesOMTC(); - gfxPlatform::GetPlatform()->SetCompositorBackend(textureFactoryIdentifier.mParentBackend); - mLayerManager = lm; return; } From abf326e01b4a344e13d26de7437c41495404ac64 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 13 Dec 2013 16:06:53 +0900 Subject: [PATCH 127/459] Bug 949906 - Add a callback to modify evaluated moz.build sandboxes, fix Sphinx docs; r=glandium --HG-- extra : rebase_source : 9ef2219145fb754a9cbe9e7e30b6f2841910f13f --- python/mozbuild/mozbuild/frontend/reader.py | 16 ++++++++++++++- .../mozbuild/test/frontend/test_reader.py | 20 +++++++++++++++++-- tools/docs/mach_commands.py | 9 ++++++++- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py index 76ee9d0c866e..da87965ea099 100644 --- a/python/mozbuild/mozbuild/frontend/reader.py +++ b/python/mozbuild/mozbuild/frontend/reader.py @@ -592,13 +592,20 @@ class BuildReader(object): This is where the build system starts. You give it a tree configuration (the output of configuration) and it executes the moz.build files and collects the data they define. + + The reader can optionally call a callable after each sandbox is evaluated + but before its evaluated content is processed. This gives callers the + opportunity to modify sandboxes before side-effects occur from their + content. This callback receives the ``Sandbox`` that was evaluated. The + return value is ignored. """ - def __init__(self, config): + def __init__(self, config, sandbox_post_eval_cb=None): self.config = config self.topsrcdir = config.topsrcdir self.topobjdir = config.topobjdir + self._sandbox_post_eval_cb = sandbox_post_eval_cb self._log = logging.getLogger(__name__) self._read_files = set() self._execution_stack = [] @@ -719,6 +726,10 @@ class BuildReader(object): sandbox = MozbuildSandbox(self.config, path, metadata=metadata) sandbox.exec_file(path, filesystem_absolute=filesystem_absolute) sandbox.execution_time = time.time() - time_start + + if self._sandbox_post_eval_cb: + self._sandbox_post_eval_cb(sandbox) + var = metadata.get('var', None) forbidden = { 'TOOL_DIRS': ['DIRS', 'PARALLEL_DIRS', 'TEST_DIRS'], @@ -770,6 +781,9 @@ class BuildReader(object): # so until the library linking operations are moved out of it, at which # point PARALLEL_DIRS will be irrelevant anyways. for gyp_sandbox in gyp_sandboxes: + if self._sandbox_post_eval_cb: + self._sandbox_post_eval_cb(gyp_sandbox) + sandbox['DIRS'].append(mozpath.relpath(gyp_sandbox['OBJDIR'], sandbox['OBJDIR'])) yield sandbox diff --git a/python/mozbuild/mozbuild/test/frontend/test_reader.py b/python/mozbuild/mozbuild/test/frontend/test_reader.py index 2d2bf672e38c..d057ef8335bc 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_reader.py +++ b/python/mozbuild/mozbuild/test/frontend/test_reader.py @@ -33,13 +33,13 @@ class TestBuildReader(unittest.TestCase): return MockConfig(path, **kwargs) - def reader(self, name, enable_tests=False): + def reader(self, name, enable_tests=False, **kwargs): extra = {} if enable_tests: extra['ENABLE_TESTS'] = '1' config = self.config(name, extra_substs=extra) - return BuildReader(config) + return BuildReader(config, **kwargs) def file_path(self, name, *args): return mozpath.join(data_path, name, *args) @@ -266,5 +266,21 @@ class TestBuildReader(unittest.TestCase): self.assertEqual([sandbox['XPIDL_MODULE'] for sandbox in sandboxes], ['foobar', 'foobar', 'foobar', 'foobar']) + def test_process_eval_callback(self): + def strip_dirs(sandbox): + sandbox['DIRS'][:] = [] + count[0] += 1 + + reader = self.reader('traversal-simple', + sandbox_post_eval_cb=strip_dirs) + + count = [0] + + sandboxes = list(reader.read_topsrcdir()) + + self.assertEqual(len(sandboxes), 1) + self.assertEqual(len(count), 1) + + if __name__ == '__main__': main() diff --git a/tools/docs/mach_commands.py b/tools/docs/mach_commands.py index b1bbb4b923b4..7948f2490de6 100644 --- a/tools/docs/mach_commands.py +++ b/tools/docs/mach_commands.py @@ -38,7 +38,14 @@ class Documentation(MachCommandBase): manager = SphinxManager(self.topsrcdir, os.path.join(self.topsrcdir, 'tools', 'docs'), outdir) - reader = BuildReader(self.config_environment) + # We don't care about GYP projects, so don't process them. This makes + # scanning faster and may even prevent an exception. + def remove_gyp_dirs(sandbox): + sandbox['GYP_DIRS'][:] = [] + + reader = BuildReader(self.config_environment, + sandbox_post_eval_cb=remove_gyp_dirs) + for sandbox in reader.walk_topsrcdir(): for dest_dir, source_dir in sandbox['SPHINX_TREES'].items(): manager.add_tree(os.path.join(sandbox['RELATIVEDIR'], From 3ec0b0a106095e0d914f72026429806ae675cfb3 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Wed, 13 Nov 2013 15:41:22 -0800 Subject: [PATCH 128/459] Bug 928195 - Part 6: Add docs for WebIDL and the build system; r=froydnj --HG-- extra : rebase_source : cee29c0bf5c050d73ef02d6ad933802b8fad21e0 --- dom/bindings/docs/index.rst | 122 ++++++++++++++++++++++++++++++++++++ dom/bindings/moz.build | 3 + 2 files changed, 125 insertions(+) create mode 100644 dom/bindings/docs/index.rst diff --git a/dom/bindings/docs/index.rst b/dom/bindings/docs/index.rst new file mode 100644 index 000000000000..16a92b64dba9 --- /dev/null +++ b/dom/bindings/docs/index.rst @@ -0,0 +1,122 @@ +.. _webidl: + +====== +WebIDL +====== + +WebIDL describes interfaces web browsers are supposed to implement. + +The interaction between WebIDL and the build system is somewhat complex. +This document will attempt to explain how it all works. + +Overview +======== + +``.webidl`` files throughout the tree define interfaces the browser +implements. Since Gecko/Firefox is implemented in C++, there is a +mechanism to convert these interfaces and associated metadata to +C++ code. That's where the build system comes into play. + +All the code for interacting with ``.webidl`` files lives under +``dom/bindings``. There is code in the build system to deal with +WebIDLs explicitly. + +WebIDL source file flavors +========================== + +Not all ``.webidl`` files are created equal! There are several flavors, +each represented by a separate symbol from :ref:`mozbuild_symbols`. + +WEBIDL_FILES + Refers to regular/static ``.webidl`` files. Most WebIDL interfaces + are defined this way. + +GENERATED_EVENTS_WEBIDL_FILES + In addition to generating a binding, these ``.webidl`` files also + generate a source file implementing the event object in C++ + +PREPROCESSED_WEBIDL_FILES + The ``.webidl`` files are generated by preprocessing an input file. + They otherwise behave like *WEBIDL_FILES*. + +TEST_WEBIDL_FILES + Like *WEBIDL_FILES* but the interfaces are for testing only and + aren't shipped with the browser. + +PREPROCESSED_TEST_WEBIDL_FILES + Like *TEST_WEBIDL_FILES* except the ``.webidl`` is obtained via + preprocessing, much like *PREPROCESSED_WEBIDL_FILES*. + +GENERATED_WEBIDL_FILES + The ``.webidl`` for these is obtained through an *external* + mechanism. Typically there are custom build rules for producing these + files. + +Producing C++ code +================== + +The most complicated part about WebIDLs is the process by which +``.webidl`` files are converted into C++. + +This process is handled by code in the :py:mod:`mozwebidlcodegen` +package. :py:class:`mozwebidlcodegen.WebIDLCodegenManager` is +specifically where you want to look for how code generation is +performed. This includes complex dependency management. + +Requirements +============ + +This section aims to document the build and developer workflow requirements +for WebIDL. + +Parser unit tests + There are parser tests provided by ``dom/bindings/parser/runtests.py`` + that should run as part of ``make check``. There must be a mechanism + to run the tests in *human* mode so they output friendly error + messages. + + The current mechanism for this is ``mach webidl-parser-test``. + +Mochitests + There are various mochitests under ``dom/bindings/test``. They should + be runnable through the standard mechanisms. + +Working with test interfaces + ``TestExampleGenBinding.cpp`` calls into methods from the + ``TestExampleInterface`` and ``TestExampleProxyInterface`` interfaces. + These interfaces need to be generated as part of the build. These + interfaces should not be exported or packaged. + + There is a ``compiletests`` make target in ``dom/bindings`` that + isn't part of the build that facilitates turnkey code generation + and test file compilation. + +Minimal rebuilds + Reprocessing every output for every change is expensive. So we don't + inconvenience people changing ``.webidl`` files, the build system + should only perform a minimal rebuild when sources change. + + This logic is mostly all handled in + :py:class:`mozwebidlcodegen.WebIDLCodegenManager`. The unit tests for + that Python code should adequately test typical rebuild scenarios. + + Bug 940469 tracks making the existing implementation better. + +Explicit method for performing codegen + There needs to be an explicit method for invoking code generation. + It needs to cover regular and test files. + + This is implemented via ``make export`` in ``dom/bindings``. + +No-op binding generation should be fast + So developers touching ``.webidl`` files are not inconvenienced, + no-op binding generation should be fast. Watch out for the build system + processing large dependency files it doesn't need in order to perform + code generation. + +Ability to generate example files + *Any* interface can have example ``.h``/``.cpp`` files generated. + There must be a mechanism to facilitate this. + + This is currently facilitated through ``mach webidl-example``. e.g. + ``mach webidl-example HTMLStyleElement``. diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index 557d4a63e92f..acb65bedc69d 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -87,3 +87,6 @@ if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']: ] FINAL_LIBRARY = 'xul' + +SPHINX_TREES['webidl'] = 'docs' +SPHINX_PYTHON_PACKAGE_DIRS += ['mozwebidlcodegen'] From 0e77c986f05b800ac29edd7d5bb029f2e9beb190 Mon Sep 17 00:00:00 2001 From: Patrick Brosset Date: Thu, 12 Dec 2013 09:31:23 +0100 Subject: [PATCH 129/459] Bug 945068 - Image preview tooltip displays correct image dimensions, r=harth --- browser/devtools/markupview/markup-view.js | 7 ++-- ...browser_inspector_markup_765105_tooltip.js | 20 ++++++----- browser/devtools/shared/widgets/Tooltip.js | 35 ++++++++++--------- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/browser/devtools/markupview/markup-view.js b/browser/devtools/markupview/markup-view.js index 7ad9cfe50f3e..b2240b0cfa2e 100644 --- a/browser/devtools/markupview/markup-view.js +++ b/browser/devtools/markupview/markup-view.js @@ -1173,10 +1173,11 @@ MarkupContainer.prototype = { this.node.getImageData(IMAGE_PREVIEW_MAX_DIM).then(data => { if (data) { data.data.string().then(str => { + let res = {data: str, size: data.size}; // Resolving the data promise and, to always keep tooltipData.data // as a promise, create a new one that resolves immediately - def.resolve(str, data.size); - this.tooltipData.data = promise.resolve(str, data.size); + def.resolve(res); + this.tooltipData.data = promise.resolve(res); }); } }); @@ -1186,7 +1187,7 @@ MarkupContainer.prototype = { _buildTooltipContent: function(target, tooltip) { if (this.tooltipData && target === this.tooltipData.target) { - this.tooltipData.data.then((data, size) => { + this.tooltipData.data.then(({data, size}) => { tooltip.setImageContent(data, size); }); return true; diff --git a/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js b/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js index a3d3dfc50817..5c5487c89d3c 100644 --- a/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js +++ b/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js @@ -16,10 +16,10 @@ const PAGE_CONTENT = [ ].join("\n"); const TEST_NODES = [ - "img.local", - "img.data", - "img.remote", - ".canvas" + {selector: "img.local", size: "192 x 192"}, + {selector: "img.data", size: "64 x 64"}, + {selector: "img.remote", size: "22 x 23"}, + {selector: ".canvas", size: "600 x 600"} ]; function test() { @@ -77,8 +77,8 @@ function testImageTooltip(index) { return endTests(); } - let node = contentDoc.querySelector(TEST_NODES[index]); - ok(node, "We have the [" + TEST_NODES[index] + "] image node to test for tooltip"); + let node = contentDoc.querySelector(TEST_NODES[index].selector); + ok(node, "We have the [" + TEST_NODES[index].selector + "] image node to test for tooltip"); let isImg = node.tagName.toLowerCase() === "img"; let container = getContainerForRawNode(markup, node); @@ -90,10 +90,14 @@ function testImageTooltip(index) { assertTooltipShownOn(target, () => { let images = markup.tooltip.panel.getElementsByTagName("image"); - is(images.length, 1, "Tooltip for [" + TEST_NODES[index] + "] contains an image"); + is(images.length, 1, + "Tooltip for [" + TEST_NODES[index].selector + "] contains an image"); + + let label = markup.tooltip.panel.querySelector(".devtools-tooltip-caption"); + is(label.textContent, TEST_NODES[index].size, + "Tooltip label for [" + TEST_NODES[index].selector + "] displays the right image size") markup.tooltip.hide(); - testImageTooltip(index + 1); }); } diff --git a/browser/devtools/shared/widgets/Tooltip.js b/browser/devtools/shared/widgets/Tooltip.js index 3b554d55b188..1d5ceb9dd1ce 100644 --- a/browser/devtools/shared/widgets/Tooltip.js +++ b/browser/devtools/shared/widgets/Tooltip.js @@ -518,30 +518,33 @@ Tooltip.prototype = { } vbox.appendChild(image); - // Temporary label during image load + // Dimension label let label = this.doc.createElement("label"); label.classList.add("devtools-tooltip-caption"); label.classList.add("theme-comment"); - label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage"); + if (options.naturalWidth && options.naturalHeight) { + label.textContent = this._getImageDimensionLabel(options.naturalWidth, + options.naturalHeight); + this.setSize(vbox.width, vbox.height); + } else { + // If no dimensions were provided, load the image to get them + label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage"); + let imgObj = new this.doc.defaultView.Image(); + imgObj.src = imageUrl; + imgObj.onload = () => { + imgObj.onload = null; + label.textContent = this._getImageDimensionLabel(imgObj.naturalWidth, + imgObj.naturalHeight); + this.setSize(vbox.width, vbox.height); + } + } vbox.appendChild(label); this.content = vbox; - - // Load the image to get dimensions and display it when done - let imgObj = new this.doc.defaultView.Image(); - imgObj.src = imageUrl; - imgObj.onload = () => { - imgObj.onload = null; - - // Display dimensions - let w = options.naturalWidth || imgObj.naturalWidth; - let h = options.naturalHeight || imgObj.naturalHeight; - label.textContent = w + " x " + h; - - this.setSize(vbox.width, vbox.height); - } }, + _getImageDimensionLabel: (w, h) => w + " x " + h, + /** * Exactly the same as the `image` function but takes a css background image * value instead : url(....) From e5f11e0c79994d97b7740a58eb77b34a72fe50d9 Mon Sep 17 00:00:00 2001 From: Christian Ascheberg Date: Thu, 12 Dec 2013 09:56:57 +0100 Subject: [PATCH 130/459] Bug 735607 - [Australis] Add hover effect in URL and search bars when using personas. r=dao --- browser/themes/windows/browser.css | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index f5f78c7b5ba6..33eb69e814e2 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -841,6 +841,16 @@ menuitem.bookmark-item { color: black; } +#urlbar:-moz-lwtheme:hover:not([focused]):not([readonly]), +.searchbar-textbox:-moz-lwtheme:hover:not([focused]) { + background-color: rgba(255,255,255,.9); +} + +#urlbar:-moz-lwtheme[focused], +.searchbar-textbox:-moz-lwtheme[focused] { + background-color: white; +} + @conditionalForwardWithUrlbar@ > #urlbar-wrapper { padding-left: @conditionalForwardWithUrlbarWidth@px; -moz-margin-start: -@conditionalForwardWithUrlbarWidth@px; @@ -898,11 +908,6 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder, color: #777; } -#urlbar:-moz-lwtheme[focused="true"], -.searchbar-textbox:-moz-lwtheme[focused="true"] { - background-color: white; -} - #urlbar-container { -moz-box-align: center; } From 3ebe27ab184162a90421ab0720e034a7182b04f7 Mon Sep 17 00:00:00 2001 From: Katie Thomas Date: Thu, 12 Dec 2013 09:58:28 +0100 Subject: [PATCH 131/459] Bug 891194 - Changed BROKEN_WM_Z_ORDER to not be defined for mac. r=dao --- browser/components/sessionstore/src/SessionStore.jsm | 4 +++- browser/modules/RecentWindow.jsm | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm index c809c36b6074..519d359ba980 100644 --- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -86,9 +86,11 @@ const BROWSER_EVENTS = [ // The number of milliseconds in a day const MS_PER_DAY = 1000.0 * 60.0 * 60.0 * 24.0; -#ifndef XP_WIN +#ifdef XP_UNIX +#ifndef XP_MACOSX #define BROKEN_WM_Z_ORDER #endif +#endif Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); diff --git a/browser/modules/RecentWindow.jsm b/browser/modules/RecentWindow.jsm index 0018b502c70e..c328b6e44d50 100644 --- a/browser/modules/RecentWindow.jsm +++ b/browser/modules/RecentWindow.jsm @@ -11,9 +11,11 @@ const Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); -#ifndef XP_WIN +#ifdef XP_UNIX +#ifndef XP_MACOSX #define BROKEN_WM_Z_ORDER #endif +#endif this.RecentWindow = { /* From 99b582a0b8ba3172b133dee8515593186311e5c3 Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Sat, 7 Dec 2013 09:50:19 +0100 Subject: [PATCH 132/459] Bug 947583 - Fix "this.updateProviderURLs is not a function" when toggling "Block reported attack sites" preference r=dolske From 8ba7cef13d00454ddcdec7e4f8eb32bcb6bee5ac Mon Sep 17 00:00:00 2001 --- toolkit/components/url-classifier/SafeBrowsing.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/url-classifier/SafeBrowsing.jsm b/toolkit/components/url-classifier/SafeBrowsing.jsm index f6ef132303f2..9f39b97cfa4b 100644 --- a/toolkit/components/url-classifier/SafeBrowsing.jsm +++ b/toolkit/components/url-classifier/SafeBrowsing.jsm @@ -35,7 +35,7 @@ this.SafeBrowsing = { return; } - Services.prefs.addObserver("browser.safebrowsing", this.readPrefs, false); + Services.prefs.addObserver("browser.safebrowsing", this.readPrefs.bind(this), false); this.readPrefs(); // Register our two types of tables, and add custom Mozilla entries From 59409361e01d51e3a19c5fe6ab00fff01a5b359b Mon Sep 17 00:00:00 2001 From: Steven MacLeod Date: Thu, 12 Dec 2013 10:18:02 +0100 Subject: [PATCH 133/459] Bug 907129 - Merge closed tabs data when restoreWindow() is called with overwriteTabs=false. r=ttaubert X-Git-Commit-ID: d51bff1ffaa80d4f441863eb864e7cfd7f8e6a00 --- .../sessionstore/src/SessionStore.jsm | 18 ++++- .../components/sessionstore/test/browser.ini | 1 + .../test/browser_merge_closed_tabs.js | 71 +++++++++++++++++++ browser/components/sessionstore/test/head.js | 5 ++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 browser/components/sessionstore/test/browser_merge_closed_tabs.js diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm index 519d359ba980..20055129ae6c 100644 --- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -2480,8 +2480,24 @@ let SessionStoreInternal = { this._windows[aWindow.__SSi].extData[key] = winData.extData[key]; } } + + let newClosedTabsData = winData._closedTabs || []; + if (overwriteTabs || firstWindow) { - this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || []; + // Overwrite existing closed tabs data when overwriteTabs=true + // or we're the first window to be restored. + this._windows[aWindow.__SSi]._closedTabs = newClosedTabsData; + } else if (this._max_tabs_undo > 0) { + // If we merge tabs, we also want to merge closed tabs data. We'll assume + // the restored tabs were closed more recently and append the current list + // of closed tabs to the new one... + newClosedTabsData = + newClosedTabsData.concat(this._windows[aWindow.__SSi]._closedTabs); + + // ... and make sure that we don't exceed the max number of closed tabs + // we can restore. + this._windows[aWindow.__SSi]._closedTabs = + newClosedTabsData.slice(0, this._max_tabs_undo); } this.restoreTabs(aWindow, tabs, winData.tabs, diff --git a/browser/components/sessionstore/test/browser.ini b/browser/components/sessionstore/test/browser.ini index 6ed3b4915046..8f0c479efa72 100644 --- a/browser/components/sessionstore/test/browser.ini +++ b/browser/components/sessionstore/test/browser.ini @@ -56,6 +56,7 @@ support-files = [browser_formdata_format.js] [browser_global_store.js] [browser_input.js] +[browser_merge_closed_tabs.js] [browser_pageshow.js] [browser_pageStyle.js] [browser_sessionStorage.js] diff --git a/browser/components/sessionstore/test/browser_merge_closed_tabs.js b/browser/components/sessionstore/test/browser_merge_closed_tabs.js new file mode 100644 index 000000000000..08a161784904 --- /dev/null +++ b/browser/components/sessionstore/test/browser_merge_closed_tabs.js @@ -0,0 +1,71 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * This test ensures that closed tabs are merged when restoring + * a window state without overwriting tabs. + */ +add_task(function () { + const initialState = { + windows: [{ + tabs: [ + { entries: [{ url: "about:blank" }] } + ], + _closedTabs: [ + { state: { entries: [{ ID: 1000, url: "about:blank" }]} }, + { state: { entries: [{ ID: 1001, url: "about:blank" }]} } + ] + }] + } + + const restoreState = { + windows: [{ + tabs: [ + { entries: [{ url: "about:robots" }] } + ], + _closedTabs: [ + { state: { entries: [{ ID: 1002, url: "about:robots" }]} }, + { state: { entries: [{ ID: 1003, url: "about:robots" }]} }, + { state: { entries: [{ ID: 1004, url: "about:robots" }]} } + ] + }] + } + + const maxTabsUndo = 4; + gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", maxTabsUndo); + + // Open a new window and restore it to an initial state. + let win = yield promiseNewWindowLoaded({private: false}); + SessionStore.setWindowState(win, JSON.stringify(initialState), true); + is(SessionStore.getClosedTabCount(win), 2, "2 closed tabs after restoring initial state"); + + // Restore the new state but do not overwrite existing tabs (this should + // cause the closed tabs to be merged). + SessionStore.setWindowState(win, JSON.stringify(restoreState), false); + + // Verify the windows closed tab data is correct. + let iClosed = initialState.windows[0]._closedTabs; + let rClosed = restoreState.windows[0]._closedTabs; + let cData = JSON.parse(SessionStore.getClosedTabData(win)); + + is(cData.length, Math.min(iClosed.length + rClosed.length, maxTabsUndo), + "Number of closed tabs is correct"); + + // When the closed tabs are merged the restored tabs are considered to be + // closed more recently. + for (let i = 0; i < cData.length; i++) { + if (i < rClosed.length) { + is(cData[i].state.entries[0].ID, rClosed[i].state.entries[0].ID, + "Closed tab entry matches"); + } else { + is(cData[i].state.entries[0].ID, iClosed[i - rClosed.length].state.entries[0].ID, + "Closed tab entry matches"); + } + } + + // Clean up. + gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo"); + win.close(); +}); + + diff --git a/browser/components/sessionstore/test/head.js b/browser/components/sessionstore/test/head.js index 8739e69c5be4..fee33a150aa5 100644 --- a/browser/components/sessionstore/test/head.js +++ b/browser/components/sessionstore/test/head.js @@ -412,6 +412,11 @@ function whenNewWindowLoaded(aOptions, aCallback) { whenDelayedStartupFinished(win, () => aCallback(win)); return win; } +function promiseNewWindowLoaded(aOptions) { + let deferred = Promise.defer(); + whenNewWindowLoaded(aOptions, deferred.resolve); + return deferred.promise; +} /** * This waits for the browser-delayed-startup-finished notification of a given From 75b19143b4be51ea169d71324f91bf2b31a245ae Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 10:55:35 +0000 Subject: [PATCH 134/459] Bug 942231 - Remove unused member from HomePager (r=margaret) --- mobile/android/base/home/HomePager.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index b0dae21f4f5c..0db739c235f5 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -30,9 +30,6 @@ import java.util.EnumMap; import java.util.EnumSet; public class HomePager extends ViewPager { - // Subpage fragment tag - public static final String SUBPAGE_TAG = "home_pager_subpage"; - private final Context mContext; private volatile boolean mLoaded; private Decor mDecor; From 9fa395445ce094b77632f35f9992b0250589fd97 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 10:55:35 +0000 Subject: [PATCH 135/459] Bug 942231 - Factor out HomePager's adapter into a separate file (r=margaret) --- mobile/android/base/home/HomeAdapter.java | 139 +++++++++++++++++++ mobile/android/base/home/HomePager.java | 157 +++++----------------- mobile/android/base/moz.build | 1 + 3 files changed, 172 insertions(+), 125 deletions(-) create mode 100644 mobile/android/base/home/HomeAdapter.java diff --git a/mobile/android/base/home/HomeAdapter.java b/mobile/android/base/home/HomeAdapter.java new file mode 100644 index 000000000000..caaab89a79b9 --- /dev/null +++ b/mobile/android/base/home/HomeAdapter.java @@ -0,0 +1,139 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.home.HomePager; +import org.mozilla.gecko.home.HomePager.Page; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.EnumMap; + +class HomeAdapter extends FragmentStatePagerAdapter { + + private final Context mContext; + private final ArrayList mTabs; + private final EnumMap mPages; + + private OnAddTabListener mAddTabListener; + + interface OnAddTabListener { + public void onAddTab(String title); + } + + final class TabInfo { + private final Page page; + private final Class clss; + private final Bundle args; + private final String title; + + TabInfo(Page page, Class clss, Bundle args, String title) { + this.page = page; + this.clss = clss; + this.args = args; + this.title = title; + } + } + + public HomeAdapter(Context context, FragmentManager fm) { + super(fm); + + mContext = context; + + mTabs = new ArrayList(); + mPages = new EnumMap(Page.class); + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Fragment getItem(int position) { + TabInfo info = mTabs.get(position); + return Fragment.instantiate(mContext, info.clss.getName(), info.args); + } + + @Override + public CharSequence getPageTitle(int position) { + TabInfo info = mTabs.get(position); + return info.title.toUpperCase(); + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + Fragment fragment = (Fragment) super.instantiateItem(container, position); + mPages.put(mTabs.get(position).page, fragment); + + return fragment; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + super.destroyItem(container, position, object); + mPages.remove(mTabs.get(position).page); + } + + public void setOnAddTabListener(OnAddTabListener listener) { + mAddTabListener = listener; + } + + public void addTab(Page page, Class clss, Bundle args, String title) { + addTab(-1, page, clss, args, title); + } + + public void addTab(int index, Page page, Class clss, Bundle args, String title) { + TabInfo info = new TabInfo(page, clss, args, title); + + if (index >= 0) { + mTabs.add(index, info); + } else { + mTabs.add(info); + } + + notifyDataSetChanged(); + + if (mAddTabListener != null) { + mAddTabListener.onAddTab(title); + } + } + + public int getItemPosition(Page page) { + for (int i = 0; i < mTabs.size(); i++) { + TabInfo info = mTabs.get(i); + if (info.page == page) { + return i; + } + } + + return -1; + } + + public Page getPageAtPosition(int position) { + TabInfo info = mTabs.get(position); + return info.page; + } + + public void setCanLoadHint(boolean canLoadHint) { + // Update fragment arguments for future instances + for (TabInfo info : mTabs) { + info.args.putBoolean(HomePager.CAN_LOAD_ARG, canLoadHint); + } + + // Enable/disable loading on all existing pages + for (Fragment page : mPages.values()) { + final HomeFragment homePage = (HomeFragment) page; + homePage.setCanLoadHint(canLoadHint); + } + } +} diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 0db739c235f5..f1d42dd4aa69 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -8,6 +8,7 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; +import org.mozilla.gecko.home.HomeAdapter.OnAddTabListener; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.HardwareUtils; @@ -16,8 +17,6 @@ import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.ViewGroup.LayoutParams; import android.util.AttributeSet; @@ -25,15 +24,16 @@ import android.view.MotionEvent; import android.view.ViewGroup; import android.view.View; -import java.util.ArrayList; -import java.util.EnumMap; import java.util.EnumSet; public class HomePager extends ViewPager { + private final Context mContext; private volatile boolean mLoaded; private Decor mDecor; + private final OnAddTabListener mAddTabListener; + // List of pages in order. @RobocopTarget public enum Page { @@ -53,8 +53,6 @@ public class HomePager extends ViewPager { static final String LIST_TAG_LAST_TABS = "last_tabs"; static final String LIST_TAG_BROWSER_SEARCH = "browser_search"; - private EnumMap mPages = new EnumMap(Page.class); - public interface OnUrlOpenListener { public enum Flags { ALLOW_SWITCH_TO_TAB @@ -92,6 +90,15 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; + mAddTabListener = new OnAddTabListener() { + @Override + public void onAddTab(String title) { + if (mDecor != null) { + mDecor.onAddPagerView(title); + } + } + }; + // This is to keep all 4 pages in memory after they are // selected in the pager. setOffscreenPageLimit(3); @@ -109,6 +116,14 @@ public class HomePager extends ViewPager { if (child instanceof Decor) { ((ViewPager.LayoutParams) params).isDecor = true; mDecor = (Decor) child; + + mDecor.setOnTitleClickListener(new OnTitleClickListener() { + @Override + public void onTitleClicked(int index) { + setCurrentItem(index, true); + } + }); + setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { @@ -129,8 +144,10 @@ public class HomePager extends ViewPager { } public void redisplay(FragmentManager fm) { - final TabsAdapter adapter = (TabsAdapter) getAdapter(); - show(fm, adapter.getCurrentPage(), null); + final HomeAdapter adapter = (HomeAdapter) getAdapter(); + final Page currentPage = adapter.getPageAtPosition(getCurrentItem()); + + show(fm, currentPage, null); } /** @@ -140,7 +157,13 @@ public class HomePager extends ViewPager { */ public void show(FragmentManager fm, Page page, PropertyAnimator animator) { mLoaded = true; - final TabsAdapter adapter = new TabsAdapter(fm); + + if (mDecor != null) { + mDecor.removeAllPagerViews(); + } + + final HomeAdapter adapter = new HomeAdapter(mContext, fm); + adapter.setOnAddTabListener(mAddTabListener); // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); @@ -222,122 +245,6 @@ public class HomePager extends ViewPager { } } - class TabsAdapter extends FragmentStatePagerAdapter - implements OnTitleClickListener { - private final ArrayList mTabs = new ArrayList(); - - final class TabInfo { - private final Page page; - private final Class clss; - private final Bundle args; - private final String title; - - TabInfo(Page page, Class clss, Bundle args, String title) { - this.page = page; - this.clss = clss; - this.args = args; - this.title = title; - } - } - - public TabsAdapter(FragmentManager fm) { - super(fm); - - if (mDecor != null) { - mDecor.removeAllPagerViews(); - mDecor.setOnTitleClickListener(this); - } - } - - public void addTab(Page page, Class clss, Bundle args, String title) { - addTab(-1, page, clss, args, title); - } - - public void addTab(int index, Page page, Class clss, Bundle args, String title) { - TabInfo info = new TabInfo(page, clss, args, title); - - if (index >= 0) { - mTabs.add(index, info); - } else { - mTabs.add(info); - } - - notifyDataSetChanged(); - - if (mDecor != null) { - mDecor.onAddPagerView(title); - } - } - - @Override - public void onTitleClicked(int index) { - setCurrentItem(index, true); - } - - public int getItemPosition(Page page) { - for (int i = 0; i < mTabs.size(); i++) { - TabInfo info = mTabs.get(i); - if (info.page == page) { - return i; - } - } - - return -1; - } - - public Page getCurrentPage() { - int currentItem = getCurrentItem(); - TabInfo info = mTabs.get(currentItem); - return info.page; - } - - @Override - public int getCount() { - return mTabs.size(); - } - - @Override - public Fragment getItem(int position) { - TabInfo info = mTabs.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), info.args); - } - - @Override - public CharSequence getPageTitle(int position) { - TabInfo info = mTabs.get(position); - return info.title.toUpperCase(); - } - - @Override - public Object instantiateItem(ViewGroup container, int position) { - Fragment fragment = (Fragment) super.instantiateItem(container, position); - - mPages.put(mTabs.get(position).page, fragment); - - return fragment; - } - - @Override - public void destroyItem(ViewGroup container, int position, Object object) { - super.destroyItem(container, position, object); - - mPages.remove(mTabs.get(position).page); - } - - public void setCanLoadHint(boolean canLoadHint) { - // Update fragment arguments for future instances - for (TabInfo info : mTabs) { - info.args.putBoolean(CAN_LOAD_ARG, canLoadHint); - } - - // Enable/disable loading on all existing pages - for (Fragment page : mPages.values()) { - final HomeFragment homePage = (HomeFragment) page; - homePage.setCanLoadHint(canLoadHint); - } - } - } - @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 2eaf5ce6262a..476cb6c4855d 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -208,6 +208,7 @@ gbjar.sources += [ 'home/BrowserSearch.java', 'home/FadedTextView.java', 'home/HistoryPage.java', + 'home/HomeAdapter.java', 'home/HomeBanner.java', 'home/HomeFragment.java', 'home/HomeListView.java', From ec75db4e9ba11edffab9e05c78e3e0597bbadbf1 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 10:55:36 +0000 Subject: [PATCH 136/459] Bug 942231 - Use 'page' terminology in HomePager instead of 'tab' (r=margaret) --- mobile/android/base/home/HomeAdapter.java | 52 +++++++++++------------ mobile/android/base/home/HomePager.java | 22 +++++----- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/mobile/android/base/home/HomeAdapter.java b/mobile/android/base/home/HomeAdapter.java index caaab89a79b9..2d424face292 100644 --- a/mobile/android/base/home/HomeAdapter.java +++ b/mobile/android/base/home/HomeAdapter.java @@ -21,22 +21,22 @@ import java.util.EnumMap; class HomeAdapter extends FragmentStatePagerAdapter { private final Context mContext; - private final ArrayList mTabs; + private final ArrayList mPageInfos; private final EnumMap mPages; - private OnAddTabListener mAddTabListener; + private OnAddPageListener mAddPageListener; - interface OnAddTabListener { - public void onAddTab(String title); + interface OnAddPageListener { + public void onAddPage(String title); } - final class TabInfo { + final class PageInfo { private final Page page; private final Class clss; private final Bundle args; private final String title; - TabInfo(Page page, Class clss, Bundle args, String title) { + PageInfo(Page page, Class clss, Bundle args, String title) { this.page = page; this.clss = clss; this.args = args; @@ -49,31 +49,31 @@ class HomeAdapter extends FragmentStatePagerAdapter { mContext = context; - mTabs = new ArrayList(); + mPageInfos = new ArrayList(); mPages = new EnumMap(Page.class); } @Override public int getCount() { - return mTabs.size(); + return mPageInfos.size(); } @Override public Fragment getItem(int position) { - TabInfo info = mTabs.get(position); + PageInfo info = mPageInfos.get(position); return Fragment.instantiate(mContext, info.clss.getName(), info.args); } @Override public CharSequence getPageTitle(int position) { - TabInfo info = mTabs.get(position); + PageInfo info = mPageInfos.get(position); return info.title.toUpperCase(); } @Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = (Fragment) super.instantiateItem(container, position); - mPages.put(mTabs.get(position).page, fragment); + mPages.put(mPageInfos.get(position).page, fragment); return fragment; } @@ -81,36 +81,36 @@ class HomeAdapter extends FragmentStatePagerAdapter { @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); - mPages.remove(mTabs.get(position).page); + mPages.remove(mPageInfos.get(position).page); } - public void setOnAddTabListener(OnAddTabListener listener) { - mAddTabListener = listener; + public void setOnAddPageListener(OnAddPageListener listener) { + mAddPageListener = listener; } - public void addTab(Page page, Class clss, Bundle args, String title) { - addTab(-1, page, clss, args, title); + public void addPage(Page page, Class clss, Bundle args, String title) { + addPage(-1, page, clss, args, title); } - public void addTab(int index, Page page, Class clss, Bundle args, String title) { - TabInfo info = new TabInfo(page, clss, args, title); + public void addPage(int index, Page page, Class clss, Bundle args, String title) { + PageInfo info = new PageInfo(page, clss, args, title); if (index >= 0) { - mTabs.add(index, info); + mPageInfos.add(index, info); } else { - mTabs.add(info); + mPageInfos.add(info); } notifyDataSetChanged(); - if (mAddTabListener != null) { - mAddTabListener.onAddTab(title); + if (mAddPageListener != null) { + mAddPageListener.onAddPage(title); } } public int getItemPosition(Page page) { - for (int i = 0; i < mTabs.size(); i++) { - TabInfo info = mTabs.get(i); + for (int i = 0; i < mPageInfos.size(); i++) { + PageInfo info = mPageInfos.get(i); if (info.page == page) { return i; } @@ -120,13 +120,13 @@ class HomeAdapter extends FragmentStatePagerAdapter { } public Page getPageAtPosition(int position) { - TabInfo info = mTabs.get(position); + PageInfo info = mPageInfos.get(position); return info.page; } public void setCanLoadHint(boolean canLoadHint) { // Update fragment arguments for future instances - for (TabInfo info : mTabs) { + for (PageInfo info : mPageInfos) { info.args.putBoolean(HomePager.CAN_LOAD_ARG, canLoadHint); } diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index f1d42dd4aa69..66253b1be0f6 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -8,7 +8,7 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; -import org.mozilla.gecko.home.HomeAdapter.OnAddTabListener; +import org.mozilla.gecko.home.HomeAdapter.OnAddPageListener; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.HardwareUtils; @@ -32,7 +32,7 @@ public class HomePager extends ViewPager { private volatile boolean mLoaded; private Decor mDecor; - private final OnAddTabListener mAddTabListener; + private final OnAddPageListener mAddPageListener; // List of pages in order. @RobocopTarget @@ -90,9 +90,9 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; - mAddTabListener = new OnAddTabListener() { + mAddPageListener = new OnAddPageListener() { @Override - public void onAddTab(String title) { + public void onAddPage(String title) { if (mDecor != null) { mDecor.onAddPagerView(title); } @@ -163,26 +163,26 @@ public class HomePager extends ViewPager { } final HomeAdapter adapter = new HomeAdapter(mContext, fm); - adapter.setOnAddTabListener(mAddTabListener); + adapter.setOnAddPageListener(mAddPageListener); // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); - adapter.addTab(Page.TOP_SITES, TopSitesPage.class, new Bundle(), + adapter.addPage(Page.TOP_SITES, TopSitesPage.class, new Bundle(), getContext().getString(R.string.home_top_sites_title)); - adapter.addTab(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), + adapter.addPage(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), getContext().getString(R.string.bookmarks_title)); // We disable reader mode support on low memory devices. Hence the // reading list page should not show up on such devices. if (!HardwareUtils.isLowMemoryPlatform()) { - adapter.addTab(Page.READING_LIST, ReadingListPage.class, new Bundle(), + adapter.addPage(Page.READING_LIST, ReadingListPage.class, new Bundle(), getContext().getString(R.string.reading_list_title)); } - // On phones, the history tab is the first tab. On tablets, the - // history tab is the last tab. - adapter.addTab(HardwareUtils.isTablet() ? -1 : 0, + // On phones, the history page is the first one. On tablets, the + // history page is the last. + adapter.addPage(HardwareUtils.isTablet() ? -1 : 0, Page.HISTORY, HistoryPage.class, new Bundle(), getContext().getString(R.string.home_history_title)); From ce83ada3cbb52713975e540333783b99db64b885 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 10:55:36 +0000 Subject: [PATCH 137/459] Bug 942231 - Keep showHomePager* method together in BrowserApp (r=margaret) --- mobile/android/base/BrowserApp.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index c6be146ff1cc..87d29f055f6b 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1618,10 +1618,6 @@ abstract public class BrowserApp extends GeckoApp } } - private void showHomePager(HomePager.Page page) { - showHomePagerWithAnimator(page, null); - } - @Override public void onLocaleReady(final String locale) { super.onLocaleReady(locale); @@ -1636,6 +1632,10 @@ abstract public class BrowserApp extends GeckoApp } } + private void showHomePager(HomePager.Page page) { + showHomePagerWithAnimator(page, null); + } + private void showHomePagerWithAnimator(HomePager.Page page, PropertyAnimator animator) { if (isHomePagerVisible()) { return; From 84a2b7ae8f66d08163008d077a46933ef7b59bea Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 10:55:36 +0000 Subject: [PATCH 138/459] Bug 942231 - Define constant with default value for 'can load' hint (r=margaret) --- mobile/android/base/home/HomeFragment.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/home/HomeFragment.java b/mobile/android/base/home/HomeFragment.java index 48b976ba74fa..45e1ee26f015 100644 --- a/mobile/android/base/home/HomeFragment.java +++ b/mobile/android/base/home/HomeFragment.java @@ -44,6 +44,9 @@ abstract class HomeFragment extends Fragment { // Share MIME type. private static final String SHARE_MIME_TYPE = "text/plain"; + // Default value for "can load" hint + static final boolean DEFAULT_CAN_LOAD_HINT = false; + // Whether the fragment can load its content or not // This is used to defer data loading until the editing // mode animation ends. @@ -58,9 +61,9 @@ abstract class HomeFragment extends Fragment { final Bundle args = getArguments(); if (args != null) { - mCanLoadHint = args.getBoolean(HomePager.CAN_LOAD_ARG, false); + mCanLoadHint = args.getBoolean(HomePager.CAN_LOAD_ARG, DEFAULT_CAN_LOAD_HINT); } else { - mCanLoadHint = false; + mCanLoadHint = DEFAULT_CAN_LOAD_HINT; } mIsLoaded = false; From 6cfc2fd5ae31e69b368a7deab041e109f22e714e Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 10:55:36 +0000 Subject: [PATCH 139/459] Bug 942231 - Change HomePager to be backed by HomeConfig (r=margaret) --- mobile/android/base/BrowserApp.java | 14 +- mobile/android/base/Tab.java | 5 +- mobile/android/base/home/HomeAdapter.java | 144 +++++++---- mobile/android/base/home/HomeConfig.java | 224 ++++++++++++++++++ .../android/base/home/HomeConfigLoader.java | 83 +++++++ .../base/home/HomeConfigMemBackend.java | 67 ++++++ mobile/android/base/home/HomePager.java | 154 +++++++++--- mobile/android/base/home/ListPage.java | 185 +++++++++++++++ mobile/android/base/moz.build | 4 + 9 files changed, 794 insertions(+), 86 deletions(-) create mode 100644 mobile/android/base/home/HomeConfig.java create mode 100644 mobile/android/base/home/HomeConfigLoader.java create mode 100644 mobile/android/base/home/HomeConfigMemBackend.java create mode 100644 mobile/android/base/home/ListPage.java diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 87d29f055f6b..3fcfa90c5427 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1467,7 +1467,7 @@ abstract public class BrowserApp extends GeckoApp animator.setUseHardwareLayer(false); mBrowserToolbar.startEditing(url, animator); - showHomePagerWithAnimator(HomePager.Page.TOP_SITES, animator); + showHomePagerWithAnimator(animator); animator.start(); } @@ -1623,7 +1623,7 @@ abstract public class BrowserApp extends GeckoApp super.onLocaleReady(locale); if (mHomePager != null) { // Blow it away and rebuild it with the right strings. - mHomePager.redisplay(getSupportFragmentManager()); + mHomePager.redisplay(getSupportLoaderManager(), getSupportFragmentManager()); } if (mMenu != null) { @@ -1636,6 +1636,12 @@ abstract public class BrowserApp extends GeckoApp showHomePagerWithAnimator(page, null); } + private void showHomePagerWithAnimator(PropertyAnimator animator) { + // Passing null here means the default page will be defined + // by the HomePager's configuration. + showHomePagerWithAnimator(null, animator); + } + private void showHomePagerWithAnimator(HomePager.Page page, PropertyAnimator animator) { if (isHomePagerVisible()) { return; @@ -1655,7 +1661,9 @@ abstract public class BrowserApp extends GeckoApp mHomePager = (HomePager) homePagerStub.inflate(); } - mHomePager.show(getSupportFragmentManager(), page, animator); + mHomePager.show(getSupportLoaderManager(), + getSupportFragmentManager(), + page, animator); // Hide the web content so it cannot be focused by screen readers. hideWebContentOnPropertyAnimationEnd(animator); diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 09171f428372..0f1a02d10a7c 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -95,7 +95,7 @@ public class Tab { mUserSearch = ""; mExternal = external; mParentId = parentId; - mAboutHomePage = HomePager.Page.TOP_SITES; + mAboutHomePage = null; mTitle = title == null ? "" : title; mFavicon = null; mFaviconUrl = null; @@ -155,7 +155,6 @@ public class Tab { mAboutHomePage = page; } - // may be null if user-entered query hasn't yet been resolved to a URI public synchronized String getURL() { return mUrl; @@ -660,6 +659,8 @@ public class Tab { final String homePage = message.getString("aboutHomePage"); if (!TextUtils.isEmpty(homePage)) { setAboutHomePage(HomePager.Page.valueOf(homePage)); + } else { + setAboutHomePage(null); } Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.LOCATION_CHANGE, oldUrl); diff --git a/mobile/android/base/home/HomeAdapter.java b/mobile/android/base/home/HomeAdapter.java index 2d424face292..7cbdc06283a2 100644 --- a/mobile/android/base/home/HomeAdapter.java +++ b/mobile/android/base/home/HomeAdapter.java @@ -5,6 +5,8 @@ package org.mozilla.gecko.home; +import org.mozilla.gecko.home.HomeConfig.PageEntry; +import org.mozilla.gecko.home.HomeConfig.PageType; import org.mozilla.gecko.home.HomePager; import org.mozilla.gecko.home.HomePager.Page; @@ -16,13 +18,16 @@ import android.support.v4.app.FragmentStatePagerAdapter; import android.view.ViewGroup; import java.util.ArrayList; -import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; class HomeAdapter extends FragmentStatePagerAdapter { private final Context mContext; private final ArrayList mPageInfos; - private final EnumMap mPages; + private final HashMap mPages; + + private boolean mCanLoadHint; private OnAddPageListener mAddPageListener; @@ -30,27 +35,14 @@ class HomeAdapter extends FragmentStatePagerAdapter { public void onAddPage(String title); } - final class PageInfo { - private final Page page; - private final Class clss; - private final Bundle args; - private final String title; - - PageInfo(Page page, Class clss, Bundle args, String title) { - this.page = page; - this.clss = clss; - this.args = args; - this.title = title; - } - } - public HomeAdapter(Context context, FragmentManager fm) { super(fm); mContext = context; + mCanLoadHint = HomeFragment.DEFAULT_CAN_LOAD_HINT; mPageInfos = new ArrayList(); - mPages = new EnumMap(Page.class); + mPages = new HashMap(); } @Override @@ -61,19 +53,23 @@ class HomeAdapter extends FragmentStatePagerAdapter { @Override public Fragment getItem(int position) { PageInfo info = mPageInfos.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), info.args); + return Fragment.instantiate(mContext, info.getClassName(), info.getArgs()); } @Override public CharSequence getPageTitle(int position) { - PageInfo info = mPageInfos.get(position); - return info.title.toUpperCase(); + if (mPageInfos.size() > 0) { + PageInfo info = mPageInfos.get(position); + return info.getTitle().toUpperCase(); + } + + return null; } @Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = (Fragment) super.instantiateItem(container, position); - mPages.put(mPageInfos.get(position).page, fragment); + mPages.put(mPageInfos.get(position).getId(), fragment); return fragment; } @@ -81,37 +77,17 @@ class HomeAdapter extends FragmentStatePagerAdapter { @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); - mPages.remove(mPageInfos.get(position).page); + mPages.remove(mPageInfos.get(position).getId()); } public void setOnAddPageListener(OnAddPageListener listener) { mAddPageListener = listener; } - public void addPage(Page page, Class clss, Bundle args, String title) { - addPage(-1, page, clss, args, title); - } - - public void addPage(int index, Page page, Class clss, Bundle args, String title) { - PageInfo info = new PageInfo(page, clss, args, title); - - if (index >= 0) { - mPageInfos.add(index, info); - } else { - mPageInfos.add(info); - } - - notifyDataSetChanged(); - - if (mAddPageListener != null) { - mAddPageListener.onAddPage(title); - } - } - public int getItemPosition(Page page) { for (int i = 0; i < mPageInfos.size(); i++) { - PageInfo info = mPageInfos.get(i); - if (info.page == page) { + final Page infoPage = mPageInfos.get(i).toPage(); + if (infoPage == page) { return i; } } @@ -121,14 +97,39 @@ class HomeAdapter extends FragmentStatePagerAdapter { public Page getPageAtPosition(int position) { PageInfo info = mPageInfos.get(position); - return info.page; + return info.toPage(); + } + + private void addPage(PageInfo info) { + mPageInfos.add(info); + + if (mAddPageListener != null) { + mAddPageListener.onAddPage(info.getTitle()); + } + } + + public void update(List pageEntries) { + mPages.clear(); + mPageInfos.clear(); + + if (pageEntries != null) { + for (PageEntry pageEntry : pageEntries) { + final PageInfo info = new PageInfo(pageEntry); + addPage(info); + } + } + + notifyDataSetChanged(); + } + + public boolean getCanLoadHint() { + return mCanLoadHint; } public void setCanLoadHint(boolean canLoadHint) { - // Update fragment arguments for future instances - for (PageInfo info : mPageInfos) { - info.args.putBoolean(HomePager.CAN_LOAD_ARG, canLoadHint); - } + // We cache the last hint value so that we can use it when + // creating new pages. See PageInfo.getArgs(). + mCanLoadHint = canLoadHint; // Enable/disable loading on all existing pages for (Fragment page : mPages.values()) { @@ -136,4 +137,49 @@ class HomeAdapter extends FragmentStatePagerAdapter { homePage.setCanLoadHint(canLoadHint); } } + + private final class PageInfo { + private final String mId; + private final PageEntry mPageEntry; + + PageInfo(PageEntry pageEntry) { + mId = pageEntry.getType() + "-" + pageEntry.getId(); + mPageEntry = pageEntry; + } + + public String getId() { + return mId; + } + + public String getTitle() { + return mPageEntry.getTitle(); + } + + public String getClassName() { + final PageType type = mPageEntry.getType(); + return type.getPageClass().getName(); + } + + public Bundle getArgs() { + final Bundle args = new Bundle(); + + args.putBoolean(HomePager.CAN_LOAD_ARG, mCanLoadHint); + + // Only list pages need the page entry + if (mPageEntry.getType() == PageType.LIST) { + args.putParcelable(HomePager.PAGE_ENTRY_ARG, mPageEntry); + } + + return args; + } + + public Page toPage() { + final PageType type = mPageEntry.getType(); + if (type == PageType.LIST) { + return null; + } + + return Page.valueOf(type); + } + } } diff --git a/mobile/android/base/home/HomeConfig.java b/mobile/android/base/home/HomeConfig.java new file mode 100644 index 000000000000..636fd617f7d8 --- /dev/null +++ b/mobile/android/base/home/HomeConfig.java @@ -0,0 +1,224 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.home.HomePager.Page; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.util.EnumSet; +import java.util.List; + +final class HomeConfig { + public static enum PageType implements Parcelable { + TOP_SITES("top_sites", TopSitesPage.class), + BOOKMARKS("bookmarks", BookmarksPage.class), + HISTORY("history", HistoryPage.class), + READING_LIST("reading_list", ReadingListPage.class), + LIST("list", ListPage.class); + + private final String mId; + private final Class mPageClass; + + PageType(String id, Class pageClass) { + mId = id; + mPageClass = pageClass; + } + + public static PageType valueOf(Page page) { + switch(page) { + case TOP_SITES: + return PageType.TOP_SITES; + + case BOOKMARKS: + return PageType.BOOKMARKS; + + case HISTORY: + return PageType.HISTORY; + + case READING_LIST: + return PageType.READING_LIST; + + default: + throw new IllegalArgumentException("Could not convert unrecognized Page"); + } + } + + public static PageType fromId(String id) { + if (id == null) { + throw new IllegalArgumentException("Could not convert null String to PageType"); + } + + for (PageType page : PageType.values()) { + if (TextUtils.equals(page.mId, id.toLowerCase())) { + return page; + } + } + + throw new IllegalArgumentException("Could not convert String id to PageType"); + } + + @Override + public String toString() { + return mId; + } + + public Class getPageClass() { + return mPageClass; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(ordinal()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PageType createFromParcel(final Parcel source) { + return PageType.values()[source.readInt()]; + } + + @Override + public PageType[] newArray(final int size) { + return new PageType[size]; + } + }; + } + + public static class PageEntry implements Parcelable { + private final PageType mType; + private final String mTitle; + private final String mId; + private final EnumSet mFlags; + + public enum Flags { + DEFAULT_PAGE + } + + @SuppressWarnings("unchecked") + public PageEntry(Parcel in) { + mType = (PageType) in.readParcelable(getClass().getClassLoader()); + mTitle = in.readString(); + mId = in.readString(); + mFlags = (EnumSet) in.readSerializable(); + } + + public PageEntry(PageType type, String title) { + this(type, title, EnumSet.noneOf(Flags.class)); + } + + public PageEntry(PageType type, String title, EnumSet flags) { + this(type, title, type.toString(), flags); + } + + public PageEntry(PageType type, String title, String id) { + this(type, title, id, EnumSet.noneOf(Flags.class)); + } + + public PageEntry(PageType type, String title, String id, EnumSet flags) { + if (type == null) { + throw new IllegalArgumentException("Can't create PageEntry with null type"); + } + mType = type; + + if (title == null) { + throw new IllegalArgumentException("Can't create PageEntry with null title"); + } + mTitle = title; + + if (id == null) { + throw new IllegalArgumentException("Can't create PageEntry with null id"); + } + mId = id; + + if (flags == null) { + throw new IllegalArgumentException("Can't create PageEntry with null flags"); + } + mFlags = flags; + } + + public PageType getType() { + return mType; + } + + public String getTitle() { + return mTitle; + } + + public String getId() { + return mId; + } + + public boolean isDefault() { + return mFlags.contains(Flags.DEFAULT_PAGE); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mType, 0); + dest.writeString(mTitle); + dest.writeString(mId); + dest.writeSerializable(mFlags); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PageEntry createFromParcel(final Parcel in) { + return new PageEntry(in); + } + + @Override + public PageEntry[] newArray(final int size) { + return new PageEntry[size]; + } + }; + } + + public interface OnChangeListener { + public void onChange(); + } + + public interface HomeConfigBackend { + public List load(); + public void save(List entries); + public void setOnChangeListener(OnChangeListener listener); + } + + private final HomeConfigBackend mBackend; + + public HomeConfig(HomeConfigBackend backend) { + mBackend = backend; + } + + public List load() { + return mBackend.load(); + } + + public void save(List entries) { + mBackend.save(entries); + } + + public void setOnChangeListener(OnChangeListener listener) { + mBackend.setOnChangeListener(listener); + } + + public static HomeConfig getDefault(Context context) { + return new HomeConfig(new HomeConfigMemBackend(context)); + } +} \ No newline at end of file diff --git a/mobile/android/base/home/HomeConfigLoader.java b/mobile/android/base/home/HomeConfigLoader.java new file mode 100644 index 000000000000..df27c8c7e961 --- /dev/null +++ b/mobile/android/base/home/HomeConfigLoader.java @@ -0,0 +1,83 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.home.HomeConfig.PageEntry; +import org.mozilla.gecko.home.HomeConfig.OnChangeListener; + +import android.content.Context; +import android.support.v4.content.AsyncTaskLoader; + +import java.util.List; + +public class HomeConfigLoader extends AsyncTaskLoader> { + private final HomeConfig mConfig; + private List mPageEntries; + + public HomeConfigLoader(Context context, HomeConfig homeConfig) { + super(context); + mConfig = homeConfig; + } + + @Override + public List loadInBackground() { + return mConfig.load(); + } + + @Override + public void deliverResult(List pageEntries) { + if (isReset()) { + mPageEntries = null; + return; + } + + mPageEntries = pageEntries; + mConfig.setOnChangeListener(new ForceLoadChangeListener()); + + if (isStarted()) { + super.deliverResult(pageEntries); + } + } + + @Override + protected void onStartLoading() { + if (mPageEntries != null) { + deliverResult(mPageEntries); + } + + if (takeContentChanged() || mPageEntries == null) { + forceLoad(); + } + } + + @Override + protected void onStopLoading() { + cancelLoad(); + } + + @Override + public void onCanceled(List pageEntries) { + mPageEntries = null; + } + + @Override + protected void onReset() { + super.onReset(); + + // Ensure the loader is stopped. + onStopLoading(); + + mPageEntries = null; + mConfig.setOnChangeListener(null); + } + + private class ForceLoadChangeListener implements OnChangeListener { + @Override + public void onChange() { + onContentChanged(); + } + } +} diff --git a/mobile/android/base/home/HomeConfigMemBackend.java b/mobile/android/base/home/HomeConfigMemBackend.java new file mode 100644 index 000000000000..03d038b1f2c5 --- /dev/null +++ b/mobile/android/base/home/HomeConfigMemBackend.java @@ -0,0 +1,67 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.R; +import org.mozilla.gecko.home.HomeConfig.HomeConfigBackend; +import org.mozilla.gecko.home.HomeConfig.OnChangeListener; +import org.mozilla.gecko.home.HomeConfig.PageEntry; +import org.mozilla.gecko.home.HomeConfig.PageType; +import org.mozilla.gecko.util.HardwareUtils; + +import android.content.Context; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +class HomeConfigMemBackend implements HomeConfigBackend { + private final Context mContext; + + public HomeConfigMemBackend(Context context) { + mContext = context; + } + + public List load() { + final ArrayList pageEntries = new ArrayList(); + + pageEntries.add(new PageEntry(PageType.TOP_SITES, + mContext.getString(R.string.home_top_sites_title), + EnumSet.of(PageEntry.Flags.DEFAULT_PAGE))); + + pageEntries.add(new PageEntry(PageType.BOOKMARKS, + mContext.getString(R.string.bookmarks_title))); + + // We disable reader mode support on low memory devices. Hence the + // reading list page should not show up on such devices. + if (!HardwareUtils.isLowMemoryPlatform()) { + pageEntries.add(new PageEntry(PageType.READING_LIST, + mContext.getString(R.string.reading_list_title))); + } + + final PageEntry historyEntry = new PageEntry(PageType.HISTORY, + mContext.getString(R.string.home_history_title)); + + // On tablets, the history page is the last. + // On phones, the history page is the first one. + if (HardwareUtils.isTablet()) { + pageEntries.add(historyEntry); + } else { + pageEntries.add(0, historyEntry); + } + + return Collections.unmodifiableList(pageEntries); + } + + public void save(List entries) { + // This is a static backend, do nothing. + } + + public void setOnChangeListener(OnChangeListener listener) { + // This is a static backend, do nothing. + } +} \ No newline at end of file diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 66253b1be0f6..3849bf4aa38d 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -9,6 +9,8 @@ import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; import org.mozilla.gecko.home.HomeAdapter.OnAddPageListener; +import org.mozilla.gecko.home.HomeConfig.PageEntry; +import org.mozilla.gecko.home.HomeConfig.PageType; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.HardwareUtils; @@ -17,6 +19,9 @@ import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; +import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; import android.support.v4.view.ViewPager; import android.view.ViewGroup.LayoutParams; import android.util.AttributeSet; @@ -25,22 +30,50 @@ import android.view.ViewGroup; import android.view.View; import java.util.EnumSet; +import java.util.List; public class HomePager extends ViewPager { + private static final int LOADER_ID_CONFIG = 0; + private final Context mContext; private volatile boolean mLoaded; private Decor mDecor; + private View mTabStrip; private final OnAddPageListener mAddPageListener; + private final HomeConfig mConfig; + private ConfigLoaderCallbacks mConfigLoaderCallbacks; + + private Page mInitialPage; + // List of pages in order. @RobocopTarget public enum Page { HISTORY, TOP_SITES, BOOKMARKS, - READING_LIST + READING_LIST; + + static Page valueOf(PageType page) { + switch(page) { + case TOP_SITES: + return Page.TOP_SITES; + + case BOOKMARKS: + return Page.BOOKMARKS; + + case HISTORY: + return Page.HISTORY; + + case READING_LIST: + return Page.READING_LIST; + + default: + throw new IllegalArgumentException("Could not convert unrecognized PageType"); + } + } } // This is mostly used by UI tests to easily fetch @@ -81,6 +114,7 @@ public class HomePager extends ViewPager { } static final String CAN_LOAD_ARG = "canLoad"; + static final String PAGE_ENTRY_ARG = "pageEntry"; public HomePager(Context context) { this(context, null); @@ -90,6 +124,9 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; + mConfig = HomeConfig.getDefault(mContext); + mConfigLoaderCallbacks = new ConfigLoaderCallbacks(); + mAddPageListener = new OnAddPageListener() { @Override public void onAddPage(String title) { @@ -116,6 +153,7 @@ public class HomePager extends ViewPager { if (child instanceof Decor) { ((ViewPager.LayoutParams) params).isDecor = true; mDecor = (Decor) child; + mTabStrip = child; mDecor.setOnTitleClickListener(new OnTitleClickListener() { @Override @@ -138,16 +176,18 @@ public class HomePager extends ViewPager { @Override public void onPageScrollStateChanged(int state) { } }); + } else if (child instanceof HomePagerTabStrip) { + mTabStrip = child; } super.addView(child, index, params); } - public void redisplay(FragmentManager fm) { + public void redisplay(LoaderManager lm, FragmentManager fm) { final HomeAdapter adapter = (HomeAdapter) getAdapter(); - final Page currentPage = adapter.getPageAtPosition(getCurrentItem()); - show(fm, currentPage, null); + Page currentPage = adapter.getPageAtPosition(getCurrentItem()); + show(lm, fm, currentPage, null); } /** @@ -155,44 +195,29 @@ public class HomePager extends ViewPager { * * @param fm FragmentManager for the adapter */ - public void show(FragmentManager fm, Page page, PropertyAnimator animator) { + public void show(LoaderManager lm, FragmentManager fm, Page page, PropertyAnimator animator) { mLoaded = true; - - if (mDecor != null) { - mDecor.removeAllPagerViews(); - } - - final HomeAdapter adapter = new HomeAdapter(mContext, fm); - adapter.setOnAddPageListener(mAddPageListener); + mInitialPage = page; // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); - adapter.addPage(Page.TOP_SITES, TopSitesPage.class, new Bundle(), - getContext().getString(R.string.home_top_sites_title)); - adapter.addPage(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), - getContext().getString(R.string.bookmarks_title)); - - // We disable reader mode support on low memory devices. Hence the - // reading list page should not show up on such devices. - if (!HardwareUtils.isLowMemoryPlatform()) { - adapter.addPage(Page.READING_LIST, ReadingListPage.class, new Bundle(), - getContext().getString(R.string.reading_list_title)); - } - - // On phones, the history page is the first one. On tablets, the - // history page is the last. - adapter.addPage(HardwareUtils.isTablet() ? -1 : 0, - Page.HISTORY, HistoryPage.class, new Bundle(), - getContext().getString(R.string.home_history_title)); - + final HomeAdapter adapter = new HomeAdapter(mContext, fm); + adapter.setOnAddPageListener(mAddPageListener); adapter.setCanLoadHint(!shouldAnimate); - setAdapter(adapter); - setCurrentItem(adapter.getItemPosition(page), false); setVisibility(VISIBLE); + // Don't show the tabs strip until we have the + // list of pages in place. + if (mTabStrip != null) { + mTabStrip.setVisibility(View.INVISIBLE); + } + + // Load list of pages from configuration + lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks); + if (shouldAnimate) { animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { @Override @@ -254,4 +279,69 @@ public class HomePager extends ViewPager { return super.onInterceptTouchEvent(event); } + + private void updateUiFromPageEntries(List pageEntries) { + // We only care about the adapter if HomePager is currently + // loaded, which means it's visible in the activity. + if (!mLoaded) { + return; + } + + if (mDecor != null) { + mDecor.removeAllPagerViews(); + } + + final HomeAdapter adapter = (HomeAdapter) getAdapter(); + + // Disable loading until the final current item is defined + // after loading the page entries. This is to stop any temporary + // active item from loading. + boolean originalCanLoadHint = adapter.getCanLoadHint(); + adapter.setCanLoadHint(false); + + // Update the adapter with the new page entries + adapter.update(pageEntries); + + final int count = (pageEntries != null ? pageEntries.size() : 0); + + if (mTabStrip != null) { + mTabStrip.setVisibility(count > 0 ? View.VISIBLE : View.INVISIBLE); + } + + // Use the default page as defined in the HomePager's configuration + // if the initial page wasn't explicitly set by the show() caller. + if (mInitialPage != null) { + setCurrentItem(adapter.getItemPosition(mInitialPage), false); + mInitialPage = null; + } else { + for (int i = 0; i < count; i++) { + final PageEntry pageEntry = pageEntries.get(i); + if (pageEntry.isDefault()) { + setCurrentItem(i, false); + break; + } + } + } + + // Restore canLoadHint now that we have the final + // state in HomePager. + adapter.setCanLoadHint(originalCanLoadHint); + } + + private class ConfigLoaderCallbacks implements LoaderCallbacks> { + @Override + public Loader> onCreateLoader(int id, Bundle args) { + return new HomeConfigLoader(mContext, mConfig); + } + + @Override + public void onLoadFinished(Loader> loader, List pageEntries) { + updateUiFromPageEntries(pageEntries); + } + + @Override + public void onLoaderReset(Loader> loader) { + updateUiFromPageEntries(null); + } + } } diff --git a/mobile/android/base/home/ListPage.java b/mobile/android/base/home/ListPage.java new file mode 100644 index 000000000000..f3c911095b84 --- /dev/null +++ b/mobile/android/base/home/ListPage.java @@ -0,0 +1,185 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.R; +import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; +import org.mozilla.gecko.home.HomeConfig.PageEntry; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.database.Cursor; +import android.os.Bundle; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +import java.util.EnumSet; + +/** + * Fragment that displays custom lists. + */ +public class ListPage extends HomeFragment { + // Cursor loader ID for the lists + private static final int LOADER_ID_LIST = 0; + + // The page entry associated with this page + private PageEntry mPageEntry; + + // Adapter for the list + private HomeListAdapter mAdapter; + + // The view shown by the fragment + private ListView mList; + + // Callbacks used for the list loader + private CursorLoaderCallbacks mCursorLoaderCallbacks; + + // On URL open listener + private OnUrlOpenListener mUrlOpenListener; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + mUrlOpenListener = (OnUrlOpenListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement HomePager.OnUrlOpenListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + + mUrlOpenListener = null; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Bundle args = getArguments(); + if (args != null) { + mPageEntry = (PageEntry) args.getParcelable(HomePager.PAGE_ENTRY_ARG); + } + + if (mPageEntry == null) { + throw new IllegalStateException("Can't create a ListPage without a PageEntry"); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + mList = new HomeListView(getActivity()); + return mList; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + registerForContextMenu(mList); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mList = null; + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + // Detach and reattach the fragment as the layout changes. + if (isVisible()) { + getFragmentManager().beginTransaction() + .detach(this) + .attach(this) + .commitAllowingStateLoss(); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAdapter = new HomeListAdapter(getActivity(), null); + mList.setAdapter(mAdapter); + + // Create callbacks before the initial loader is started. + mCursorLoaderCallbacks = new CursorLoaderCallbacks(); + loadIfVisible(); + } + + @Override + protected void load() { + getLoaderManager().initLoader(LOADER_ID_LIST, null, mCursorLoaderCallbacks); + } + + /** + * Cursor loader for the lists. + */ + private static class HomeListLoader extends SimpleCursorLoader { + public HomeListLoader(Context context) { + super(context); + } + + @Override + public Cursor loadCursor() { + // Do nothing + return null; + } + } + + /** + * Cursor adapter for the list. + */ + private class HomeListAdapter extends CursorAdapter { + public HomeListAdapter(Context context, Cursor cursor) { + super(context, cursor, 0); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + final TwoLinePageRow row = (TwoLinePageRow) view; + row.updateFromCursor(cursor); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_item_row, parent, false); + } + } + + /** + * LoaderCallbacks implementation that interacts with the LoaderManager. + */ + private class CursorLoaderCallbacks implements LoaderCallbacks { + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new HomeListLoader(getActivity()); + } + + @Override + public void onLoadFinished(Loader loader, Cursor c) { + mAdapter.swapCursor(c); + } + + @Override + public void onLoaderReset(Loader loader) { + mAdapter.swapCursor(null); + } + } +} diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 476cb6c4855d..2bb5769946ea 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -210,11 +210,15 @@ gbjar.sources += [ 'home/HistoryPage.java', 'home/HomeAdapter.java', 'home/HomeBanner.java', + 'home/HomeConfig.java', + 'home/HomeConfigLoader.java', + 'home/HomeConfigMemBackend.java', 'home/HomeFragment.java', 'home/HomeListView.java', 'home/HomePager.java', 'home/HomePagerTabStrip.java', 'home/LastTabsPage.java', + 'home/ListPage.java', 'home/MostRecentPage.java', 'home/MultiTypeCursorAdapter.java', 'home/PinSiteDialog.java', From 2430f71df6b93e17580319f380181c9f81d2d26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Thu, 12 Dec 2013 12:38:34 +0100 Subject: [PATCH 140/459] Backed out changeset 9f7da731bf06 (CLOSED TREE) --- browser/components/sessionstore/src/SessionStore.jsm | 4 +--- browser/modules/RecentWindow.jsm | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm index 20055129ae6c..8cd1649d111d 100644 --- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -86,11 +86,9 @@ const BROWSER_EVENTS = [ // The number of milliseconds in a day const MS_PER_DAY = 1000.0 * 60.0 * 60.0 * 24.0; -#ifdef XP_UNIX -#ifndef XP_MACOSX +#ifndef XP_WIN #define BROKEN_WM_Z_ORDER #endif -#endif Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); diff --git a/browser/modules/RecentWindow.jsm b/browser/modules/RecentWindow.jsm index c328b6e44d50..0018b502c70e 100644 --- a/browser/modules/RecentWindow.jsm +++ b/browser/modules/RecentWindow.jsm @@ -11,11 +11,9 @@ const Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); -#ifdef XP_UNIX -#ifndef XP_MACOSX +#ifndef XP_WIN #define BROKEN_WM_Z_ORDER #endif -#endif this.RecentWindow = { /* From 56e72953a6d338b0956f66061e540322d93423c0 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 12 Dec 2013 12:44:11 +0100 Subject: [PATCH 141/459] Backed out changeset 64376a7a08df (bug 942231) for Android Bustage on a CLOSED TREE --- mobile/android/base/BrowserApp.java | 14 +- mobile/android/base/Tab.java | 5 +- mobile/android/base/home/HomeAdapter.java | 144 ++++------- mobile/android/base/home/HomeConfig.java | 224 ------------------ .../android/base/home/HomeConfigLoader.java | 83 ------- .../base/home/HomeConfigMemBackend.java | 67 ------ mobile/android/base/home/HomePager.java | 156 +++--------- mobile/android/base/home/ListPage.java | 185 --------------- mobile/android/base/moz.build | 4 - 9 files changed, 87 insertions(+), 795 deletions(-) delete mode 100644 mobile/android/base/home/HomeConfig.java delete mode 100644 mobile/android/base/home/HomeConfigLoader.java delete mode 100644 mobile/android/base/home/HomeConfigMemBackend.java delete mode 100644 mobile/android/base/home/ListPage.java diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 3fcfa90c5427..87d29f055f6b 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1467,7 +1467,7 @@ abstract public class BrowserApp extends GeckoApp animator.setUseHardwareLayer(false); mBrowserToolbar.startEditing(url, animator); - showHomePagerWithAnimator(animator); + showHomePagerWithAnimator(HomePager.Page.TOP_SITES, animator); animator.start(); } @@ -1623,7 +1623,7 @@ abstract public class BrowserApp extends GeckoApp super.onLocaleReady(locale); if (mHomePager != null) { // Blow it away and rebuild it with the right strings. - mHomePager.redisplay(getSupportLoaderManager(), getSupportFragmentManager()); + mHomePager.redisplay(getSupportFragmentManager()); } if (mMenu != null) { @@ -1636,12 +1636,6 @@ abstract public class BrowserApp extends GeckoApp showHomePagerWithAnimator(page, null); } - private void showHomePagerWithAnimator(PropertyAnimator animator) { - // Passing null here means the default page will be defined - // by the HomePager's configuration. - showHomePagerWithAnimator(null, animator); - } - private void showHomePagerWithAnimator(HomePager.Page page, PropertyAnimator animator) { if (isHomePagerVisible()) { return; @@ -1661,9 +1655,7 @@ abstract public class BrowserApp extends GeckoApp mHomePager = (HomePager) homePagerStub.inflate(); } - mHomePager.show(getSupportLoaderManager(), - getSupportFragmentManager(), - page, animator); + mHomePager.show(getSupportFragmentManager(), page, animator); // Hide the web content so it cannot be focused by screen readers. hideWebContentOnPropertyAnimationEnd(animator); diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 0f1a02d10a7c..09171f428372 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -95,7 +95,7 @@ public class Tab { mUserSearch = ""; mExternal = external; mParentId = parentId; - mAboutHomePage = null; + mAboutHomePage = HomePager.Page.TOP_SITES; mTitle = title == null ? "" : title; mFavicon = null; mFaviconUrl = null; @@ -155,6 +155,7 @@ public class Tab { mAboutHomePage = page; } + // may be null if user-entered query hasn't yet been resolved to a URI public synchronized String getURL() { return mUrl; @@ -659,8 +660,6 @@ public class Tab { final String homePage = message.getString("aboutHomePage"); if (!TextUtils.isEmpty(homePage)) { setAboutHomePage(HomePager.Page.valueOf(homePage)); - } else { - setAboutHomePage(null); } Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.LOCATION_CHANGE, oldUrl); diff --git a/mobile/android/base/home/HomeAdapter.java b/mobile/android/base/home/HomeAdapter.java index 7cbdc06283a2..2d424face292 100644 --- a/mobile/android/base/home/HomeAdapter.java +++ b/mobile/android/base/home/HomeAdapter.java @@ -5,8 +5,6 @@ package org.mozilla.gecko.home; -import org.mozilla.gecko.home.HomeConfig.PageEntry; -import org.mozilla.gecko.home.HomeConfig.PageType; import org.mozilla.gecko.home.HomePager; import org.mozilla.gecko.home.HomePager.Page; @@ -18,16 +16,13 @@ import android.support.v4.app.FragmentStatePagerAdapter; import android.view.ViewGroup; import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import java.util.EnumMap; class HomeAdapter extends FragmentStatePagerAdapter { private final Context mContext; private final ArrayList mPageInfos; - private final HashMap mPages; - - private boolean mCanLoadHint; + private final EnumMap mPages; private OnAddPageListener mAddPageListener; @@ -35,14 +30,27 @@ class HomeAdapter extends FragmentStatePagerAdapter { public void onAddPage(String title); } + final class PageInfo { + private final Page page; + private final Class clss; + private final Bundle args; + private final String title; + + PageInfo(Page page, Class clss, Bundle args, String title) { + this.page = page; + this.clss = clss; + this.args = args; + this.title = title; + } + } + public HomeAdapter(Context context, FragmentManager fm) { super(fm); mContext = context; - mCanLoadHint = HomeFragment.DEFAULT_CAN_LOAD_HINT; mPageInfos = new ArrayList(); - mPages = new HashMap(); + mPages = new EnumMap(Page.class); } @Override @@ -53,23 +61,19 @@ class HomeAdapter extends FragmentStatePagerAdapter { @Override public Fragment getItem(int position) { PageInfo info = mPageInfos.get(position); - return Fragment.instantiate(mContext, info.getClassName(), info.getArgs()); + return Fragment.instantiate(mContext, info.clss.getName(), info.args); } @Override public CharSequence getPageTitle(int position) { - if (mPageInfos.size() > 0) { - PageInfo info = mPageInfos.get(position); - return info.getTitle().toUpperCase(); - } - - return null; + PageInfo info = mPageInfos.get(position); + return info.title.toUpperCase(); } @Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = (Fragment) super.instantiateItem(container, position); - mPages.put(mPageInfos.get(position).getId(), fragment); + mPages.put(mPageInfos.get(position).page, fragment); return fragment; } @@ -77,17 +81,37 @@ class HomeAdapter extends FragmentStatePagerAdapter { @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); - mPages.remove(mPageInfos.get(position).getId()); + mPages.remove(mPageInfos.get(position).page); } public void setOnAddPageListener(OnAddPageListener listener) { mAddPageListener = listener; } + public void addPage(Page page, Class clss, Bundle args, String title) { + addPage(-1, page, clss, args, title); + } + + public void addPage(int index, Page page, Class clss, Bundle args, String title) { + PageInfo info = new PageInfo(page, clss, args, title); + + if (index >= 0) { + mPageInfos.add(index, info); + } else { + mPageInfos.add(info); + } + + notifyDataSetChanged(); + + if (mAddPageListener != null) { + mAddPageListener.onAddPage(title); + } + } + public int getItemPosition(Page page) { for (int i = 0; i < mPageInfos.size(); i++) { - final Page infoPage = mPageInfos.get(i).toPage(); - if (infoPage == page) { + PageInfo info = mPageInfos.get(i); + if (info.page == page) { return i; } } @@ -97,39 +121,14 @@ class HomeAdapter extends FragmentStatePagerAdapter { public Page getPageAtPosition(int position) { PageInfo info = mPageInfos.get(position); - return info.toPage(); - } - - private void addPage(PageInfo info) { - mPageInfos.add(info); - - if (mAddPageListener != null) { - mAddPageListener.onAddPage(info.getTitle()); - } - } - - public void update(List pageEntries) { - mPages.clear(); - mPageInfos.clear(); - - if (pageEntries != null) { - for (PageEntry pageEntry : pageEntries) { - final PageInfo info = new PageInfo(pageEntry); - addPage(info); - } - } - - notifyDataSetChanged(); - } - - public boolean getCanLoadHint() { - return mCanLoadHint; + return info.page; } public void setCanLoadHint(boolean canLoadHint) { - // We cache the last hint value so that we can use it when - // creating new pages. See PageInfo.getArgs(). - mCanLoadHint = canLoadHint; + // Update fragment arguments for future instances + for (PageInfo info : mPageInfos) { + info.args.putBoolean(HomePager.CAN_LOAD_ARG, canLoadHint); + } // Enable/disable loading on all existing pages for (Fragment page : mPages.values()) { @@ -137,49 +136,4 @@ class HomeAdapter extends FragmentStatePagerAdapter { homePage.setCanLoadHint(canLoadHint); } } - - private final class PageInfo { - private final String mId; - private final PageEntry mPageEntry; - - PageInfo(PageEntry pageEntry) { - mId = pageEntry.getType() + "-" + pageEntry.getId(); - mPageEntry = pageEntry; - } - - public String getId() { - return mId; - } - - public String getTitle() { - return mPageEntry.getTitle(); - } - - public String getClassName() { - final PageType type = mPageEntry.getType(); - return type.getPageClass().getName(); - } - - public Bundle getArgs() { - final Bundle args = new Bundle(); - - args.putBoolean(HomePager.CAN_LOAD_ARG, mCanLoadHint); - - // Only list pages need the page entry - if (mPageEntry.getType() == PageType.LIST) { - args.putParcelable(HomePager.PAGE_ENTRY_ARG, mPageEntry); - } - - return args; - } - - public Page toPage() { - final PageType type = mPageEntry.getType(); - if (type == PageType.LIST) { - return null; - } - - return Page.valueOf(type); - } - } } diff --git a/mobile/android/base/home/HomeConfig.java b/mobile/android/base/home/HomeConfig.java deleted file mode 100644 index 636fd617f7d8..000000000000 --- a/mobile/android/base/home/HomeConfig.java +++ /dev/null @@ -1,224 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.home; - -import org.mozilla.gecko.home.HomePager.Page; - -import android.content.Context; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; - -import java.util.EnumSet; -import java.util.List; - -final class HomeConfig { - public static enum PageType implements Parcelable { - TOP_SITES("top_sites", TopSitesPage.class), - BOOKMARKS("bookmarks", BookmarksPage.class), - HISTORY("history", HistoryPage.class), - READING_LIST("reading_list", ReadingListPage.class), - LIST("list", ListPage.class); - - private final String mId; - private final Class mPageClass; - - PageType(String id, Class pageClass) { - mId = id; - mPageClass = pageClass; - } - - public static PageType valueOf(Page page) { - switch(page) { - case TOP_SITES: - return PageType.TOP_SITES; - - case BOOKMARKS: - return PageType.BOOKMARKS; - - case HISTORY: - return PageType.HISTORY; - - case READING_LIST: - return PageType.READING_LIST; - - default: - throw new IllegalArgumentException("Could not convert unrecognized Page"); - } - } - - public static PageType fromId(String id) { - if (id == null) { - throw new IllegalArgumentException("Could not convert null String to PageType"); - } - - for (PageType page : PageType.values()) { - if (TextUtils.equals(page.mId, id.toLowerCase())) { - return page; - } - } - - throw new IllegalArgumentException("Could not convert String id to PageType"); - } - - @Override - public String toString() { - return mId; - } - - public Class getPageClass() { - return mPageClass; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(ordinal()); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PageType createFromParcel(final Parcel source) { - return PageType.values()[source.readInt()]; - } - - @Override - public PageType[] newArray(final int size) { - return new PageType[size]; - } - }; - } - - public static class PageEntry implements Parcelable { - private final PageType mType; - private final String mTitle; - private final String mId; - private final EnumSet mFlags; - - public enum Flags { - DEFAULT_PAGE - } - - @SuppressWarnings("unchecked") - public PageEntry(Parcel in) { - mType = (PageType) in.readParcelable(getClass().getClassLoader()); - mTitle = in.readString(); - mId = in.readString(); - mFlags = (EnumSet) in.readSerializable(); - } - - public PageEntry(PageType type, String title) { - this(type, title, EnumSet.noneOf(Flags.class)); - } - - public PageEntry(PageType type, String title, EnumSet flags) { - this(type, title, type.toString(), flags); - } - - public PageEntry(PageType type, String title, String id) { - this(type, title, id, EnumSet.noneOf(Flags.class)); - } - - public PageEntry(PageType type, String title, String id, EnumSet flags) { - if (type == null) { - throw new IllegalArgumentException("Can't create PageEntry with null type"); - } - mType = type; - - if (title == null) { - throw new IllegalArgumentException("Can't create PageEntry with null title"); - } - mTitle = title; - - if (id == null) { - throw new IllegalArgumentException("Can't create PageEntry with null id"); - } - mId = id; - - if (flags == null) { - throw new IllegalArgumentException("Can't create PageEntry with null flags"); - } - mFlags = flags; - } - - public PageType getType() { - return mType; - } - - public String getTitle() { - return mTitle; - } - - public String getId() { - return mId; - } - - public boolean isDefault() { - return mFlags.contains(Flags.DEFAULT_PAGE); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(mType, 0); - dest.writeString(mTitle); - dest.writeString(mId); - dest.writeSerializable(mFlags); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PageEntry createFromParcel(final Parcel in) { - return new PageEntry(in); - } - - @Override - public PageEntry[] newArray(final int size) { - return new PageEntry[size]; - } - }; - } - - public interface OnChangeListener { - public void onChange(); - } - - public interface HomeConfigBackend { - public List load(); - public void save(List entries); - public void setOnChangeListener(OnChangeListener listener); - } - - private final HomeConfigBackend mBackend; - - public HomeConfig(HomeConfigBackend backend) { - mBackend = backend; - } - - public List load() { - return mBackend.load(); - } - - public void save(List entries) { - mBackend.save(entries); - } - - public void setOnChangeListener(OnChangeListener listener) { - mBackend.setOnChangeListener(listener); - } - - public static HomeConfig getDefault(Context context) { - return new HomeConfig(new HomeConfigMemBackend(context)); - } -} \ No newline at end of file diff --git a/mobile/android/base/home/HomeConfigLoader.java b/mobile/android/base/home/HomeConfigLoader.java deleted file mode 100644 index df27c8c7e961..000000000000 --- a/mobile/android/base/home/HomeConfigLoader.java +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.home; - -import org.mozilla.gecko.home.HomeConfig.PageEntry; -import org.mozilla.gecko.home.HomeConfig.OnChangeListener; - -import android.content.Context; -import android.support.v4.content.AsyncTaskLoader; - -import java.util.List; - -public class HomeConfigLoader extends AsyncTaskLoader> { - private final HomeConfig mConfig; - private List mPageEntries; - - public HomeConfigLoader(Context context, HomeConfig homeConfig) { - super(context); - mConfig = homeConfig; - } - - @Override - public List loadInBackground() { - return mConfig.load(); - } - - @Override - public void deliverResult(List pageEntries) { - if (isReset()) { - mPageEntries = null; - return; - } - - mPageEntries = pageEntries; - mConfig.setOnChangeListener(new ForceLoadChangeListener()); - - if (isStarted()) { - super.deliverResult(pageEntries); - } - } - - @Override - protected void onStartLoading() { - if (mPageEntries != null) { - deliverResult(mPageEntries); - } - - if (takeContentChanged() || mPageEntries == null) { - forceLoad(); - } - } - - @Override - protected void onStopLoading() { - cancelLoad(); - } - - @Override - public void onCanceled(List pageEntries) { - mPageEntries = null; - } - - @Override - protected void onReset() { - super.onReset(); - - // Ensure the loader is stopped. - onStopLoading(); - - mPageEntries = null; - mConfig.setOnChangeListener(null); - } - - private class ForceLoadChangeListener implements OnChangeListener { - @Override - public void onChange() { - onContentChanged(); - } - } -} diff --git a/mobile/android/base/home/HomeConfigMemBackend.java b/mobile/android/base/home/HomeConfigMemBackend.java deleted file mode 100644 index 03d038b1f2c5..000000000000 --- a/mobile/android/base/home/HomeConfigMemBackend.java +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.home; - -import org.mozilla.gecko.R; -import org.mozilla.gecko.home.HomeConfig.HomeConfigBackend; -import org.mozilla.gecko.home.HomeConfig.OnChangeListener; -import org.mozilla.gecko.home.HomeConfig.PageEntry; -import org.mozilla.gecko.home.HomeConfig.PageType; -import org.mozilla.gecko.util.HardwareUtils; - -import android.content.Context; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; - -class HomeConfigMemBackend implements HomeConfigBackend { - private final Context mContext; - - public HomeConfigMemBackend(Context context) { - mContext = context; - } - - public List load() { - final ArrayList pageEntries = new ArrayList(); - - pageEntries.add(new PageEntry(PageType.TOP_SITES, - mContext.getString(R.string.home_top_sites_title), - EnumSet.of(PageEntry.Flags.DEFAULT_PAGE))); - - pageEntries.add(new PageEntry(PageType.BOOKMARKS, - mContext.getString(R.string.bookmarks_title))); - - // We disable reader mode support on low memory devices. Hence the - // reading list page should not show up on such devices. - if (!HardwareUtils.isLowMemoryPlatform()) { - pageEntries.add(new PageEntry(PageType.READING_LIST, - mContext.getString(R.string.reading_list_title))); - } - - final PageEntry historyEntry = new PageEntry(PageType.HISTORY, - mContext.getString(R.string.home_history_title)); - - // On tablets, the history page is the last. - // On phones, the history page is the first one. - if (HardwareUtils.isTablet()) { - pageEntries.add(historyEntry); - } else { - pageEntries.add(0, historyEntry); - } - - return Collections.unmodifiableList(pageEntries); - } - - public void save(List entries) { - // This is a static backend, do nothing. - } - - public void setOnChangeListener(OnChangeListener listener) { - // This is a static backend, do nothing. - } -} \ No newline at end of file diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 3849bf4aa38d..66253b1be0f6 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -9,8 +9,6 @@ import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; import org.mozilla.gecko.home.HomeAdapter.OnAddPageListener; -import org.mozilla.gecko.home.HomeConfig.PageEntry; -import org.mozilla.gecko.home.HomeConfig.PageType; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.HardwareUtils; @@ -19,9 +17,6 @@ import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.app.LoaderManager; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.Loader; import android.support.v4.view.ViewPager; import android.view.ViewGroup.LayoutParams; import android.util.AttributeSet; @@ -30,50 +25,22 @@ import android.view.ViewGroup; import android.view.View; import java.util.EnumSet; -import java.util.List; public class HomePager extends ViewPager { - private static final int LOADER_ID_CONFIG = 0; - private final Context mContext; private volatile boolean mLoaded; private Decor mDecor; - private View mTabStrip; private final OnAddPageListener mAddPageListener; - private final HomeConfig mConfig; - private ConfigLoaderCallbacks mConfigLoaderCallbacks; - - private Page mInitialPage; - // List of pages in order. @RobocopTarget public enum Page { HISTORY, TOP_SITES, BOOKMARKS, - READING_LIST; - - static Page valueOf(PageType page) { - switch(page) { - case TOP_SITES: - return Page.TOP_SITES; - - case BOOKMARKS: - return Page.BOOKMARKS; - - case HISTORY: - return Page.HISTORY; - - case READING_LIST: - return Page.READING_LIST; - - default: - throw new IllegalArgumentException("Could not convert unrecognized PageType"); - } - } + READING_LIST } // This is mostly used by UI tests to easily fetch @@ -114,7 +81,6 @@ public class HomePager extends ViewPager { } static final String CAN_LOAD_ARG = "canLoad"; - static final String PAGE_ENTRY_ARG = "pageEntry"; public HomePager(Context context) { this(context, null); @@ -124,9 +90,6 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; - mConfig = HomeConfig.getDefault(mContext); - mConfigLoaderCallbacks = new ConfigLoaderCallbacks(); - mAddPageListener = new OnAddPageListener() { @Override public void onAddPage(String title) { @@ -153,7 +116,6 @@ public class HomePager extends ViewPager { if (child instanceof Decor) { ((ViewPager.LayoutParams) params).isDecor = true; mDecor = (Decor) child; - mTabStrip = child; mDecor.setOnTitleClickListener(new OnTitleClickListener() { @Override @@ -176,18 +138,16 @@ public class HomePager extends ViewPager { @Override public void onPageScrollStateChanged(int state) { } }); - } else if (child instanceof HomePagerTabStrip) { - mTabStrip = child; } super.addView(child, index, params); } - public void redisplay(LoaderManager lm, FragmentManager fm) { + public void redisplay(FragmentManager fm) { final HomeAdapter adapter = (HomeAdapter) getAdapter(); + final Page currentPage = adapter.getPageAtPosition(getCurrentItem()); - Page currentPage = adapter.getPageAtPosition(getCurrentItem()); - show(lm, fm, currentPage, null); + show(fm, currentPage, null); } /** @@ -195,28 +155,43 @@ public class HomePager extends ViewPager { * * @param fm FragmentManager for the adapter */ - public void show(LoaderManager lm, FragmentManager fm, Page page, PropertyAnimator animator) { + public void show(FragmentManager fm, Page page, PropertyAnimator animator) { mLoaded = true; - mInitialPage = page; + + if (mDecor != null) { + mDecor.removeAllPagerViews(); + } + + final HomeAdapter adapter = new HomeAdapter(mContext, fm); + adapter.setOnAddPageListener(mAddPageListener); // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); - final HomeAdapter adapter = new HomeAdapter(mContext, fm); - adapter.setOnAddPageListener(mAddPageListener); - adapter.setCanLoadHint(!shouldAnimate); - setAdapter(adapter); + adapter.addPage(Page.TOP_SITES, TopSitesPage.class, new Bundle(), + getContext().getString(R.string.home_top_sites_title)); + adapter.addPage(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), + getContext().getString(R.string.bookmarks_title)); - setVisibility(VISIBLE); - - // Don't show the tabs strip until we have the - // list of pages in place. - if (mTabStrip != null) { - mTabStrip.setVisibility(View.INVISIBLE); + // We disable reader mode support on low memory devices. Hence the + // reading list page should not show up on such devices. + if (!HardwareUtils.isLowMemoryPlatform()) { + adapter.addPage(Page.READING_LIST, ReadingListPage.class, new Bundle(), + getContext().getString(R.string.reading_list_title)); } - // Load list of pages from configuration - lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks); + // On phones, the history page is the first one. On tablets, the + // history page is the last. + adapter.addPage(HardwareUtils.isTablet() ? -1 : 0, + Page.HISTORY, HistoryPage.class, new Bundle(), + getContext().getString(R.string.home_history_title)); + + adapter.setCanLoadHint(!shouldAnimate); + + setAdapter(adapter); + + setCurrentItem(adapter.getItemPosition(page), false); + setVisibility(VISIBLE); if (shouldAnimate) { animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { @@ -279,69 +254,4 @@ public class HomePager extends ViewPager { return super.onInterceptTouchEvent(event); } - - private void updateUiFromPageEntries(List pageEntries) { - // We only care about the adapter if HomePager is currently - // loaded, which means it's visible in the activity. - if (!mLoaded) { - return; - } - - if (mDecor != null) { - mDecor.removeAllPagerViews(); - } - - final HomeAdapter adapter = (HomeAdapter) getAdapter(); - - // Disable loading until the final current item is defined - // after loading the page entries. This is to stop any temporary - // active item from loading. - boolean originalCanLoadHint = adapter.getCanLoadHint(); - adapter.setCanLoadHint(false); - - // Update the adapter with the new page entries - adapter.update(pageEntries); - - final int count = (pageEntries != null ? pageEntries.size() : 0); - - if (mTabStrip != null) { - mTabStrip.setVisibility(count > 0 ? View.VISIBLE : View.INVISIBLE); - } - - // Use the default page as defined in the HomePager's configuration - // if the initial page wasn't explicitly set by the show() caller. - if (mInitialPage != null) { - setCurrentItem(adapter.getItemPosition(mInitialPage), false); - mInitialPage = null; - } else { - for (int i = 0; i < count; i++) { - final PageEntry pageEntry = pageEntries.get(i); - if (pageEntry.isDefault()) { - setCurrentItem(i, false); - break; - } - } - } - - // Restore canLoadHint now that we have the final - // state in HomePager. - adapter.setCanLoadHint(originalCanLoadHint); - } - - private class ConfigLoaderCallbacks implements LoaderCallbacks> { - @Override - public Loader> onCreateLoader(int id, Bundle args) { - return new HomeConfigLoader(mContext, mConfig); - } - - @Override - public void onLoadFinished(Loader> loader, List pageEntries) { - updateUiFromPageEntries(pageEntries); - } - - @Override - public void onLoaderReset(Loader> loader) { - updateUiFromPageEntries(null); - } - } } diff --git a/mobile/android/base/home/ListPage.java b/mobile/android/base/home/ListPage.java deleted file mode 100644 index f3c911095b84..000000000000 --- a/mobile/android/base/home/ListPage.java +++ /dev/null @@ -1,185 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.home; - -import org.mozilla.gecko.R; -import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; -import org.mozilla.gecko.home.HomeConfig.PageEntry; - -import android.app.Activity; -import android.content.Context; -import android.content.res.Configuration; -import android.database.Cursor; -import android.os.Bundle; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.Loader; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ListView; - -import java.util.EnumSet; - -/** - * Fragment that displays custom lists. - */ -public class ListPage extends HomeFragment { - // Cursor loader ID for the lists - private static final int LOADER_ID_LIST = 0; - - // The page entry associated with this page - private PageEntry mPageEntry; - - // Adapter for the list - private HomeListAdapter mAdapter; - - // The view shown by the fragment - private ListView mList; - - // Callbacks used for the list loader - private CursorLoaderCallbacks mCursorLoaderCallbacks; - - // On URL open listener - private OnUrlOpenListener mUrlOpenListener; - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - try { - mUrlOpenListener = (OnUrlOpenListener) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() - + " must implement HomePager.OnUrlOpenListener"); - } - } - - @Override - public void onDetach() { - super.onDetach(); - - mUrlOpenListener = null; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - final Bundle args = getArguments(); - if (args != null) { - mPageEntry = (PageEntry) args.getParcelable(HomePager.PAGE_ENTRY_ARG); - } - - if (mPageEntry == null) { - throw new IllegalStateException("Can't create a ListPage without a PageEntry"); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - mList = new HomeListView(getActivity()); - return mList; - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - registerForContextMenu(mList); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - mList = null; - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - // Detach and reattach the fragment as the layout changes. - if (isVisible()) { - getFragmentManager().beginTransaction() - .detach(this) - .attach(this) - .commitAllowingStateLoss(); - } - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mAdapter = new HomeListAdapter(getActivity(), null); - mList.setAdapter(mAdapter); - - // Create callbacks before the initial loader is started. - mCursorLoaderCallbacks = new CursorLoaderCallbacks(); - loadIfVisible(); - } - - @Override - protected void load() { - getLoaderManager().initLoader(LOADER_ID_LIST, null, mCursorLoaderCallbacks); - } - - /** - * Cursor loader for the lists. - */ - private static class HomeListLoader extends SimpleCursorLoader { - public HomeListLoader(Context context) { - super(context); - } - - @Override - public Cursor loadCursor() { - // Do nothing - return null; - } - } - - /** - * Cursor adapter for the list. - */ - private class HomeListAdapter extends CursorAdapter { - public HomeListAdapter(Context context, Cursor cursor) { - super(context, cursor, 0); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - final TwoLinePageRow row = (TwoLinePageRow) view; - row.updateFromCursor(cursor); - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_item_row, parent, false); - } - } - - /** - * LoaderCallbacks implementation that interacts with the LoaderManager. - */ - private class CursorLoaderCallbacks implements LoaderCallbacks { - @Override - public Loader onCreateLoader(int id, Bundle args) { - return new HomeListLoader(getActivity()); - } - - @Override - public void onLoadFinished(Loader loader, Cursor c) { - mAdapter.swapCursor(c); - } - - @Override - public void onLoaderReset(Loader loader) { - mAdapter.swapCursor(null); - } - } -} diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 2bb5769946ea..476cb6c4855d 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -210,15 +210,11 @@ gbjar.sources += [ 'home/HistoryPage.java', 'home/HomeAdapter.java', 'home/HomeBanner.java', - 'home/HomeConfig.java', - 'home/HomeConfigLoader.java', - 'home/HomeConfigMemBackend.java', 'home/HomeFragment.java', 'home/HomeListView.java', 'home/HomePager.java', 'home/HomePagerTabStrip.java', 'home/LastTabsPage.java', - 'home/ListPage.java', 'home/MostRecentPage.java', 'home/MultiTypeCursorAdapter.java', 'home/PinSiteDialog.java', From 496ef3f0bd299b827716e3fd6326846474c46ced Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 12 Dec 2013 12:44:23 +0100 Subject: [PATCH 142/459] Backed out changeset 7744ed51864c (bug 942231) for Android Bustage on a CLOSED TREE --- mobile/android/base/home/HomeFragment.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mobile/android/base/home/HomeFragment.java b/mobile/android/base/home/HomeFragment.java index 45e1ee26f015..48b976ba74fa 100644 --- a/mobile/android/base/home/HomeFragment.java +++ b/mobile/android/base/home/HomeFragment.java @@ -44,9 +44,6 @@ abstract class HomeFragment extends Fragment { // Share MIME type. private static final String SHARE_MIME_TYPE = "text/plain"; - // Default value for "can load" hint - static final boolean DEFAULT_CAN_LOAD_HINT = false; - // Whether the fragment can load its content or not // This is used to defer data loading until the editing // mode animation ends. @@ -61,9 +58,9 @@ abstract class HomeFragment extends Fragment { final Bundle args = getArguments(); if (args != null) { - mCanLoadHint = args.getBoolean(HomePager.CAN_LOAD_ARG, DEFAULT_CAN_LOAD_HINT); + mCanLoadHint = args.getBoolean(HomePager.CAN_LOAD_ARG, false); } else { - mCanLoadHint = DEFAULT_CAN_LOAD_HINT; + mCanLoadHint = false; } mIsLoaded = false; From 17610f17b4d63c275e7036b828ae7e581eeb6146 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 12 Dec 2013 12:44:32 +0100 Subject: [PATCH 143/459] Backed out changeset c521a1683852 (bug 942231) for Android Bustage on a CLOSED TREE --- mobile/android/base/BrowserApp.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 87d29f055f6b..c6be146ff1cc 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1618,6 +1618,10 @@ abstract public class BrowserApp extends GeckoApp } } + private void showHomePager(HomePager.Page page) { + showHomePagerWithAnimator(page, null); + } + @Override public void onLocaleReady(final String locale) { super.onLocaleReady(locale); @@ -1632,10 +1636,6 @@ abstract public class BrowserApp extends GeckoApp } } - private void showHomePager(HomePager.Page page) { - showHomePagerWithAnimator(page, null); - } - private void showHomePagerWithAnimator(HomePager.Page page, PropertyAnimator animator) { if (isHomePagerVisible()) { return; From 3c90185a806ca7d2227b85d6f86a4e63b9da2373 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 12 Dec 2013 12:44:48 +0100 Subject: [PATCH 144/459] Backed out changeset f279d53c2d4d (bug 942231) for Android Bustage on a CLOSED TREE --- mobile/android/base/home/HomeAdapter.java | 52 +++++++++++------------ mobile/android/base/home/HomePager.java | 22 +++++----- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/mobile/android/base/home/HomeAdapter.java b/mobile/android/base/home/HomeAdapter.java index 2d424face292..caaab89a79b9 100644 --- a/mobile/android/base/home/HomeAdapter.java +++ b/mobile/android/base/home/HomeAdapter.java @@ -21,22 +21,22 @@ import java.util.EnumMap; class HomeAdapter extends FragmentStatePagerAdapter { private final Context mContext; - private final ArrayList mPageInfos; + private final ArrayList mTabs; private final EnumMap mPages; - private OnAddPageListener mAddPageListener; + private OnAddTabListener mAddTabListener; - interface OnAddPageListener { - public void onAddPage(String title); + interface OnAddTabListener { + public void onAddTab(String title); } - final class PageInfo { + final class TabInfo { private final Page page; private final Class clss; private final Bundle args; private final String title; - PageInfo(Page page, Class clss, Bundle args, String title) { + TabInfo(Page page, Class clss, Bundle args, String title) { this.page = page; this.clss = clss; this.args = args; @@ -49,31 +49,31 @@ class HomeAdapter extends FragmentStatePagerAdapter { mContext = context; - mPageInfos = new ArrayList(); + mTabs = new ArrayList(); mPages = new EnumMap(Page.class); } @Override public int getCount() { - return mPageInfos.size(); + return mTabs.size(); } @Override public Fragment getItem(int position) { - PageInfo info = mPageInfos.get(position); + TabInfo info = mTabs.get(position); return Fragment.instantiate(mContext, info.clss.getName(), info.args); } @Override public CharSequence getPageTitle(int position) { - PageInfo info = mPageInfos.get(position); + TabInfo info = mTabs.get(position); return info.title.toUpperCase(); } @Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = (Fragment) super.instantiateItem(container, position); - mPages.put(mPageInfos.get(position).page, fragment); + mPages.put(mTabs.get(position).page, fragment); return fragment; } @@ -81,36 +81,36 @@ class HomeAdapter extends FragmentStatePagerAdapter { @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); - mPages.remove(mPageInfos.get(position).page); + mPages.remove(mTabs.get(position).page); } - public void setOnAddPageListener(OnAddPageListener listener) { - mAddPageListener = listener; + public void setOnAddTabListener(OnAddTabListener listener) { + mAddTabListener = listener; } - public void addPage(Page page, Class clss, Bundle args, String title) { - addPage(-1, page, clss, args, title); + public void addTab(Page page, Class clss, Bundle args, String title) { + addTab(-1, page, clss, args, title); } - public void addPage(int index, Page page, Class clss, Bundle args, String title) { - PageInfo info = new PageInfo(page, clss, args, title); + public void addTab(int index, Page page, Class clss, Bundle args, String title) { + TabInfo info = new TabInfo(page, clss, args, title); if (index >= 0) { - mPageInfos.add(index, info); + mTabs.add(index, info); } else { - mPageInfos.add(info); + mTabs.add(info); } notifyDataSetChanged(); - if (mAddPageListener != null) { - mAddPageListener.onAddPage(title); + if (mAddTabListener != null) { + mAddTabListener.onAddTab(title); } } public int getItemPosition(Page page) { - for (int i = 0; i < mPageInfos.size(); i++) { - PageInfo info = mPageInfos.get(i); + for (int i = 0; i < mTabs.size(); i++) { + TabInfo info = mTabs.get(i); if (info.page == page) { return i; } @@ -120,13 +120,13 @@ class HomeAdapter extends FragmentStatePagerAdapter { } public Page getPageAtPosition(int position) { - PageInfo info = mPageInfos.get(position); + TabInfo info = mTabs.get(position); return info.page; } public void setCanLoadHint(boolean canLoadHint) { // Update fragment arguments for future instances - for (PageInfo info : mPageInfos) { + for (TabInfo info : mTabs) { info.args.putBoolean(HomePager.CAN_LOAD_ARG, canLoadHint); } diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 66253b1be0f6..f1d42dd4aa69 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -8,7 +8,7 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; -import org.mozilla.gecko.home.HomeAdapter.OnAddPageListener; +import org.mozilla.gecko.home.HomeAdapter.OnAddTabListener; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.HardwareUtils; @@ -32,7 +32,7 @@ public class HomePager extends ViewPager { private volatile boolean mLoaded; private Decor mDecor; - private final OnAddPageListener mAddPageListener; + private final OnAddTabListener mAddTabListener; // List of pages in order. @RobocopTarget @@ -90,9 +90,9 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; - mAddPageListener = new OnAddPageListener() { + mAddTabListener = new OnAddTabListener() { @Override - public void onAddPage(String title) { + public void onAddTab(String title) { if (mDecor != null) { mDecor.onAddPagerView(title); } @@ -163,26 +163,26 @@ public class HomePager extends ViewPager { } final HomeAdapter adapter = new HomeAdapter(mContext, fm); - adapter.setOnAddPageListener(mAddPageListener); + adapter.setOnAddTabListener(mAddTabListener); // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); - adapter.addPage(Page.TOP_SITES, TopSitesPage.class, new Bundle(), + adapter.addTab(Page.TOP_SITES, TopSitesPage.class, new Bundle(), getContext().getString(R.string.home_top_sites_title)); - adapter.addPage(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), + adapter.addTab(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), getContext().getString(R.string.bookmarks_title)); // We disable reader mode support on low memory devices. Hence the // reading list page should not show up on such devices. if (!HardwareUtils.isLowMemoryPlatform()) { - adapter.addPage(Page.READING_LIST, ReadingListPage.class, new Bundle(), + adapter.addTab(Page.READING_LIST, ReadingListPage.class, new Bundle(), getContext().getString(R.string.reading_list_title)); } - // On phones, the history page is the first one. On tablets, the - // history page is the last. - adapter.addPage(HardwareUtils.isTablet() ? -1 : 0, + // On phones, the history tab is the first tab. On tablets, the + // history tab is the last tab. + adapter.addTab(HardwareUtils.isTablet() ? -1 : 0, Page.HISTORY, HistoryPage.class, new Bundle(), getContext().getString(R.string.home_history_title)); From 248253386a14b0ba457bddfe22c40a0551ebde35 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 12 Dec 2013 12:44:58 +0100 Subject: [PATCH 145/459] Backed out changeset c4e1151b6a7e (bug 942231) for Android Bustage on a CLOSED TREE --- mobile/android/base/home/HomeAdapter.java | 139 ------------------- mobile/android/base/home/HomePager.java | 157 +++++++++++++++++----- mobile/android/base/moz.build | 1 - 3 files changed, 125 insertions(+), 172 deletions(-) delete mode 100644 mobile/android/base/home/HomeAdapter.java diff --git a/mobile/android/base/home/HomeAdapter.java b/mobile/android/base/home/HomeAdapter.java deleted file mode 100644 index caaab89a79b9..000000000000 --- a/mobile/android/base/home/HomeAdapter.java +++ /dev/null @@ -1,139 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.home; - -import org.mozilla.gecko.home.HomePager; -import org.mozilla.gecko.home.HomePager.Page; - -import android.content.Context; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.view.ViewGroup; - -import java.util.ArrayList; -import java.util.EnumMap; - -class HomeAdapter extends FragmentStatePagerAdapter { - - private final Context mContext; - private final ArrayList mTabs; - private final EnumMap mPages; - - private OnAddTabListener mAddTabListener; - - interface OnAddTabListener { - public void onAddTab(String title); - } - - final class TabInfo { - private final Page page; - private final Class clss; - private final Bundle args; - private final String title; - - TabInfo(Page page, Class clss, Bundle args, String title) { - this.page = page; - this.clss = clss; - this.args = args; - this.title = title; - } - } - - public HomeAdapter(Context context, FragmentManager fm) { - super(fm); - - mContext = context; - - mTabs = new ArrayList(); - mPages = new EnumMap(Page.class); - } - - @Override - public int getCount() { - return mTabs.size(); - } - - @Override - public Fragment getItem(int position) { - TabInfo info = mTabs.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), info.args); - } - - @Override - public CharSequence getPageTitle(int position) { - TabInfo info = mTabs.get(position); - return info.title.toUpperCase(); - } - - @Override - public Object instantiateItem(ViewGroup container, int position) { - Fragment fragment = (Fragment) super.instantiateItem(container, position); - mPages.put(mTabs.get(position).page, fragment); - - return fragment; - } - - @Override - public void destroyItem(ViewGroup container, int position, Object object) { - super.destroyItem(container, position, object); - mPages.remove(mTabs.get(position).page); - } - - public void setOnAddTabListener(OnAddTabListener listener) { - mAddTabListener = listener; - } - - public void addTab(Page page, Class clss, Bundle args, String title) { - addTab(-1, page, clss, args, title); - } - - public void addTab(int index, Page page, Class clss, Bundle args, String title) { - TabInfo info = new TabInfo(page, clss, args, title); - - if (index >= 0) { - mTabs.add(index, info); - } else { - mTabs.add(info); - } - - notifyDataSetChanged(); - - if (mAddTabListener != null) { - mAddTabListener.onAddTab(title); - } - } - - public int getItemPosition(Page page) { - for (int i = 0; i < mTabs.size(); i++) { - TabInfo info = mTabs.get(i); - if (info.page == page) { - return i; - } - } - - return -1; - } - - public Page getPageAtPosition(int position) { - TabInfo info = mTabs.get(position); - return info.page; - } - - public void setCanLoadHint(boolean canLoadHint) { - // Update fragment arguments for future instances - for (TabInfo info : mTabs) { - info.args.putBoolean(HomePager.CAN_LOAD_ARG, canLoadHint); - } - - // Enable/disable loading on all existing pages - for (Fragment page : mPages.values()) { - final HomeFragment homePage = (HomeFragment) page; - homePage.setCanLoadHint(canLoadHint); - } - } -} diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index f1d42dd4aa69..0db739c235f5 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -8,7 +8,6 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; -import org.mozilla.gecko.home.HomeAdapter.OnAddTabListener; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.HardwareUtils; @@ -17,6 +16,8 @@ import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.ViewGroup.LayoutParams; import android.util.AttributeSet; @@ -24,16 +25,15 @@ import android.view.MotionEvent; import android.view.ViewGroup; import android.view.View; +import java.util.ArrayList; +import java.util.EnumMap; import java.util.EnumSet; public class HomePager extends ViewPager { - private final Context mContext; private volatile boolean mLoaded; private Decor mDecor; - private final OnAddTabListener mAddTabListener; - // List of pages in order. @RobocopTarget public enum Page { @@ -53,6 +53,8 @@ public class HomePager extends ViewPager { static final String LIST_TAG_LAST_TABS = "last_tabs"; static final String LIST_TAG_BROWSER_SEARCH = "browser_search"; + private EnumMap mPages = new EnumMap(Page.class); + public interface OnUrlOpenListener { public enum Flags { ALLOW_SWITCH_TO_TAB @@ -90,15 +92,6 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; - mAddTabListener = new OnAddTabListener() { - @Override - public void onAddTab(String title) { - if (mDecor != null) { - mDecor.onAddPagerView(title); - } - } - }; - // This is to keep all 4 pages in memory after they are // selected in the pager. setOffscreenPageLimit(3); @@ -116,14 +109,6 @@ public class HomePager extends ViewPager { if (child instanceof Decor) { ((ViewPager.LayoutParams) params).isDecor = true; mDecor = (Decor) child; - - mDecor.setOnTitleClickListener(new OnTitleClickListener() { - @Override - public void onTitleClicked(int index) { - setCurrentItem(index, true); - } - }); - setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { @@ -144,10 +129,8 @@ public class HomePager extends ViewPager { } public void redisplay(FragmentManager fm) { - final HomeAdapter adapter = (HomeAdapter) getAdapter(); - final Page currentPage = adapter.getPageAtPosition(getCurrentItem()); - - show(fm, currentPage, null); + final TabsAdapter adapter = (TabsAdapter) getAdapter(); + show(fm, adapter.getCurrentPage(), null); } /** @@ -157,13 +140,7 @@ public class HomePager extends ViewPager { */ public void show(FragmentManager fm, Page page, PropertyAnimator animator) { mLoaded = true; - - if (mDecor != null) { - mDecor.removeAllPagerViews(); - } - - final HomeAdapter adapter = new HomeAdapter(mContext, fm); - adapter.setOnAddTabListener(mAddTabListener); + final TabsAdapter adapter = new TabsAdapter(fm); // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); @@ -245,6 +222,122 @@ public class HomePager extends ViewPager { } } + class TabsAdapter extends FragmentStatePagerAdapter + implements OnTitleClickListener { + private final ArrayList mTabs = new ArrayList(); + + final class TabInfo { + private final Page page; + private final Class clss; + private final Bundle args; + private final String title; + + TabInfo(Page page, Class clss, Bundle args, String title) { + this.page = page; + this.clss = clss; + this.args = args; + this.title = title; + } + } + + public TabsAdapter(FragmentManager fm) { + super(fm); + + if (mDecor != null) { + mDecor.removeAllPagerViews(); + mDecor.setOnTitleClickListener(this); + } + } + + public void addTab(Page page, Class clss, Bundle args, String title) { + addTab(-1, page, clss, args, title); + } + + public void addTab(int index, Page page, Class clss, Bundle args, String title) { + TabInfo info = new TabInfo(page, clss, args, title); + + if (index >= 0) { + mTabs.add(index, info); + } else { + mTabs.add(info); + } + + notifyDataSetChanged(); + + if (mDecor != null) { + mDecor.onAddPagerView(title); + } + } + + @Override + public void onTitleClicked(int index) { + setCurrentItem(index, true); + } + + public int getItemPosition(Page page) { + for (int i = 0; i < mTabs.size(); i++) { + TabInfo info = mTabs.get(i); + if (info.page == page) { + return i; + } + } + + return -1; + } + + public Page getCurrentPage() { + int currentItem = getCurrentItem(); + TabInfo info = mTabs.get(currentItem); + return info.page; + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Fragment getItem(int position) { + TabInfo info = mTabs.get(position); + return Fragment.instantiate(mContext, info.clss.getName(), info.args); + } + + @Override + public CharSequence getPageTitle(int position) { + TabInfo info = mTabs.get(position); + return info.title.toUpperCase(); + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + Fragment fragment = (Fragment) super.instantiateItem(container, position); + + mPages.put(mTabs.get(position).page, fragment); + + return fragment; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + super.destroyItem(container, position, object); + + mPages.remove(mTabs.get(position).page); + } + + public void setCanLoadHint(boolean canLoadHint) { + // Update fragment arguments for future instances + for (TabInfo info : mTabs) { + info.args.putBoolean(CAN_LOAD_ARG, canLoadHint); + } + + // Enable/disable loading on all existing pages + for (Fragment page : mPages.values()) { + final HomeFragment homePage = (HomeFragment) page; + homePage.setCanLoadHint(canLoadHint); + } + } + } + @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 476cb6c4855d..2eaf5ce6262a 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -208,7 +208,6 @@ gbjar.sources += [ 'home/BrowserSearch.java', 'home/FadedTextView.java', 'home/HistoryPage.java', - 'home/HomeAdapter.java', 'home/HomeBanner.java', 'home/HomeFragment.java', 'home/HomeListView.java', From 1c9bad3196732b1e04a606f0aa0d52a8a44d74d6 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 12 Dec 2013 12:45:09 +0100 Subject: [PATCH 146/459] Backed out changeset f1769583f43b (bug 942231) for Android Bustage on a CLOSED TREE --- mobile/android/base/home/HomePager.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 0db739c235f5..b0dae21f4f5c 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -30,6 +30,9 @@ import java.util.EnumMap; import java.util.EnumSet; public class HomePager extends ViewPager { + // Subpage fragment tag + public static final String SUBPAGE_TAG = "home_pager_subpage"; + private final Context mContext; private volatile boolean mLoaded; private Decor mDecor; From 29dddfe9663ff451df462c8ea48c4b098bd51b5a Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 12:02:49 +0000 Subject: [PATCH 147/459] Bug 942231 - Remove unused member from HomePager (r=margaret) --- mobile/android/base/home/HomePager.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index b0dae21f4f5c..0db739c235f5 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -30,9 +30,6 @@ import java.util.EnumMap; import java.util.EnumSet; public class HomePager extends ViewPager { - // Subpage fragment tag - public static final String SUBPAGE_TAG = "home_pager_subpage"; - private final Context mContext; private volatile boolean mLoaded; private Decor mDecor; From 620a1b0cd6b050c76647748b5383a61610aa3ce2 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 12:02:50 +0000 Subject: [PATCH 148/459] Bug 942231 - Factor out HomePager's adapter into a separate file (r=margaret) --- mobile/android/base/home/HomeAdapter.java | 139 +++++++++++++++++++ mobile/android/base/home/HomePager.java | 157 +++++----------------- mobile/android/base/moz.build | 1 + 3 files changed, 172 insertions(+), 125 deletions(-) create mode 100644 mobile/android/base/home/HomeAdapter.java diff --git a/mobile/android/base/home/HomeAdapter.java b/mobile/android/base/home/HomeAdapter.java new file mode 100644 index 000000000000..caaab89a79b9 --- /dev/null +++ b/mobile/android/base/home/HomeAdapter.java @@ -0,0 +1,139 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.home.HomePager; +import org.mozilla.gecko.home.HomePager.Page; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.EnumMap; + +class HomeAdapter extends FragmentStatePagerAdapter { + + private final Context mContext; + private final ArrayList mTabs; + private final EnumMap mPages; + + private OnAddTabListener mAddTabListener; + + interface OnAddTabListener { + public void onAddTab(String title); + } + + final class TabInfo { + private final Page page; + private final Class clss; + private final Bundle args; + private final String title; + + TabInfo(Page page, Class clss, Bundle args, String title) { + this.page = page; + this.clss = clss; + this.args = args; + this.title = title; + } + } + + public HomeAdapter(Context context, FragmentManager fm) { + super(fm); + + mContext = context; + + mTabs = new ArrayList(); + mPages = new EnumMap(Page.class); + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Fragment getItem(int position) { + TabInfo info = mTabs.get(position); + return Fragment.instantiate(mContext, info.clss.getName(), info.args); + } + + @Override + public CharSequence getPageTitle(int position) { + TabInfo info = mTabs.get(position); + return info.title.toUpperCase(); + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + Fragment fragment = (Fragment) super.instantiateItem(container, position); + mPages.put(mTabs.get(position).page, fragment); + + return fragment; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + super.destroyItem(container, position, object); + mPages.remove(mTabs.get(position).page); + } + + public void setOnAddTabListener(OnAddTabListener listener) { + mAddTabListener = listener; + } + + public void addTab(Page page, Class clss, Bundle args, String title) { + addTab(-1, page, clss, args, title); + } + + public void addTab(int index, Page page, Class clss, Bundle args, String title) { + TabInfo info = new TabInfo(page, clss, args, title); + + if (index >= 0) { + mTabs.add(index, info); + } else { + mTabs.add(info); + } + + notifyDataSetChanged(); + + if (mAddTabListener != null) { + mAddTabListener.onAddTab(title); + } + } + + public int getItemPosition(Page page) { + for (int i = 0; i < mTabs.size(); i++) { + TabInfo info = mTabs.get(i); + if (info.page == page) { + return i; + } + } + + return -1; + } + + public Page getPageAtPosition(int position) { + TabInfo info = mTabs.get(position); + return info.page; + } + + public void setCanLoadHint(boolean canLoadHint) { + // Update fragment arguments for future instances + for (TabInfo info : mTabs) { + info.args.putBoolean(HomePager.CAN_LOAD_ARG, canLoadHint); + } + + // Enable/disable loading on all existing pages + for (Fragment page : mPages.values()) { + final HomeFragment homePage = (HomeFragment) page; + homePage.setCanLoadHint(canLoadHint); + } + } +} diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 0db739c235f5..f1d42dd4aa69 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -8,6 +8,7 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; +import org.mozilla.gecko.home.HomeAdapter.OnAddTabListener; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.HardwareUtils; @@ -16,8 +17,6 @@ import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.ViewGroup.LayoutParams; import android.util.AttributeSet; @@ -25,15 +24,16 @@ import android.view.MotionEvent; import android.view.ViewGroup; import android.view.View; -import java.util.ArrayList; -import java.util.EnumMap; import java.util.EnumSet; public class HomePager extends ViewPager { + private final Context mContext; private volatile boolean mLoaded; private Decor mDecor; + private final OnAddTabListener mAddTabListener; + // List of pages in order. @RobocopTarget public enum Page { @@ -53,8 +53,6 @@ public class HomePager extends ViewPager { static final String LIST_TAG_LAST_TABS = "last_tabs"; static final String LIST_TAG_BROWSER_SEARCH = "browser_search"; - private EnumMap mPages = new EnumMap(Page.class); - public interface OnUrlOpenListener { public enum Flags { ALLOW_SWITCH_TO_TAB @@ -92,6 +90,15 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; + mAddTabListener = new OnAddTabListener() { + @Override + public void onAddTab(String title) { + if (mDecor != null) { + mDecor.onAddPagerView(title); + } + } + }; + // This is to keep all 4 pages in memory after they are // selected in the pager. setOffscreenPageLimit(3); @@ -109,6 +116,14 @@ public class HomePager extends ViewPager { if (child instanceof Decor) { ((ViewPager.LayoutParams) params).isDecor = true; mDecor = (Decor) child; + + mDecor.setOnTitleClickListener(new OnTitleClickListener() { + @Override + public void onTitleClicked(int index) { + setCurrentItem(index, true); + } + }); + setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { @@ -129,8 +144,10 @@ public class HomePager extends ViewPager { } public void redisplay(FragmentManager fm) { - final TabsAdapter adapter = (TabsAdapter) getAdapter(); - show(fm, adapter.getCurrentPage(), null); + final HomeAdapter adapter = (HomeAdapter) getAdapter(); + final Page currentPage = adapter.getPageAtPosition(getCurrentItem()); + + show(fm, currentPage, null); } /** @@ -140,7 +157,13 @@ public class HomePager extends ViewPager { */ public void show(FragmentManager fm, Page page, PropertyAnimator animator) { mLoaded = true; - final TabsAdapter adapter = new TabsAdapter(fm); + + if (mDecor != null) { + mDecor.removeAllPagerViews(); + } + + final HomeAdapter adapter = new HomeAdapter(mContext, fm); + adapter.setOnAddTabListener(mAddTabListener); // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); @@ -222,122 +245,6 @@ public class HomePager extends ViewPager { } } - class TabsAdapter extends FragmentStatePagerAdapter - implements OnTitleClickListener { - private final ArrayList mTabs = new ArrayList(); - - final class TabInfo { - private final Page page; - private final Class clss; - private final Bundle args; - private final String title; - - TabInfo(Page page, Class clss, Bundle args, String title) { - this.page = page; - this.clss = clss; - this.args = args; - this.title = title; - } - } - - public TabsAdapter(FragmentManager fm) { - super(fm); - - if (mDecor != null) { - mDecor.removeAllPagerViews(); - mDecor.setOnTitleClickListener(this); - } - } - - public void addTab(Page page, Class clss, Bundle args, String title) { - addTab(-1, page, clss, args, title); - } - - public void addTab(int index, Page page, Class clss, Bundle args, String title) { - TabInfo info = new TabInfo(page, clss, args, title); - - if (index >= 0) { - mTabs.add(index, info); - } else { - mTabs.add(info); - } - - notifyDataSetChanged(); - - if (mDecor != null) { - mDecor.onAddPagerView(title); - } - } - - @Override - public void onTitleClicked(int index) { - setCurrentItem(index, true); - } - - public int getItemPosition(Page page) { - for (int i = 0; i < mTabs.size(); i++) { - TabInfo info = mTabs.get(i); - if (info.page == page) { - return i; - } - } - - return -1; - } - - public Page getCurrentPage() { - int currentItem = getCurrentItem(); - TabInfo info = mTabs.get(currentItem); - return info.page; - } - - @Override - public int getCount() { - return mTabs.size(); - } - - @Override - public Fragment getItem(int position) { - TabInfo info = mTabs.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), info.args); - } - - @Override - public CharSequence getPageTitle(int position) { - TabInfo info = mTabs.get(position); - return info.title.toUpperCase(); - } - - @Override - public Object instantiateItem(ViewGroup container, int position) { - Fragment fragment = (Fragment) super.instantiateItem(container, position); - - mPages.put(mTabs.get(position).page, fragment); - - return fragment; - } - - @Override - public void destroyItem(ViewGroup container, int position, Object object) { - super.destroyItem(container, position, object); - - mPages.remove(mTabs.get(position).page); - } - - public void setCanLoadHint(boolean canLoadHint) { - // Update fragment arguments for future instances - for (TabInfo info : mTabs) { - info.args.putBoolean(CAN_LOAD_ARG, canLoadHint); - } - - // Enable/disable loading on all existing pages - for (Fragment page : mPages.values()) { - final HomeFragment homePage = (HomeFragment) page; - homePage.setCanLoadHint(canLoadHint); - } - } - } - @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 2eaf5ce6262a..476cb6c4855d 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -208,6 +208,7 @@ gbjar.sources += [ 'home/BrowserSearch.java', 'home/FadedTextView.java', 'home/HistoryPage.java', + 'home/HomeAdapter.java', 'home/HomeBanner.java', 'home/HomeFragment.java', 'home/HomeListView.java', From 4711082bdb7d6cb52facfa746f014419c018c2bb Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 12:02:51 +0000 Subject: [PATCH 149/459] Bug 942231 - Use 'page' terminology in HomePager instead of 'tab' (r=margaret) --- mobile/android/base/home/HomeAdapter.java | 52 +++++++++++------------ mobile/android/base/home/HomePager.java | 22 +++++----- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/mobile/android/base/home/HomeAdapter.java b/mobile/android/base/home/HomeAdapter.java index caaab89a79b9..2d424face292 100644 --- a/mobile/android/base/home/HomeAdapter.java +++ b/mobile/android/base/home/HomeAdapter.java @@ -21,22 +21,22 @@ import java.util.EnumMap; class HomeAdapter extends FragmentStatePagerAdapter { private final Context mContext; - private final ArrayList mTabs; + private final ArrayList mPageInfos; private final EnumMap mPages; - private OnAddTabListener mAddTabListener; + private OnAddPageListener mAddPageListener; - interface OnAddTabListener { - public void onAddTab(String title); + interface OnAddPageListener { + public void onAddPage(String title); } - final class TabInfo { + final class PageInfo { private final Page page; private final Class clss; private final Bundle args; private final String title; - TabInfo(Page page, Class clss, Bundle args, String title) { + PageInfo(Page page, Class clss, Bundle args, String title) { this.page = page; this.clss = clss; this.args = args; @@ -49,31 +49,31 @@ class HomeAdapter extends FragmentStatePagerAdapter { mContext = context; - mTabs = new ArrayList(); + mPageInfos = new ArrayList(); mPages = new EnumMap(Page.class); } @Override public int getCount() { - return mTabs.size(); + return mPageInfos.size(); } @Override public Fragment getItem(int position) { - TabInfo info = mTabs.get(position); + PageInfo info = mPageInfos.get(position); return Fragment.instantiate(mContext, info.clss.getName(), info.args); } @Override public CharSequence getPageTitle(int position) { - TabInfo info = mTabs.get(position); + PageInfo info = mPageInfos.get(position); return info.title.toUpperCase(); } @Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = (Fragment) super.instantiateItem(container, position); - mPages.put(mTabs.get(position).page, fragment); + mPages.put(mPageInfos.get(position).page, fragment); return fragment; } @@ -81,36 +81,36 @@ class HomeAdapter extends FragmentStatePagerAdapter { @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); - mPages.remove(mTabs.get(position).page); + mPages.remove(mPageInfos.get(position).page); } - public void setOnAddTabListener(OnAddTabListener listener) { - mAddTabListener = listener; + public void setOnAddPageListener(OnAddPageListener listener) { + mAddPageListener = listener; } - public void addTab(Page page, Class clss, Bundle args, String title) { - addTab(-1, page, clss, args, title); + public void addPage(Page page, Class clss, Bundle args, String title) { + addPage(-1, page, clss, args, title); } - public void addTab(int index, Page page, Class clss, Bundle args, String title) { - TabInfo info = new TabInfo(page, clss, args, title); + public void addPage(int index, Page page, Class clss, Bundle args, String title) { + PageInfo info = new PageInfo(page, clss, args, title); if (index >= 0) { - mTabs.add(index, info); + mPageInfos.add(index, info); } else { - mTabs.add(info); + mPageInfos.add(info); } notifyDataSetChanged(); - if (mAddTabListener != null) { - mAddTabListener.onAddTab(title); + if (mAddPageListener != null) { + mAddPageListener.onAddPage(title); } } public int getItemPosition(Page page) { - for (int i = 0; i < mTabs.size(); i++) { - TabInfo info = mTabs.get(i); + for (int i = 0; i < mPageInfos.size(); i++) { + PageInfo info = mPageInfos.get(i); if (info.page == page) { return i; } @@ -120,13 +120,13 @@ class HomeAdapter extends FragmentStatePagerAdapter { } public Page getPageAtPosition(int position) { - TabInfo info = mTabs.get(position); + PageInfo info = mPageInfos.get(position); return info.page; } public void setCanLoadHint(boolean canLoadHint) { // Update fragment arguments for future instances - for (TabInfo info : mTabs) { + for (PageInfo info : mPageInfos) { info.args.putBoolean(HomePager.CAN_LOAD_ARG, canLoadHint); } diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index f1d42dd4aa69..66253b1be0f6 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -8,7 +8,7 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; -import org.mozilla.gecko.home.HomeAdapter.OnAddTabListener; +import org.mozilla.gecko.home.HomeAdapter.OnAddPageListener; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.HardwareUtils; @@ -32,7 +32,7 @@ public class HomePager extends ViewPager { private volatile boolean mLoaded; private Decor mDecor; - private final OnAddTabListener mAddTabListener; + private final OnAddPageListener mAddPageListener; // List of pages in order. @RobocopTarget @@ -90,9 +90,9 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; - mAddTabListener = new OnAddTabListener() { + mAddPageListener = new OnAddPageListener() { @Override - public void onAddTab(String title) { + public void onAddPage(String title) { if (mDecor != null) { mDecor.onAddPagerView(title); } @@ -163,26 +163,26 @@ public class HomePager extends ViewPager { } final HomeAdapter adapter = new HomeAdapter(mContext, fm); - adapter.setOnAddTabListener(mAddTabListener); + adapter.setOnAddPageListener(mAddPageListener); // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); - adapter.addTab(Page.TOP_SITES, TopSitesPage.class, new Bundle(), + adapter.addPage(Page.TOP_SITES, TopSitesPage.class, new Bundle(), getContext().getString(R.string.home_top_sites_title)); - adapter.addTab(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), + adapter.addPage(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), getContext().getString(R.string.bookmarks_title)); // We disable reader mode support on low memory devices. Hence the // reading list page should not show up on such devices. if (!HardwareUtils.isLowMemoryPlatform()) { - adapter.addTab(Page.READING_LIST, ReadingListPage.class, new Bundle(), + adapter.addPage(Page.READING_LIST, ReadingListPage.class, new Bundle(), getContext().getString(R.string.reading_list_title)); } - // On phones, the history tab is the first tab. On tablets, the - // history tab is the last tab. - adapter.addTab(HardwareUtils.isTablet() ? -1 : 0, + // On phones, the history page is the first one. On tablets, the + // history page is the last. + adapter.addPage(HardwareUtils.isTablet() ? -1 : 0, Page.HISTORY, HistoryPage.class, new Bundle(), getContext().getString(R.string.home_history_title)); From f339fade7477617aa9c7b3f2ea36de72e817eb3e Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 12:02:51 +0000 Subject: [PATCH 150/459] Bug 942231 - Keep showHomePager* method together in BrowserApp (r=margaret) --- mobile/android/base/BrowserApp.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index c6be146ff1cc..87d29f055f6b 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1618,10 +1618,6 @@ abstract public class BrowserApp extends GeckoApp } } - private void showHomePager(HomePager.Page page) { - showHomePagerWithAnimator(page, null); - } - @Override public void onLocaleReady(final String locale) { super.onLocaleReady(locale); @@ -1636,6 +1632,10 @@ abstract public class BrowserApp extends GeckoApp } } + private void showHomePager(HomePager.Page page) { + showHomePagerWithAnimator(page, null); + } + private void showHomePagerWithAnimator(HomePager.Page page, PropertyAnimator animator) { if (isHomePagerVisible()) { return; From c5f8fff541c751e85d170a79d73b7dedb52b0bb2 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 12:02:52 +0000 Subject: [PATCH 151/459] Bug 942231 - Define constant with default value for 'can load' hint (r=margaret) --- mobile/android/base/home/HomeFragment.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/home/HomeFragment.java b/mobile/android/base/home/HomeFragment.java index 48b976ba74fa..45e1ee26f015 100644 --- a/mobile/android/base/home/HomeFragment.java +++ b/mobile/android/base/home/HomeFragment.java @@ -44,6 +44,9 @@ abstract class HomeFragment extends Fragment { // Share MIME type. private static final String SHARE_MIME_TYPE = "text/plain"; + // Default value for "can load" hint + static final boolean DEFAULT_CAN_LOAD_HINT = false; + // Whether the fragment can load its content or not // This is used to defer data loading until the editing // mode animation ends. @@ -58,9 +61,9 @@ abstract class HomeFragment extends Fragment { final Bundle args = getArguments(); if (args != null) { - mCanLoadHint = args.getBoolean(HomePager.CAN_LOAD_ARG, false); + mCanLoadHint = args.getBoolean(HomePager.CAN_LOAD_ARG, DEFAULT_CAN_LOAD_HINT); } else { - mCanLoadHint = false; + mCanLoadHint = DEFAULT_CAN_LOAD_HINT; } mIsLoaded = false; From c1e1d383354849d95269c92da5ff3683381e27b2 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 12 Dec 2013 12:04:31 +0000 Subject: [PATCH 152/459] Bug 942231 - Change HomePager to be backed by HomeConfig (r=margaret) --- CLOBBER | 2 +- mobile/android/base/BrowserApp.java | 14 +- mobile/android/base/Tab.java | 5 +- mobile/android/base/home/HomeAdapter.java | 144 +++++++---- mobile/android/base/home/HomeConfig.java | 224 ++++++++++++++++++ .../android/base/home/HomeConfigLoader.java | 83 +++++++ .../base/home/HomeConfigMemBackend.java | 67 ++++++ mobile/android/base/home/HomePager.java | 149 +++++++++--- mobile/android/base/home/ListPage.java | 185 +++++++++++++++ mobile/android/base/moz.build | 4 + 10 files changed, 790 insertions(+), 87 deletions(-) create mode 100644 mobile/android/base/home/HomeConfig.java create mode 100644 mobile/android/base/home/HomeConfigLoader.java create mode 100644 mobile/android/base/home/HomeConfigMemBackend.java create mode 100644 mobile/android/base/home/ListPage.java diff --git a/CLOBBER b/CLOBBER index c8fd7d31d7bc..d71a89884b10 100644 --- a/CLOBBER +++ b/CLOBBER @@ -18,4 +18,4 @@ # Modifying this file will now automatically clobber the buildbot machines \o/ # -Bug 946067 required a clobber on Windows because bug 928195 +Bug 942231 needs a clobber -- JNI wrappers need to be re-generated. diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 87d29f055f6b..3fcfa90c5427 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1467,7 +1467,7 @@ abstract public class BrowserApp extends GeckoApp animator.setUseHardwareLayer(false); mBrowserToolbar.startEditing(url, animator); - showHomePagerWithAnimator(HomePager.Page.TOP_SITES, animator); + showHomePagerWithAnimator(animator); animator.start(); } @@ -1623,7 +1623,7 @@ abstract public class BrowserApp extends GeckoApp super.onLocaleReady(locale); if (mHomePager != null) { // Blow it away and rebuild it with the right strings. - mHomePager.redisplay(getSupportFragmentManager()); + mHomePager.redisplay(getSupportLoaderManager(), getSupportFragmentManager()); } if (mMenu != null) { @@ -1636,6 +1636,12 @@ abstract public class BrowserApp extends GeckoApp showHomePagerWithAnimator(page, null); } + private void showHomePagerWithAnimator(PropertyAnimator animator) { + // Passing null here means the default page will be defined + // by the HomePager's configuration. + showHomePagerWithAnimator(null, animator); + } + private void showHomePagerWithAnimator(HomePager.Page page, PropertyAnimator animator) { if (isHomePagerVisible()) { return; @@ -1655,7 +1661,9 @@ abstract public class BrowserApp extends GeckoApp mHomePager = (HomePager) homePagerStub.inflate(); } - mHomePager.show(getSupportFragmentManager(), page, animator); + mHomePager.show(getSupportLoaderManager(), + getSupportFragmentManager(), + page, animator); // Hide the web content so it cannot be focused by screen readers. hideWebContentOnPropertyAnimationEnd(animator); diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 09171f428372..0f1a02d10a7c 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -95,7 +95,7 @@ public class Tab { mUserSearch = ""; mExternal = external; mParentId = parentId; - mAboutHomePage = HomePager.Page.TOP_SITES; + mAboutHomePage = null; mTitle = title == null ? "" : title; mFavicon = null; mFaviconUrl = null; @@ -155,7 +155,6 @@ public class Tab { mAboutHomePage = page; } - // may be null if user-entered query hasn't yet been resolved to a URI public synchronized String getURL() { return mUrl; @@ -660,6 +659,8 @@ public class Tab { final String homePage = message.getString("aboutHomePage"); if (!TextUtils.isEmpty(homePage)) { setAboutHomePage(HomePager.Page.valueOf(homePage)); + } else { + setAboutHomePage(null); } Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.LOCATION_CHANGE, oldUrl); diff --git a/mobile/android/base/home/HomeAdapter.java b/mobile/android/base/home/HomeAdapter.java index 2d424face292..7cbdc06283a2 100644 --- a/mobile/android/base/home/HomeAdapter.java +++ b/mobile/android/base/home/HomeAdapter.java @@ -5,6 +5,8 @@ package org.mozilla.gecko.home; +import org.mozilla.gecko.home.HomeConfig.PageEntry; +import org.mozilla.gecko.home.HomeConfig.PageType; import org.mozilla.gecko.home.HomePager; import org.mozilla.gecko.home.HomePager.Page; @@ -16,13 +18,16 @@ import android.support.v4.app.FragmentStatePagerAdapter; import android.view.ViewGroup; import java.util.ArrayList; -import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; class HomeAdapter extends FragmentStatePagerAdapter { private final Context mContext; private final ArrayList mPageInfos; - private final EnumMap mPages; + private final HashMap mPages; + + private boolean mCanLoadHint; private OnAddPageListener mAddPageListener; @@ -30,27 +35,14 @@ class HomeAdapter extends FragmentStatePagerAdapter { public void onAddPage(String title); } - final class PageInfo { - private final Page page; - private final Class clss; - private final Bundle args; - private final String title; - - PageInfo(Page page, Class clss, Bundle args, String title) { - this.page = page; - this.clss = clss; - this.args = args; - this.title = title; - } - } - public HomeAdapter(Context context, FragmentManager fm) { super(fm); mContext = context; + mCanLoadHint = HomeFragment.DEFAULT_CAN_LOAD_HINT; mPageInfos = new ArrayList(); - mPages = new EnumMap(Page.class); + mPages = new HashMap(); } @Override @@ -61,19 +53,23 @@ class HomeAdapter extends FragmentStatePagerAdapter { @Override public Fragment getItem(int position) { PageInfo info = mPageInfos.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), info.args); + return Fragment.instantiate(mContext, info.getClassName(), info.getArgs()); } @Override public CharSequence getPageTitle(int position) { - PageInfo info = mPageInfos.get(position); - return info.title.toUpperCase(); + if (mPageInfos.size() > 0) { + PageInfo info = mPageInfos.get(position); + return info.getTitle().toUpperCase(); + } + + return null; } @Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = (Fragment) super.instantiateItem(container, position); - mPages.put(mPageInfos.get(position).page, fragment); + mPages.put(mPageInfos.get(position).getId(), fragment); return fragment; } @@ -81,37 +77,17 @@ class HomeAdapter extends FragmentStatePagerAdapter { @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); - mPages.remove(mPageInfos.get(position).page); + mPages.remove(mPageInfos.get(position).getId()); } public void setOnAddPageListener(OnAddPageListener listener) { mAddPageListener = listener; } - public void addPage(Page page, Class clss, Bundle args, String title) { - addPage(-1, page, clss, args, title); - } - - public void addPage(int index, Page page, Class clss, Bundle args, String title) { - PageInfo info = new PageInfo(page, clss, args, title); - - if (index >= 0) { - mPageInfos.add(index, info); - } else { - mPageInfos.add(info); - } - - notifyDataSetChanged(); - - if (mAddPageListener != null) { - mAddPageListener.onAddPage(title); - } - } - public int getItemPosition(Page page) { for (int i = 0; i < mPageInfos.size(); i++) { - PageInfo info = mPageInfos.get(i); - if (info.page == page) { + final Page infoPage = mPageInfos.get(i).toPage(); + if (infoPage == page) { return i; } } @@ -121,14 +97,39 @@ class HomeAdapter extends FragmentStatePagerAdapter { public Page getPageAtPosition(int position) { PageInfo info = mPageInfos.get(position); - return info.page; + return info.toPage(); + } + + private void addPage(PageInfo info) { + mPageInfos.add(info); + + if (mAddPageListener != null) { + mAddPageListener.onAddPage(info.getTitle()); + } + } + + public void update(List pageEntries) { + mPages.clear(); + mPageInfos.clear(); + + if (pageEntries != null) { + for (PageEntry pageEntry : pageEntries) { + final PageInfo info = new PageInfo(pageEntry); + addPage(info); + } + } + + notifyDataSetChanged(); + } + + public boolean getCanLoadHint() { + return mCanLoadHint; } public void setCanLoadHint(boolean canLoadHint) { - // Update fragment arguments for future instances - for (PageInfo info : mPageInfos) { - info.args.putBoolean(HomePager.CAN_LOAD_ARG, canLoadHint); - } + // We cache the last hint value so that we can use it when + // creating new pages. See PageInfo.getArgs(). + mCanLoadHint = canLoadHint; // Enable/disable loading on all existing pages for (Fragment page : mPages.values()) { @@ -136,4 +137,49 @@ class HomeAdapter extends FragmentStatePagerAdapter { homePage.setCanLoadHint(canLoadHint); } } + + private final class PageInfo { + private final String mId; + private final PageEntry mPageEntry; + + PageInfo(PageEntry pageEntry) { + mId = pageEntry.getType() + "-" + pageEntry.getId(); + mPageEntry = pageEntry; + } + + public String getId() { + return mId; + } + + public String getTitle() { + return mPageEntry.getTitle(); + } + + public String getClassName() { + final PageType type = mPageEntry.getType(); + return type.getPageClass().getName(); + } + + public Bundle getArgs() { + final Bundle args = new Bundle(); + + args.putBoolean(HomePager.CAN_LOAD_ARG, mCanLoadHint); + + // Only list pages need the page entry + if (mPageEntry.getType() == PageType.LIST) { + args.putParcelable(HomePager.PAGE_ENTRY_ARG, mPageEntry); + } + + return args; + } + + public Page toPage() { + final PageType type = mPageEntry.getType(); + if (type == PageType.LIST) { + return null; + } + + return Page.valueOf(type); + } + } } diff --git a/mobile/android/base/home/HomeConfig.java b/mobile/android/base/home/HomeConfig.java new file mode 100644 index 000000000000..636fd617f7d8 --- /dev/null +++ b/mobile/android/base/home/HomeConfig.java @@ -0,0 +1,224 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.home.HomePager.Page; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.util.EnumSet; +import java.util.List; + +final class HomeConfig { + public static enum PageType implements Parcelable { + TOP_SITES("top_sites", TopSitesPage.class), + BOOKMARKS("bookmarks", BookmarksPage.class), + HISTORY("history", HistoryPage.class), + READING_LIST("reading_list", ReadingListPage.class), + LIST("list", ListPage.class); + + private final String mId; + private final Class mPageClass; + + PageType(String id, Class pageClass) { + mId = id; + mPageClass = pageClass; + } + + public static PageType valueOf(Page page) { + switch(page) { + case TOP_SITES: + return PageType.TOP_SITES; + + case BOOKMARKS: + return PageType.BOOKMARKS; + + case HISTORY: + return PageType.HISTORY; + + case READING_LIST: + return PageType.READING_LIST; + + default: + throw new IllegalArgumentException("Could not convert unrecognized Page"); + } + } + + public static PageType fromId(String id) { + if (id == null) { + throw new IllegalArgumentException("Could not convert null String to PageType"); + } + + for (PageType page : PageType.values()) { + if (TextUtils.equals(page.mId, id.toLowerCase())) { + return page; + } + } + + throw new IllegalArgumentException("Could not convert String id to PageType"); + } + + @Override + public String toString() { + return mId; + } + + public Class getPageClass() { + return mPageClass; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(ordinal()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PageType createFromParcel(final Parcel source) { + return PageType.values()[source.readInt()]; + } + + @Override + public PageType[] newArray(final int size) { + return new PageType[size]; + } + }; + } + + public static class PageEntry implements Parcelable { + private final PageType mType; + private final String mTitle; + private final String mId; + private final EnumSet mFlags; + + public enum Flags { + DEFAULT_PAGE + } + + @SuppressWarnings("unchecked") + public PageEntry(Parcel in) { + mType = (PageType) in.readParcelable(getClass().getClassLoader()); + mTitle = in.readString(); + mId = in.readString(); + mFlags = (EnumSet) in.readSerializable(); + } + + public PageEntry(PageType type, String title) { + this(type, title, EnumSet.noneOf(Flags.class)); + } + + public PageEntry(PageType type, String title, EnumSet flags) { + this(type, title, type.toString(), flags); + } + + public PageEntry(PageType type, String title, String id) { + this(type, title, id, EnumSet.noneOf(Flags.class)); + } + + public PageEntry(PageType type, String title, String id, EnumSet flags) { + if (type == null) { + throw new IllegalArgumentException("Can't create PageEntry with null type"); + } + mType = type; + + if (title == null) { + throw new IllegalArgumentException("Can't create PageEntry with null title"); + } + mTitle = title; + + if (id == null) { + throw new IllegalArgumentException("Can't create PageEntry with null id"); + } + mId = id; + + if (flags == null) { + throw new IllegalArgumentException("Can't create PageEntry with null flags"); + } + mFlags = flags; + } + + public PageType getType() { + return mType; + } + + public String getTitle() { + return mTitle; + } + + public String getId() { + return mId; + } + + public boolean isDefault() { + return mFlags.contains(Flags.DEFAULT_PAGE); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mType, 0); + dest.writeString(mTitle); + dest.writeString(mId); + dest.writeSerializable(mFlags); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PageEntry createFromParcel(final Parcel in) { + return new PageEntry(in); + } + + @Override + public PageEntry[] newArray(final int size) { + return new PageEntry[size]; + } + }; + } + + public interface OnChangeListener { + public void onChange(); + } + + public interface HomeConfigBackend { + public List load(); + public void save(List entries); + public void setOnChangeListener(OnChangeListener listener); + } + + private final HomeConfigBackend mBackend; + + public HomeConfig(HomeConfigBackend backend) { + mBackend = backend; + } + + public List load() { + return mBackend.load(); + } + + public void save(List entries) { + mBackend.save(entries); + } + + public void setOnChangeListener(OnChangeListener listener) { + mBackend.setOnChangeListener(listener); + } + + public static HomeConfig getDefault(Context context) { + return new HomeConfig(new HomeConfigMemBackend(context)); + } +} \ No newline at end of file diff --git a/mobile/android/base/home/HomeConfigLoader.java b/mobile/android/base/home/HomeConfigLoader.java new file mode 100644 index 000000000000..df27c8c7e961 --- /dev/null +++ b/mobile/android/base/home/HomeConfigLoader.java @@ -0,0 +1,83 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.home.HomeConfig.PageEntry; +import org.mozilla.gecko.home.HomeConfig.OnChangeListener; + +import android.content.Context; +import android.support.v4.content.AsyncTaskLoader; + +import java.util.List; + +public class HomeConfigLoader extends AsyncTaskLoader> { + private final HomeConfig mConfig; + private List mPageEntries; + + public HomeConfigLoader(Context context, HomeConfig homeConfig) { + super(context); + mConfig = homeConfig; + } + + @Override + public List loadInBackground() { + return mConfig.load(); + } + + @Override + public void deliverResult(List pageEntries) { + if (isReset()) { + mPageEntries = null; + return; + } + + mPageEntries = pageEntries; + mConfig.setOnChangeListener(new ForceLoadChangeListener()); + + if (isStarted()) { + super.deliverResult(pageEntries); + } + } + + @Override + protected void onStartLoading() { + if (mPageEntries != null) { + deliverResult(mPageEntries); + } + + if (takeContentChanged() || mPageEntries == null) { + forceLoad(); + } + } + + @Override + protected void onStopLoading() { + cancelLoad(); + } + + @Override + public void onCanceled(List pageEntries) { + mPageEntries = null; + } + + @Override + protected void onReset() { + super.onReset(); + + // Ensure the loader is stopped. + onStopLoading(); + + mPageEntries = null; + mConfig.setOnChangeListener(null); + } + + private class ForceLoadChangeListener implements OnChangeListener { + @Override + public void onChange() { + onContentChanged(); + } + } +} diff --git a/mobile/android/base/home/HomeConfigMemBackend.java b/mobile/android/base/home/HomeConfigMemBackend.java new file mode 100644 index 000000000000..03d038b1f2c5 --- /dev/null +++ b/mobile/android/base/home/HomeConfigMemBackend.java @@ -0,0 +1,67 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.R; +import org.mozilla.gecko.home.HomeConfig.HomeConfigBackend; +import org.mozilla.gecko.home.HomeConfig.OnChangeListener; +import org.mozilla.gecko.home.HomeConfig.PageEntry; +import org.mozilla.gecko.home.HomeConfig.PageType; +import org.mozilla.gecko.util.HardwareUtils; + +import android.content.Context; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +class HomeConfigMemBackend implements HomeConfigBackend { + private final Context mContext; + + public HomeConfigMemBackend(Context context) { + mContext = context; + } + + public List load() { + final ArrayList pageEntries = new ArrayList(); + + pageEntries.add(new PageEntry(PageType.TOP_SITES, + mContext.getString(R.string.home_top_sites_title), + EnumSet.of(PageEntry.Flags.DEFAULT_PAGE))); + + pageEntries.add(new PageEntry(PageType.BOOKMARKS, + mContext.getString(R.string.bookmarks_title))); + + // We disable reader mode support on low memory devices. Hence the + // reading list page should not show up on such devices. + if (!HardwareUtils.isLowMemoryPlatform()) { + pageEntries.add(new PageEntry(PageType.READING_LIST, + mContext.getString(R.string.reading_list_title))); + } + + final PageEntry historyEntry = new PageEntry(PageType.HISTORY, + mContext.getString(R.string.home_history_title)); + + // On tablets, the history page is the last. + // On phones, the history page is the first one. + if (HardwareUtils.isTablet()) { + pageEntries.add(historyEntry); + } else { + pageEntries.add(0, historyEntry); + } + + return Collections.unmodifiableList(pageEntries); + } + + public void save(List entries) { + // This is a static backend, do nothing. + } + + public void setOnChangeListener(OnChangeListener listener) { + // This is a static backend, do nothing. + } +} \ No newline at end of file diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 66253b1be0f6..55b149b7f06d 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -9,6 +9,8 @@ import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; import org.mozilla.gecko.home.HomeAdapter.OnAddPageListener; +import org.mozilla.gecko.home.HomeConfig.PageEntry; +import org.mozilla.gecko.home.HomeConfig.PageType; import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.util.HardwareUtils; @@ -17,6 +19,9 @@ import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; +import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; import android.support.v4.view.ViewPager; import android.view.ViewGroup.LayoutParams; import android.util.AttributeSet; @@ -25,22 +30,50 @@ import android.view.ViewGroup; import android.view.View; import java.util.EnumSet; +import java.util.List; public class HomePager extends ViewPager { + private static final int LOADER_ID_CONFIG = 0; + private final Context mContext; private volatile boolean mLoaded; private Decor mDecor; + private View mTabStrip; private final OnAddPageListener mAddPageListener; + private final HomeConfig mConfig; + private ConfigLoaderCallbacks mConfigLoaderCallbacks; + + private Page mInitialPage; + // List of pages in order. @RobocopTarget public enum Page { HISTORY, TOP_SITES, BOOKMARKS, - READING_LIST + READING_LIST; + + static Page valueOf(PageType page) { + switch(page) { + case TOP_SITES: + return Page.TOP_SITES; + + case BOOKMARKS: + return Page.BOOKMARKS; + + case HISTORY: + return Page.HISTORY; + + case READING_LIST: + return Page.READING_LIST; + + default: + throw new IllegalArgumentException("Could not convert unrecognized PageType"); + } + } } // This is mostly used by UI tests to easily fetch @@ -81,6 +114,7 @@ public class HomePager extends ViewPager { } static final String CAN_LOAD_ARG = "canLoad"; + static final String PAGE_ENTRY_ARG = "pageEntry"; public HomePager(Context context) { this(context, null); @@ -90,6 +124,9 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; + mConfig = HomeConfig.getDefault(mContext); + mConfigLoaderCallbacks = new ConfigLoaderCallbacks(); + mAddPageListener = new OnAddPageListener() { @Override public void onAddPage(String title) { @@ -116,6 +153,7 @@ public class HomePager extends ViewPager { if (child instanceof Decor) { ((ViewPager.LayoutParams) params).isDecor = true; mDecor = (Decor) child; + mTabStrip = child; mDecor.setOnTitleClickListener(new OnTitleClickListener() { @Override @@ -138,16 +176,18 @@ public class HomePager extends ViewPager { @Override public void onPageScrollStateChanged(int state) { } }); + } else if (child instanceof HomePagerTabStrip) { + mTabStrip = child; } super.addView(child, index, params); } - public void redisplay(FragmentManager fm) { + public void redisplay(LoaderManager lm, FragmentManager fm) { final HomeAdapter adapter = (HomeAdapter) getAdapter(); - final Page currentPage = adapter.getPageAtPosition(getCurrentItem()); - show(fm, currentPage, null); + Page currentPage = adapter.getPageAtPosition(getCurrentItem()); + show(lm, fm, currentPage, null); } /** @@ -155,44 +195,27 @@ public class HomePager extends ViewPager { * * @param fm FragmentManager for the adapter */ - public void show(FragmentManager fm, Page page, PropertyAnimator animator) { + public void show(LoaderManager lm, FragmentManager fm, Page page, PropertyAnimator animator) { mLoaded = true; - - if (mDecor != null) { - mDecor.removeAllPagerViews(); - } - - final HomeAdapter adapter = new HomeAdapter(mContext, fm); - adapter.setOnAddPageListener(mAddPageListener); + mInitialPage = page; // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); - adapter.addPage(Page.TOP_SITES, TopSitesPage.class, new Bundle(), - getContext().getString(R.string.home_top_sites_title)); - adapter.addPage(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), - getContext().getString(R.string.bookmarks_title)); - - // We disable reader mode support on low memory devices. Hence the - // reading list page should not show up on such devices. - if (!HardwareUtils.isLowMemoryPlatform()) { - adapter.addPage(Page.READING_LIST, ReadingListPage.class, new Bundle(), - getContext().getString(R.string.reading_list_title)); - } - - // On phones, the history page is the first one. On tablets, the - // history page is the last. - adapter.addPage(HardwareUtils.isTablet() ? -1 : 0, - Page.HISTORY, HistoryPage.class, new Bundle(), - getContext().getString(R.string.home_history_title)); - + final HomeAdapter adapter = new HomeAdapter(mContext, fm); + adapter.setOnAddPageListener(mAddPageListener); adapter.setCanLoadHint(!shouldAnimate); - setAdapter(adapter); - setCurrentItem(adapter.getItemPosition(page), false); setVisibility(VISIBLE); + // Don't show the tabs strip until we have the + // list of pages in place. + mTabStrip.setVisibility(View.INVISIBLE); + + // Load list of pages from configuration + lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks); + if (shouldAnimate) { animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { @Override @@ -254,4 +277,66 @@ public class HomePager extends ViewPager { return super.onInterceptTouchEvent(event); } + + private void updateUiFromPageEntries(List pageEntries) { + // We only care about the adapter if HomePager is currently + // loaded, which means it's visible in the activity. + if (!mLoaded) { + return; + } + + if (mDecor != null) { + mDecor.removeAllPagerViews(); + } + + final HomeAdapter adapter = (HomeAdapter) getAdapter(); + + // Disable loading until the final current item is defined + // after loading the page entries. This is to stop any temporary + // active item from loading. + boolean originalCanLoadHint = adapter.getCanLoadHint(); + adapter.setCanLoadHint(false); + + // Update the adapter with the new page entries + adapter.update(pageEntries); + + final int count = (pageEntries != null ? pageEntries.size() : 0); + mTabStrip.setVisibility(count > 0 ? View.VISIBLE : View.INVISIBLE); + + // Use the default page as defined in the HomePager's configuration + // if the initial page wasn't explicitly set by the show() caller. + if (mInitialPage != null) { + setCurrentItem(adapter.getItemPosition(mInitialPage), false); + mInitialPage = null; + } else { + for (int i = 0; i < count; i++) { + final PageEntry pageEntry = pageEntries.get(i); + if (pageEntry.isDefault()) { + setCurrentItem(i, false); + break; + } + } + } + + // Restore canLoadHint now that we have the final + // state in HomePager. + adapter.setCanLoadHint(originalCanLoadHint); + } + + private class ConfigLoaderCallbacks implements LoaderCallbacks> { + @Override + public Loader> onCreateLoader(int id, Bundle args) { + return new HomeConfigLoader(mContext, mConfig); + } + + @Override + public void onLoadFinished(Loader> loader, List pageEntries) { + updateUiFromPageEntries(pageEntries); + } + + @Override + public void onLoaderReset(Loader> loader) { + updateUiFromPageEntries(null); + } + } } diff --git a/mobile/android/base/home/ListPage.java b/mobile/android/base/home/ListPage.java new file mode 100644 index 000000000000..f3c911095b84 --- /dev/null +++ b/mobile/android/base/home/ListPage.java @@ -0,0 +1,185 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.home; + +import org.mozilla.gecko.R; +import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; +import org.mozilla.gecko.home.HomeConfig.PageEntry; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.database.Cursor; +import android.os.Bundle; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +import java.util.EnumSet; + +/** + * Fragment that displays custom lists. + */ +public class ListPage extends HomeFragment { + // Cursor loader ID for the lists + private static final int LOADER_ID_LIST = 0; + + // The page entry associated with this page + private PageEntry mPageEntry; + + // Adapter for the list + private HomeListAdapter mAdapter; + + // The view shown by the fragment + private ListView mList; + + // Callbacks used for the list loader + private CursorLoaderCallbacks mCursorLoaderCallbacks; + + // On URL open listener + private OnUrlOpenListener mUrlOpenListener; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + mUrlOpenListener = (OnUrlOpenListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement HomePager.OnUrlOpenListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + + mUrlOpenListener = null; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Bundle args = getArguments(); + if (args != null) { + mPageEntry = (PageEntry) args.getParcelable(HomePager.PAGE_ENTRY_ARG); + } + + if (mPageEntry == null) { + throw new IllegalStateException("Can't create a ListPage without a PageEntry"); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + mList = new HomeListView(getActivity()); + return mList; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + registerForContextMenu(mList); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mList = null; + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + // Detach and reattach the fragment as the layout changes. + if (isVisible()) { + getFragmentManager().beginTransaction() + .detach(this) + .attach(this) + .commitAllowingStateLoss(); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAdapter = new HomeListAdapter(getActivity(), null); + mList.setAdapter(mAdapter); + + // Create callbacks before the initial loader is started. + mCursorLoaderCallbacks = new CursorLoaderCallbacks(); + loadIfVisible(); + } + + @Override + protected void load() { + getLoaderManager().initLoader(LOADER_ID_LIST, null, mCursorLoaderCallbacks); + } + + /** + * Cursor loader for the lists. + */ + private static class HomeListLoader extends SimpleCursorLoader { + public HomeListLoader(Context context) { + super(context); + } + + @Override + public Cursor loadCursor() { + // Do nothing + return null; + } + } + + /** + * Cursor adapter for the list. + */ + private class HomeListAdapter extends CursorAdapter { + public HomeListAdapter(Context context, Cursor cursor) { + super(context, cursor, 0); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + final TwoLinePageRow row = (TwoLinePageRow) view; + row.updateFromCursor(cursor); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_item_row, parent, false); + } + } + + /** + * LoaderCallbacks implementation that interacts with the LoaderManager. + */ + private class CursorLoaderCallbacks implements LoaderCallbacks { + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new HomeListLoader(getActivity()); + } + + @Override + public void onLoadFinished(Loader loader, Cursor c) { + mAdapter.swapCursor(c); + } + + @Override + public void onLoaderReset(Loader loader) { + mAdapter.swapCursor(null); + } + } +} diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 476cb6c4855d..2bb5769946ea 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -210,11 +210,15 @@ gbjar.sources += [ 'home/HistoryPage.java', 'home/HomeAdapter.java', 'home/HomeBanner.java', + 'home/HomeConfig.java', + 'home/HomeConfigLoader.java', + 'home/HomeConfigMemBackend.java', 'home/HomeFragment.java', 'home/HomeListView.java', 'home/HomePager.java', 'home/HomePagerTabStrip.java', 'home/LastTabsPage.java', + 'home/ListPage.java', 'home/MostRecentPage.java', 'home/MultiTypeCursorAdapter.java', 'home/PinSiteDialog.java', From e7993891403a4678d179a0c1926b72a045b69811 Mon Sep 17 00:00:00 2001 From: Katie Thomas Date: Thu, 12 Dec 2013 12:48:41 +0100 Subject: [PATCH 153/459] Bug 891194 - Changed BROKEN_WM_Z_ORDER to not be defined for mac. r=dao --HG-- extra : rebase_source : 0947d2d97f3aa4d4707215f0d9bf90ba6f508b59 --- browser/components/sessionstore/src/SessionStore.jsm | 4 +++- browser/modules/RecentWindow.jsm | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm index 8cd1649d111d..c983129091f1 100644 --- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -86,9 +86,11 @@ const BROWSER_EVENTS = [ // The number of milliseconds in a day const MS_PER_DAY = 1000.0 * 60.0 * 60.0 * 24.0; -#ifndef XP_WIN +#ifdef XP_UNIX +#ifndef XP_MACOSX #define BROKEN_WM_Z_ORDER #endif +#endif Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); diff --git a/browser/modules/RecentWindow.jsm b/browser/modules/RecentWindow.jsm index 0018b502c70e..c328b6e44d50 100644 --- a/browser/modules/RecentWindow.jsm +++ b/browser/modules/RecentWindow.jsm @@ -11,9 +11,11 @@ const Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); -#ifndef XP_WIN +#ifdef XP_UNIX +#ifndef XP_MACOSX #define BROKEN_WM_Z_ORDER #endif +#endif this.RecentWindow = { /* From aaf58d52c30f37b9eb017a0e984a34a2ce2addc8 Mon Sep 17 00:00:00 2001 From: Victor Porof Date: Thu, 12 Dec 2013 14:59:09 +0200 Subject: [PATCH 154/459] Bug 949249 - Conditional breakpoint should be kept after to toggle breakpoint checkbox, r=past --- .../devtools/debugger/debugger-controller.js | 8 +- browser/devtools/debugger/test/browser.ini | 1 + .../browser_dbg_conditional-breakpoints-03.js | 82 +++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-03.js diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js index 7d91b658e3b2..ea42fb5ecd64 100644 --- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -1698,7 +1698,13 @@ Breakpoints.prototype = { // By default, new breakpoints are always enabled. Disabled breakpoints // are, in fact, removed from the server but preserved in the frontend, // so that they may not be forgotten across target navigations. - this._disabled.delete(identifier); + let disabledPromise = this._disabled.get(identifier); + if (disabledPromise) { + disabledPromise.then(({ conditionalExpression: previousValue }) => { + aBreakpointClient.conditionalExpression = previousValue; + }); + this._disabled.delete(identifier); + } // Preserve information about the breakpoint's line text, to display it // in the sources pane without requiring fetching the source (for example, diff --git a/browser/devtools/debugger/test/browser.ini b/browser/devtools/debugger/test/browser.ini index 1f8fc4bdd843..e1da1f0de68c 100644 --- a/browser/devtools/debugger/test/browser.ini +++ b/browser/devtools/debugger/test/browser.ini @@ -94,6 +94,7 @@ support-files = [browser_dbg_cmd-dbg.js] [browser_dbg_conditional-breakpoints-01.js] [browser_dbg_conditional-breakpoints-02.js] +[browser_dbg_conditional-breakpoints-03.js] [browser_dbg_controller-evaluate-01.js] [browser_dbg_controller-evaluate-02.js] [browser_dbg_debugger-statement.js] diff --git a/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-03.js b/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-03.js new file mode 100644 index 000000000000..b7c080c02a76 --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-03.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that conditional breakpoint expressions survive disabled breakpoints. + */ + +const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html"; + +function test() { + let gTab, gDebuggee, gPanel, gDebugger; + let gSources, gBreakpoints, gLocation; + + initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => { + gTab = aTab; + gDebuggee = aDebuggee; + gPanel = aPanel; + gDebugger = gPanel.panelWin; + gSources = gDebugger.DebuggerView.Sources; + gBreakpoints = gDebugger.DebuggerController.Breakpoints; + + gLocation = { url: gSources.selectedValue, line: 18 }; + + waitForSourceAndCaretAndScopes(gPanel, ".html", 17) + .then(addBreakpoint) + .then(setConditional) + .then(() => { + let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_REMOVED); + toggleBreakpoint(); + return finished; + }) + .then(() => { + let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_ADDED); + toggleBreakpoint(); + return finished; + }) + .then(testConditionalExpressionOnClient) + .then(() => { + let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.CONDITIONAL_BREAKPOINT_POPUP_SHOWING); + openConditionalPopup(); + return finished; + }) + .then(testConditionalExpressionInPopup) + .then(() => resumeDebuggerThenCloseAndFinish(gPanel)) + .then(null, aError => { + ok(false, "Got an error: " + aError.message + "\n" + aError.stack); + }); + + gDebuggee.ermahgerd(); + }); + + function addBreakpoint() { + return gPanel.addBreakpoint(gLocation); + } + + function setConditional(aClient) { + aClient.conditionalExpression = "hello"; + } + + function toggleBreakpoint() { + EventUtils.sendMouseEvent({ type: "click" }, + gDebugger.document.querySelector(".dbg-breakpoint-checkbox"), + gDebugger); + } + + function openConditionalPopup() { + EventUtils.sendMouseEvent({ type: "click" }, + gDebugger.document.querySelector(".dbg-breakpoint"), + gDebugger); + } + + function testConditionalExpressionOnClient() { + return gBreakpoints._getAdded(gLocation).then(aClient => { + is(aClient.conditionalExpression, "hello", "The expression is correct (1)."); + }); + } + + function testConditionalExpressionInPopup() { + let textbox = gDebugger.document.getElementById("conditional-breakpoint-panel-textbox"); + is(textbox.value, "hello", "The expression is correct (2).") + } +} From 0e20438b6203c52393b42b82e7d5d70335d66a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Thu, 12 Dec 2013 14:07:55 +0100 Subject: [PATCH 155/459] Bug 847955 - Let _getMostRecentBrowserWindow utilize RecentWindow.getMostRecentBrowserWindow. r=ttaubert --- .../sessionstore/src/SessionStore.jsm | 39 +++---------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm index c983129091f1..218eb3eadf64 100644 --- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -86,12 +86,6 @@ const BROWSER_EVENTS = [ // The number of milliseconds in a day const MS_PER_DAY = 1000.0 * 60.0 * 60.0 * 24.0; -#ifdef XP_UNIX -#ifndef XP_MACOSX -#define BROKEN_WM_Z_ORDER -#endif -#endif - Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", this); @@ -106,14 +100,16 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup", XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager", "@mozilla.org/gfx/screenmanager;1", "nsIScreenManager"); -XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager", - "resource:///modules/devtools/scratchpad-manager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities", "resource:///modules/sessionstore/DocShellCapabilities.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Messenger", "resource:///modules/sessionstore/Messenger.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PageStyle", "resource:///modules/sessionstore/PageStyle.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", + "resource:///modules/RecentWindow.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager", + "resource:///modules/devtools/scratchpad-manager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "SessionSaver", "resource:///modules/sessionstore/SessionSaver.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage", @@ -3217,32 +3213,7 @@ let SessionStoreInternal = { * @returns Window reference */ _getMostRecentBrowserWindow: function ssi_getMostRecentBrowserWindow() { - var win = Services.wm.getMostRecentWindow("navigator:browser"); - if (!win) - return null; - if (!win.closed) - return win; - -#ifdef BROKEN_WM_Z_ORDER - win = null; - var windowsEnum = Services.wm.getEnumerator("navigator:browser"); - // this is oldest to newest, so this gets a bit ugly - while (windowsEnum.hasMoreElements()) { - let nextWin = windowsEnum.getNext(); - if (!nextWin.closed) - win = nextWin; - } - return win; -#else - var windowsEnum = - Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true); - while (windowsEnum.hasMoreElements()) { - win = windowsEnum.getNext(); - if (!win.closed) - return win; - } - return null; -#endif + return RecentWindow.getMostRecentBrowserWindow({ allowPopups: true }); }, /** From 84e2a2de14af60feeb5988e06d496c4b96477002 Mon Sep 17 00:00:00 2001 From: Brian Grinstead Date: Wed, 11 Dec 2013 10:22:21 -0600 Subject: [PATCH 156/459] Bug 947346: Theme updates for Windows --- browser/themes/linux/devtools/splitview.css | 6 ++---- browser/themes/osx/devtools/splitview.css | 6 ++---- browser/themes/shared/devtools/common.css | 5 +++-- browser/themes/shared/devtools/toolbars.inc.css | 10 ++++++++++ browser/themes/windows/devtools/splitview.css | 7 ++----- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/browser/themes/linux/devtools/splitview.css b/browser/themes/linux/devtools/splitview.css index a24d099ee22f..0eb870c9fb10 100644 --- a/browser/themes/linux/devtools/splitview.css +++ b/browser/themes/linux/devtools/splitview.css @@ -68,12 +68,10 @@ /* Toolbars */ -.devtools-toolbar { - height: 26px; +.splitview-main > .devtools-toolbar { background-origin: border-box; background-clip: border-box; border-bottom: 1px solid hsla(210,8%,5%,.65); - padding: 3px; } .splitview-main > toolbar:-moz-locale-dir(ltr) { @@ -84,7 +82,7 @@ border-left: 1px solid hsla(210,8%,5%,.5); } -.devtools-toolbarbutton { +.splitview-main > .devtools-toolbarbutton { font-size: 11px; padding: 0 8px; width: auto; diff --git a/browser/themes/osx/devtools/splitview.css b/browser/themes/osx/devtools/splitview.css index a24d099ee22f..0eb870c9fb10 100644 --- a/browser/themes/osx/devtools/splitview.css +++ b/browser/themes/osx/devtools/splitview.css @@ -68,12 +68,10 @@ /* Toolbars */ -.devtools-toolbar { - height: 26px; +.splitview-main > .devtools-toolbar { background-origin: border-box; background-clip: border-box; border-bottom: 1px solid hsla(210,8%,5%,.65); - padding: 3px; } .splitview-main > toolbar:-moz-locale-dir(ltr) { @@ -84,7 +82,7 @@ border-left: 1px solid hsla(210,8%,5%,.5); } -.devtools-toolbarbutton { +.splitview-main > .devtools-toolbarbutton { font-size: 11px; padding: 0 8px; width: auto; diff --git a/browser/themes/shared/devtools/common.css b/browser/themes/shared/devtools/common.css index 9bd0e1abc6c0..a8208dc9fe1e 100644 --- a/browser/themes/shared/devtools/common.css +++ b/browser/themes/shared/devtools/common.css @@ -25,8 +25,9 @@ .devtools-horizontal-splitter { -moz-appearance: none; background-image: none; - border-top: 1px solid black; - border-bottom-width: 0; + background-color: transparent; + border: 1px solid black; + border-width: 1px 0 0 0; min-height: 3px; height: 3px; margin-bottom: -3px; diff --git a/browser/themes/shared/devtools/toolbars.inc.css b/browser/themes/shared/devtools/toolbars.inc.css index aca52970aab7..fed04f63c5dd 100644 --- a/browser/themes/shared/devtools/toolbars.inc.css +++ b/browser/themes/shared/devtools/toolbars.inc.css @@ -39,6 +39,10 @@ outline-offset: -4px; } +.devtools-toolbarbutton > .toolbarbutton-icon { + margin: 0; +} + .devtools-toolbarbutton:not([label]) { min-width: 32px; } @@ -130,7 +134,11 @@ margin: 0 3px; min-height: 22px; border: 1px solid hsla(210,8%,5%,.6); +%ifdef XP_MACOSX border-radius: 20px; +%else + border-radius: 2px; +%endif background-color: transparent; background-image: linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35)); padding: 3px; @@ -322,6 +330,7 @@ #toolbox-controls > toolbarbutton, #toolbox-dock-buttons > toolbarbutton { -moz-appearance: none; + border: none; margin: 0 4px; min-width: 16px; width: 16px; @@ -372,6 +381,7 @@ .command-button { -moz-appearance: none; + border: none; padding: 0 8px; margin: 0; width: 16px; diff --git a/browser/themes/windows/devtools/splitview.css b/browser/themes/windows/devtools/splitview.css index 549f4cdaa302..2688558b189f 100644 --- a/browser/themes/windows/devtools/splitview.css +++ b/browser/themes/windows/devtools/splitview.css @@ -68,13 +68,10 @@ /* Toolbars */ -.devtools-toolbar { - height: 26px; +.splitview-main > .devtools-toolbar { background-origin: border-box; background-clip: border-box; - border-top-width: 0; border-bottom: 1px solid hsla(210,8%,5%,.65); - padding: 3px; } .splitview-main > toolbar:-moz-locale-dir(ltr) { @@ -85,7 +82,7 @@ border-left: 1px solid hsla(210,8%,5%,.5); } -.devtools-toolbarbutton { +.splitview-main > .devtools-toolbarbutton { font-size: 11px; padding: 0 8px; width: auto; From 48eded32c87edcac585fbff61866007568594877 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Thu, 12 Dec 2013 09:14:12 -0800 Subject: [PATCH 157/459] Bug 898843 - Update logos and colors in Metro About flyout [r=rsilveira,dolske] --- browser/branding/aurora/content/jar.mn | 5 ++++ .../aurora/content/metro-about-footer.png | Bin 0 -> 103796 bytes .../aurora/content/metro-about-wordmark.png | Bin 0 -> 5832 bytes .../branding/aurora/content/metro-about.css | 14 +++++++++++ browser/branding/nightly/content/jar.mn | 5 ++++ .../nightly/content/metro-about-footer.png | Bin 0 -> 95994 bytes .../nightly/content/metro-about-wordmark.png | Bin 0 -> 5770 bytes .../branding/nightly/content/metro-about.css | 14 +++++++++++ browser/branding/official/content/jar.mn | 5 ++++ .../official/content/metro-about-footer.png | Bin 0 -> 72541 bytes .../official/content/metro-about-wordmark.png | Bin 0 -> 3751 bytes .../branding/official/content/metro-about.css | 14 +++++++++++ browser/branding/unofficial/content/jar.mn | 5 ++++ .../unofficial/content/metro-about-footer.png | Bin 0 -> 95994 bytes .../content/metro-about-wordmark.png | Bin 0 -> 5770 bytes .../unofficial/content/metro-about.css | 14 +++++++++++ browser/metro/base/content/browser.xul | 5 +++- browser/metro/theme/browser.css | 23 ++++++++++++------ browser/metro/theme/flyoutpanel.css | 2 +- browser/metro/theme/images/about-footer.png | Bin 129698 -> 0 bytes browser/metro/theme/jar.mn | 1 - 21 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 browser/branding/aurora/content/metro-about-footer.png create mode 100644 browser/branding/aurora/content/metro-about-wordmark.png create mode 100644 browser/branding/aurora/content/metro-about.css create mode 100644 browser/branding/nightly/content/metro-about-footer.png create mode 100644 browser/branding/nightly/content/metro-about-wordmark.png create mode 100644 browser/branding/nightly/content/metro-about.css create mode 100644 browser/branding/official/content/metro-about-footer.png create mode 100644 browser/branding/official/content/metro-about-wordmark.png create mode 100644 browser/branding/official/content/metro-about.css create mode 100644 browser/branding/unofficial/content/metro-about-footer.png create mode 100644 browser/branding/unofficial/content/metro-about-wordmark.png create mode 100644 browser/branding/unofficial/content/metro-about.css delete mode 100644 browser/metro/theme/images/about-footer.png diff --git a/browser/branding/aurora/content/jar.mn b/browser/branding/aurora/content/jar.mn index e22cb11c088d..3946423387f3 100644 --- a/browser/branding/aurora/content/jar.mn +++ b/browser/branding/aurora/content/jar.mn @@ -17,3 +17,8 @@ browser.jar: content/branding/identity-icons-brand.png (identity-icons-brand.png) content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png) content/branding/aboutDialog.css (aboutDialog.css) +#ifdef MOZ_METRO + content/branding/metro-about.css (metro-about.css) + content/branding/metro-about-footer.png (metro-about-footer.png) + content/branding/metro-about-wordmark.png (metro-about-wordmark.png) +#endif diff --git a/browser/branding/aurora/content/metro-about-footer.png b/browser/branding/aurora/content/metro-about-footer.png new file mode 100644 index 0000000000000000000000000000000000000000..309912c1aace89e84bfa71cc7c503c36bc569fdf GIT binary patch literal 103796 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 z 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 JU9~Na@;@)F_rm}H literal 0 HcmV?d00001 diff --git a/browser/branding/aurora/content/metro-about-wordmark.png b/browser/branding/aurora/content/metro-about-wordmark.png new file mode 100644 index 0000000000000000000000000000000000000000..06c12f2f72689f28a45dacb1d52a3d2b80059791 GIT binary patch literal 5832 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 literal 0 HcmV?d00001 diff --git a/browser/branding/aurora/content/metro-about.css b/browser/branding/aurora/content/metro-about.css new file mode 100644 index 000000000000..678e2fd74680 --- /dev/null +++ b/browser/branding/aurora/content/metro-about.css @@ -0,0 +1,14 @@ +/* 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/. */ + +#about-flyoutpanel { + background-color: #331e54; + color: white; +} + +#about-policy-label:hover, +#about-policy-label:active { + background: #181327; +} + diff --git a/browser/branding/nightly/content/jar.mn b/browser/branding/nightly/content/jar.mn index e22cb11c088d..3946423387f3 100644 --- a/browser/branding/nightly/content/jar.mn +++ b/browser/branding/nightly/content/jar.mn @@ -17,3 +17,8 @@ browser.jar: content/branding/identity-icons-brand.png (identity-icons-brand.png) content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png) content/branding/aboutDialog.css (aboutDialog.css) +#ifdef MOZ_METRO + content/branding/metro-about.css (metro-about.css) + content/branding/metro-about-footer.png (metro-about-footer.png) + content/branding/metro-about-wordmark.png (metro-about-wordmark.png) +#endif diff --git a/browser/branding/nightly/content/metro-about-footer.png b/browser/branding/nightly/content/metro-about-footer.png new file mode 100644 index 0000000000000000000000000000000000000000..8e507f174085347b918c8ac422374613713593be GIT binary patch literal 95994 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 VSUM=f!r=e_002ovPDHLkV1hxMcQ60| literal 0 HcmV?d00001 diff --git a/browser/branding/nightly/content/metro-about-wordmark.png b/browser/branding/nightly/content/metro-about-wordmark.png new file mode 100644 index 0000000000000000000000000000000000000000..94569858eeede9c42297637af3396b39b13587c9 GIT binary patch literal 5770 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 E02&EnSpWb4 literal 0 HcmV?d00001 diff --git a/browser/branding/nightly/content/metro-about.css b/browser/branding/nightly/content/metro-about.css new file mode 100644 index 000000000000..ae59ca779e3c --- /dev/null +++ b/browser/branding/nightly/content/metro-about.css @@ -0,0 +1,14 @@ +/* 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/. */ + +#about-flyoutpanel { + background-color: #002147; + color: white; +} + +#about-policy-label:hover, +#about-policy-label:active { + background: #0a111c; +} + diff --git a/browser/branding/official/content/jar.mn b/browser/branding/official/content/jar.mn index ae7a189dcd95..e8e152bdfe11 100644 --- a/browser/branding/official/content/jar.mn +++ b/browser/branding/official/content/jar.mn @@ -16,3 +16,8 @@ browser.jar: content/branding/identity-icons-brand.png (identity-icons-brand.png) content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png) content/branding/aboutDialog.css (aboutDialog.css) +#ifdef MOZ_METRO + content/branding/metro-about.css (metro-about.css) + content/branding/metro-about-footer.png (metro-about-footer.png) + content/branding/metro-about-wordmark.png (metro-about-wordmark.png) +#endif diff --git a/browser/branding/official/content/metro-about-footer.png b/browser/branding/official/content/metro-about-footer.png new file mode 100644 index 0000000000000000000000000000000000000000..00df16c62c4ed329a008af9fa041bb8ebebf625e GIT binary patch literal 72541 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 literal 0 HcmV?d00001 diff --git a/browser/branding/official/content/metro-about-wordmark.png b/browser/branding/official/content/metro-about-wordmark.png new file mode 100644 index 0000000000000000000000000000000000000000..496d769da996e7f78e93599378146601a1e566f3 GIT binary patch literal 3751 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 MSRKF%_IaK97tR-uuK)l5 literal 0 HcmV?d00001 diff --git a/browser/branding/official/content/metro-about.css b/browser/branding/official/content/metro-about.css new file mode 100644 index 000000000000..70c5c73b9aa8 --- /dev/null +++ b/browser/branding/official/content/metro-about.css @@ -0,0 +1,14 @@ +/* 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/. */ + +#about-flyoutpanel { + background-color: #0095dd; + color: white; +} + +#about-policy-label:hover, +#about-policy-label:active { + background: #036fa4; +} + diff --git a/browser/branding/unofficial/content/jar.mn b/browser/branding/unofficial/content/jar.mn index e22cb11c088d..3946423387f3 100644 --- a/browser/branding/unofficial/content/jar.mn +++ b/browser/branding/unofficial/content/jar.mn @@ -17,3 +17,8 @@ browser.jar: content/branding/identity-icons-brand.png (identity-icons-brand.png) content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png) content/branding/aboutDialog.css (aboutDialog.css) +#ifdef MOZ_METRO + content/branding/metro-about.css (metro-about.css) + content/branding/metro-about-footer.png (metro-about-footer.png) + content/branding/metro-about-wordmark.png (metro-about-wordmark.png) +#endif diff --git a/browser/branding/unofficial/content/metro-about-footer.png b/browser/branding/unofficial/content/metro-about-footer.png new file mode 100644 index 0000000000000000000000000000000000000000..8e507f174085347b918c8ac422374613713593be GIT binary patch literal 95994 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 VSUM=f!r=e_002ovPDHLkV1hxMcQ60| literal 0 HcmV?d00001 diff --git a/browser/branding/unofficial/content/metro-about-wordmark.png b/browser/branding/unofficial/content/metro-about-wordmark.png new file mode 100644 index 0000000000000000000000000000000000000000..94569858eeede9c42297637af3396b39b13587c9 GIT binary patch literal 5770 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 E02&EnSpWb4 literal 0 HcmV?d00001 diff --git a/browser/branding/unofficial/content/metro-about.css b/browser/branding/unofficial/content/metro-about.css new file mode 100644 index 000000000000..ae59ca779e3c --- /dev/null +++ b/browser/branding/unofficial/content/metro-about.css @@ -0,0 +1,14 @@ +/* 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/. */ + +#about-flyoutpanel { + background-color: #002147; + color: white; +} + +#about-policy-label:hover, +#about-policy-label:active { + background: #0a111c; +} + diff --git a/browser/metro/base/content/browser.xul b/browser/metro/base/content/browser.xul index 7e9fd2cfc941..ad97c2857fad 100644 --- a/browser/metro/base/content/browser.xul +++ b/browser/metro/base/content/browser.xul @@ -9,6 +9,7 @@ + @@ -384,7 +385,9 @@ Desktop browser's sync prefs. -