From 92e5958c610b832f5456775ab64e041c61a62f56 Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Tue, 7 Feb 2012 13:02:32 -0500 Subject: [PATCH 01/28] Bug 707382, merge nsIDOMDataTransfer and nsIDOMNSDataTransfer, r=jonas --- content/base/src/nsContentUtils.cpp | 23 +++------- content/events/src/nsDOMDataTransfer.cpp | 1 - content/events/src/nsDOMDataTransfer.h | 4 +- content/events/src/nsEventStateManager.cpp | 24 ++++------- dom/base/nsDOMClassInfo.cpp | 1 - dom/interfaces/events/nsIDOMDataTransfer.idl | 42 +++++++++---------- .../libeditor/base/nsEditorEventListener.cpp | 5 +-- widget/cocoa/nsChildView.mm | 7 +--- widget/gtk2/nsDragService.cpp | 7 +--- widget/windows/nsDragService.cpp | 11 ++--- widget/windows/nsNativeDragSource.h | 2 +- 11 files changed, 43 insertions(+), 84 deletions(-) diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 561ccc2b2bee..5ae56576462c 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -4874,11 +4874,8 @@ nsContentUtils::SetDataTransferInEvent(nsDragEvent* aDragEvent) } // each event should use a clone of the original dataTransfer. - nsCOMPtr initialDataTransferNS = - do_QueryInterface(initialDataTransfer); - NS_ENSURE_TRUE(initialDataTransferNS, NS_ERROR_FAILURE); - initialDataTransferNS->Clone(aDragEvent->message, aDragEvent->userCancelled, - getter_AddRefs(aDragEvent->dataTransfer)); + initialDataTransfer->Clone(aDragEvent->message, aDragEvent->userCancelled, + getter_AddRefs(aDragEvent->dataTransfer)); NS_ENSURE_TRUE(aDragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY); // for the dragenter and dragover events, initialize the drop effect @@ -4886,14 +4883,10 @@ nsContentUtils::SetDataTransferInEvent(nsDragEvent* aDragEvent) // the event is fired based on the keyboard state. if (aDragEvent->message == NS_DRAGDROP_ENTER || aDragEvent->message == NS_DRAGDROP_OVER) { - nsCOMPtr newDataTransfer = - do_QueryInterface(aDragEvent->dataTransfer); - NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE); - PRUint32 action, effectAllowed; dragSession->GetDragAction(&action); - newDataTransfer->GetEffectAllowedInt(&effectAllowed); - newDataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed)); + aDragEvent->dataTransfer->GetEffectAllowedInt(&effectAllowed); + aDragEvent->dataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed)); } else if (aDragEvent->message == NS_DRAGDROP_DROP || aDragEvent->message == NS_DRAGDROP_DRAGDROP || @@ -4902,13 +4895,9 @@ nsContentUtils::SetDataTransferInEvent(nsDragEvent* aDragEvent) // last value that the dropEffect had. This will have been set in // nsEventStateManager::PostHandleEvent for the last dragenter or // dragover event. - nsCOMPtr newDataTransfer = - do_QueryInterface(aDragEvent->dataTransfer); - NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE); - PRUint32 dropEffect; - initialDataTransferNS->GetDropEffectInt(&dropEffect); - newDataTransfer->SetDropEffectInt(dropEffect); + initialDataTransfer->GetDropEffectInt(&dropEffect); + aDragEvent->dataTransfer->SetDropEffectInt(dropEffect); } return NS_OK; diff --git a/content/events/src/nsDOMDataTransfer.cpp b/content/events/src/nsDOMDataTransfer.cpp index f80274287325..f34d44a3a954 100644 --- a/content/events/src/nsDOMDataTransfer.cpp +++ b/content/events/src/nsDOMDataTransfer.cpp @@ -67,7 +67,6 @@ DOMCI_DATA(DataTransfer, nsDOMDataTransfer) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMDataTransfer) NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer) - NS_INTERFACE_MAP_ENTRY(nsIDOMNSDataTransfer) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DataTransfer) NS_INTERFACE_MAP_END diff --git a/content/events/src/nsDOMDataTransfer.h b/content/events/src/nsDOMDataTransfer.h index 5b2c17c4440b..1b7b72d688fb 100644 --- a/content/events/src/nsDOMDataTransfer.h +++ b/content/events/src/nsDOMDataTransfer.h @@ -67,13 +67,11 @@ struct TransferItem { nsCOMPtr mData; }; -class nsDOMDataTransfer : public nsIDOMDataTransfer, - public nsIDOMNSDataTransfer +class nsDOMDataTransfer : public nsIDOMDataTransfer { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIDOMDATATRANSFER - NS_DECL_NSIDOMNSDATATRANSFER NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMDataTransfer, nsIDOMDataTransfer) diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index 8c8f6d5db040..7978989e7d20 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -3302,13 +3302,10 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext, // the initial dataTransfer is the one from the dragstart event that // was set on the dragSession when the drag began. - nsCOMPtr dataTransfer; + nsCOMPtr dataTransfer; nsCOMPtr initialDataTransfer; dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer)); - nsCOMPtr initialDataTransferNS = - do_QueryInterface(initialDataTransfer); - nsDragEvent *dragEvent = (nsDragEvent*)aEvent; // collect any changes to moz cursor settings stored in the event's @@ -3340,7 +3337,7 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext, // initialized (which is done in nsDOMDragEvent::GetDataTransfer), // so set it from the drag action. We'll still want to filter it // based on the effectAllowed below. - dataTransfer = initialDataTransferNS; + dataTransfer = initialDataTransfer; PRUint32 action; dragSession->GetDragAction(&action); @@ -3390,8 +3387,8 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext, // now set the drop effect in the initial dataTransfer. This ensures // that we can get the desired drop effect in the drop event. - if (initialDataTransferNS) - initialDataTransferNS->SetDropEffectInt(dropEffect); + if (initialDataTransfer) + initialDataTransfer->SetDropEffectInt(dropEffect); } break; @@ -4184,18 +4181,11 @@ nsEventStateManager::UpdateDragDataTransfer(nsDragEvent* dragEvent) // was set on the dragSession when the drag began. nsCOMPtr initialDataTransfer; dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer)); - - // grab the interface that has GetMozCursor. - nsCOMPtr initialDataTransferNS = - do_QueryInterface(initialDataTransfer); - nsCOMPtr eventTransferNS = - do_QueryInterface(dragEvent->dataTransfer); - - if (initialDataTransferNS && eventTransferNS) { + if (initialDataTransfer) { // retrieve the current moz cursor setting and save it. nsAutoString mozCursor; - eventTransferNS->GetMozCursor(mozCursor); - initialDataTransferNS->SetMozCursor(mozCursor); + dragEvent->dataTransfer->GetMozCursor(mozCursor); + initialDataTransfer->SetMozCursor(mozCursor); } } } diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 4651087df700..7766cd95b7e6 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -4118,7 +4118,6 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_BEGIN(DataTransfer, nsIDOMDataTransfer) DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataTransfer) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSDataTransfer) DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(NotifyPaintEvent, nsIDOMNotifyPaintEvent) diff --git a/dom/interfaces/events/nsIDOMDataTransfer.idl b/dom/interfaces/events/nsIDOMDataTransfer.idl index 9cdd3cf4c40d..8517e87bd81b 100644 --- a/dom/interfaces/events/nsIDOMDataTransfer.idl +++ b/dom/interfaces/events/nsIDOMDataTransfer.idl @@ -40,7 +40,7 @@ interface nsIVariant; interface nsIDOMFileList; -[scriptable, uuid(34042440-60A8-4992-AE5C-798E69148955)] +[scriptable, uuid(E929ACB6-435C-4CB8-9AD1-AE3B9353BCC5)] interface nsIDOMDataTransfer : nsISupports { /** @@ -160,28 +160,6 @@ interface nsIDOMDataTransfer : nsISupports * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified */ void addElement(in nsIDOMElement element); -}; - -[scriptable, uuid(AE6DF4E2-FA37-4701-A33E-A5678F826EED)] -interface nsIDOMNSDataTransfer : nsISupports -{ - /* - * Integer version of dropEffect, set to one of the constants in nsIDragService. - */ - [noscript] attribute unsigned long dropEffectInt; - - /* - * Integer version of effectAllowed, set to one or a combination of the - * constants in nsIDragService. - */ - [noscript] attribute unsigned long effectAllowedInt; - - /** - * Creates a copy of the data transfer object, for the given event type and - * user cancelled flag. - */ - [noscript] nsIDOMDataTransfer clone(in PRUint32 aEventType, - in boolean aUserCancelled); /** * The number of items being dragged. @@ -272,4 +250,22 @@ interface nsIDOMNSDataTransfer : nsISupports * drags, or if the caller cannot access this node, this will be null. */ readonly attribute nsIDOMNode mozSourceNode; + + /* + * Integer version of dropEffect, set to one of the constants in nsIDragService. + */ + [noscript] attribute unsigned long dropEffectInt; + + /* + * Integer version of effectAllowed, set to one or a combination of the + * constants in nsIDragService. + */ + [noscript] attribute unsigned long effectAllowedInt; + + /** + * Creates a copy of the data transfer object, for the given event type and + * user cancelled flag. + */ + [noscript] nsIDOMDataTransfer clone(in PRUint32 aEventType, + in boolean aUserCancelled); }; diff --git a/editor/libeditor/base/nsEditorEventListener.cpp b/editor/libeditor/base/nsEditorEventListener.cpp index 652470090a03..6948a30dbfc3 100644 --- a/editor/libeditor/base/nsEditorEventListener.cpp +++ b/editor/libeditor/base/nsEditorEventListener.cpp @@ -831,14 +831,11 @@ nsEditorEventListener::CanDrop(nsIDOMDragEvent* aEvent) NS_ENSURE_TRUE(typeSupported, false); - nsCOMPtr dataTransferNS(do_QueryInterface(dataTransfer)); - NS_ENSURE_TRUE(dataTransferNS, false); - // If there is no source node, this is probably an external drag and the // drop is allowed. The later checks rely on checking if the drag target // is the same as the drag source. nsCOMPtr sourceNode; - dataTransferNS->GetMozSourceNode(getter_AddRefs(sourceNode)); + dataTransfer->GetMozSourceNode(getter_AddRefs(sourceNode)); if (!sourceNode) return true; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index d81d24e31ec4..0db6b1c94a55 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -4597,11 +4597,8 @@ NSEvent* gLastDragMouseDownEvent = nil; if (operation == NSDragOperationNone) { nsCOMPtr dataTransfer; dragService->GetDataTransfer(getter_AddRefs(dataTransfer)); - nsCOMPtr dataTransferNS = - do_QueryInterface(dataTransfer); - - if (dataTransferNS) - dataTransferNS->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE); + if (dataTransfer) + dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE); } mDragService->EndDragSession(true); diff --git a/widget/gtk2/nsDragService.cpp b/widget/gtk2/nsDragService.cpp index 343df8a76e9d..77e57d3afd71 100644 --- a/widget/gtk2/nsDragService.cpp +++ b/widget/gtk2/nsDragService.cpp @@ -1372,11 +1372,8 @@ nsDragService::SourceEndDragSession(GdkDragContext *aContext, } } - nsCOMPtr dataTransfer = - do_QueryInterface(mDataTransfer); - - if (dataTransfer) { - dataTransfer->SetDropEffectInt(dropEffect); + if (mDataTransfer) { + mDataTransfer->SetDropEffectInt(dropEffect); } // Inform the drag session that we're ending the drag. diff --git a/widget/windows/nsDragService.cpp b/widget/windows/nsDragService.cpp index 8c5df7eee926..b6b5b6228799 100644 --- a/widget/windows/nsDragService.cpp +++ b/widget/windows/nsDragService.cpp @@ -317,7 +317,7 @@ nsDragService::StartInvokingDragSession(IDataObject * aDataObj, HRESULT res = ::DoDragDrop(aDataObj, mNativeDragSrc, effects, &winDropRes); // In cases where the drop operation completed outside the application, update - // the source node's nsIDOMNSDataTransfer dropEffect value so it is up to date. + // the source node's nsIDOMDataTransfer dropEffect value so it is up to date. if (!mSentLocalDropEvent) { PRUint32 dropResult; // Order is important, since multiple flags can be returned. @@ -330,14 +330,11 @@ nsDragService::StartInvokingDragSession(IDataObject * aDataObj, else dropResult = DRAGDROP_ACTION_NONE; - nsCOMPtr dataTransfer = - do_QueryInterface(mDataTransfer); - - if (dataTransfer) { + if (mDataTransfer) { if (res == DRAGDROP_S_DROP) // Success - dataTransfer->SetDropEffectInt(dropResult); + mDataTransfer->SetDropEffectInt(dropResult); else - dataTransfer->SetDropEffectInt(DRAGDROP_ACTION_NONE); + mDataTransfer->SetDropEffectInt(DRAGDROP_ACTION_NONE); } } diff --git a/widget/windows/nsNativeDragSource.h b/widget/windows/nsNativeDragSource.h index 1d05e1de3493..cd069c3ef3f1 100644 --- a/widget/windows/nsNativeDragSource.h +++ b/widget/windows/nsNativeDragSource.h @@ -84,7 +84,7 @@ protected: ULONG m_cRef; // Data object, hold information about cursor state - nsCOMPtr mDataTransfer; + nsCOMPtr mDataTransfer; // Custom drag cursor HCURSOR m_hCursor; From 873e3dc344e741d6b9b533658242ae7b27d1a1dc Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Tue, 7 Feb 2012 13:02:33 -0500 Subject: [PATCH 02/28] Bug 707386, remove use of drag session from treebodyframe, r=neil --- .../xul/base/src/tree/src/nsTreeBodyFrame.cpp | 46 ++++++++++--------- .../xul/base/src/tree/src/nsTreeBodyFrame.h | 6 +-- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp index 68682cbcb772..fd45024e4ad0 100644 --- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp @@ -86,7 +86,6 @@ #include "nsIURL.h" #include "nsNetUtil.h" #include "nsBoxLayoutState.h" -#include "nsIDragService.h" #include "nsTreeContentView.h" #include "nsTreeUtils.h" #include "nsChildIterator.h" @@ -103,6 +102,7 @@ #include "nsDisplayList.h" #include "nsTreeBoxObject.h" #include "nsRenderingContext.h" +#include "nsIScriptableRegion.h" #ifdef IBMBIDI #include "nsBidiUtils.h" @@ -1948,7 +1948,7 @@ nsTreeBodyFrame::PrefillPropertyArray(PRInt32 aRowIndex, nsTreeColumn* aCol) mScratchArray->AppendElement(nsGkAtoms::sorted); // drag session - if (mSlots && mSlots->mDragSession) + if (mSlots && mSlots->mIsDragging) mScratchArray->AppendElement(nsGkAtoms::dragSession); if (aRowIndex != -1) { @@ -2555,6 +2555,18 @@ nsTreeBodyFrame::GetCursor(const nsPoint& aPoint, return nsLeafBoxFrame::GetCursor(aPoint, aCursor); } +static PRUint32 GetDropEffect(nsGUIEvent* aEvent) +{ + NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type"); + nsDragEvent* dragEvent = static_cast(aEvent); + nsContentUtils::SetDataTransferInEvent(dragEvent); + + PRUint32 action = 0; + if (dragEvent->dataTransfer) + dragEvent->dataTransfer->GetDropEffectInt(&action); + return action; +} + NS_IMETHODIMP nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, nsGUIEvent* aEvent, @@ -2593,17 +2605,10 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, } // Cache the drag session. - nsCOMPtr dragService = - do_GetService("@mozilla.org/widget/dragservice;1"); - dragService->GetCurrentSession(getter_AddRefs(mSlots->mDragSession)); - NS_ASSERTION(mSlots->mDragSession, "can't get drag session"); - - if (mSlots->mDragSession) - mSlots->mDragSession->GetDragAction(&mSlots->mDragAction); - else - mSlots->mDragAction = 0; + mSlots->mIsDragging = true; mSlots->mDropRow = -1; mSlots->mDropOrient = -1; + mSlots->mDragAction = GetDropEffect(aEvent); } else if (aEvent->message == NS_DRAGDROP_OVER) { // The mouse is hovering over this tree. If we determine things are @@ -2627,8 +2632,7 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, // Find out the current drag action PRUint32 lastDragAction = mSlots->mDragAction; - if (mSlots->mDragSession) - mSlots->mDragSession->GetDragAction(&mSlots->mDragAction); + mSlots->mDragAction = GetDropEffect(aEvent); // Compute the row mouse is over and the above/below/on state. // Below we'll use this to see if anything changed. @@ -2698,11 +2702,9 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, } } - NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type"); - nsDragEvent* dragEvent = static_cast(aEvent); - nsContentUtils::SetDataTransferInEvent(dragEvent); - + // The dataTransfer was initialized by the call to GetDropEffect above. bool canDropAtNewLocation = false; + nsDragEvent* dragEvent = static_cast(aEvent); mView->CanDrop(mSlots->mDropRow, mSlots->mDropOrient, dragEvent->dataTransfer, &canDropAtNewLocation); @@ -2714,10 +2716,9 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, } } - // Alert the drag session we accept the drop. We have to do this every time - // since the |canDrop| attribute is reset before we're called. - if (mSlots->mDropAllowed && mSlots->mDragSession) - mSlots->mDragSession->SetCanDrop(true); + // Indicate that the drop is allowed by preventing the default behaviour. + if (mSlots->mDropAllowed) + *aEventStatus = nsEventStatus_eConsumeNoDefault; } else if (aEvent->message == NS_DRAGDROP_DROP) { // this event was meant for another frame, so ignore it @@ -2741,6 +2742,7 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, mView->Drop(mSlots->mDropRow, mSlots->mDropOrient, dragEvent->dataTransfer); mSlots->mDropRow = -1; mSlots->mDropOrient = -1; + mSlots->mIsDragging = false; *aEventStatus = nsEventStatus_eConsumeNoDefault; // already handled the drop } else if (aEvent->message == NS_DRAGDROP_EXIT) { @@ -2756,7 +2758,7 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, } else mSlots->mDropAllowed = false; - mSlots->mDragSession = nsnull; + mSlots->mIsDragging = false; mSlots->mScrollLines = 0; // If a drop is occuring, the exit event will fire just before the drop // event, so don't reset mDropRow or mDropOrient as these fields are used diff --git a/layout/xul/base/src/tree/src/nsTreeBodyFrame.h b/layout/xul/base/src/tree/src/nsTreeBodyFrame.h index 474d80436422..c25408738b05 100644 --- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.h +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.h @@ -50,7 +50,6 @@ #include "nsITreeView.h" #include "nsICSSPseudoComparator.h" #include "nsIScrollbarMediator.h" -#include "nsIDragSession.h" #include "nsITimer.h" #include "nsIReflowCallback.h" #include "nsTArray.h" @@ -535,6 +534,9 @@ protected: // Data Members // If the drop is actually allowed here or not. bool mDropAllowed; + // True while dragging over the tree. + bool mIsDragging; + // The row the mouse is hovering over during a drop. PRInt32 mDropRow; @@ -547,8 +549,6 @@ protected: // Data Members // The drag action that was received for this slot PRUint32 mDragAction; - nsCOMPtr mDragSession; - // Timer for opening/closing spring loaded folders or scrolling the tree. nsCOMPtr mTimer; From 7e07fb61e131fa0051cc198c534da17f3af04b72 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Tue, 7 Feb 2012 13:06:05 -0500 Subject: [PATCH 03/28] Bug 716594 - Switch nsBinaryInputStream::ReadBytes back to using the fallible allocator because it correctly checks for allocation errors and streams are untrusted input we should try to work with, r=dbaron --- xpcom/base/nsIMemory.idl | 7 +++---- xpcom/build/nsXPCOM.h | 5 ++--- xpcom/io/nsBinaryStream.cpp | 11 +++++------ 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/xpcom/base/nsIMemory.idl b/xpcom/base/nsIMemory.idl index 5f4fb892dbb5..9fd5ff63cb13 100644 --- a/xpcom/base/nsIMemory.idl +++ b/xpcom/base/nsIMemory.idl @@ -74,8 +74,8 @@ interface nsIMemory : nsISupports { /** * Allocates a block of memory of a particular size. If the memory - * cannot be allocated (because of an out-of-memory condition), null - * is returned. + * cannot be allocated (because of an out-of-memory condition), the + * process aborts. * * @param size - the size of the block to allocate * @result the block of memory @@ -93,8 +93,7 @@ interface nsIMemory : nsISupports * If s is the size of the block to which ptr points, the first * min(s, size) bytes of ptr's block are copied to the new block. * If the allocation succeeds, ptr is freed and a pointer to the - * new block returned. If the allocation fails, ptr is not freed - * and null is returned. The returned value may be the same as ptr. + * new block returned. If the allocation fails, the process aborts. */ [noscript, notxpcom] voidPtr realloc(in voidPtr ptr, in size_t newSize); diff --git a/xpcom/build/nsXPCOM.h b/xpcom/build/nsXPCOM.h index b7a688711245..b3882c23a5ef 100644 --- a/xpcom/build/nsXPCOM.h +++ b/xpcom/build/nsXPCOM.h @@ -240,7 +240,7 @@ NS_NewNativeLocalFile(const nsACString &path, /** * Allocates a block of memory of a particular size. If the memory cannot - * be allocated (because of an out-of-memory condition), null is returned. + * be allocated (because of an out-of-memory condition), the process aborts. * * @param size The size of the block to allocate * @result The block of memory @@ -262,8 +262,7 @@ NS_Alloc(PRSize size); * If s is the size of the block to which ptr points, the first min(s, size) * bytes of ptr's block are copied to the new block. If the allocation * succeeds, ptr is freed and a pointer to the new block is returned. If the - * allocation fails, ptr is not freed and null is returned. The returned - * value may be the same as ptr. + * allocation fails, the process aborts. */ XPCOM_API(void*) NS_Realloc(void* ptr, PRSize size); diff --git a/xpcom/io/nsBinaryStream.cpp b/xpcom/io/nsBinaryStream.cpp index c37bd451b2f9..a1ada145cf66 100644 --- a/xpcom/io/nsBinaryStream.cpp +++ b/xpcom/io/nsBinaryStream.cpp @@ -54,7 +54,6 @@ #include "nsBinaryStream.h" #include "nsCRT.h" #include "nsIStreamBufferAccess.h" -#include "nsMemory.h" #include "prlong.h" #include "nsString.h" #include "nsISerializable.h" @@ -220,7 +219,7 @@ nsBinaryOutputStream::WriteWStringZ(const PRUnichar* aString) if (length <= 64) { copy = temp; } else { - copy = reinterpret_cast(nsMemory::Alloc(byteCount)); + copy = reinterpret_cast(moz_malloc(byteCount)); if (!copy) return NS_ERROR_OUT_OF_MEMORY; } @@ -229,7 +228,7 @@ nsBinaryOutputStream::WriteWStringZ(const PRUnichar* aString) copy[i] = NS_SWAP16(aString[i]); rv = WriteBytes(reinterpret_cast(copy), byteCount); if (copy != temp) - nsMemory::Free(copy); + moz_free(copy); #endif return rv; @@ -725,17 +724,17 @@ nsBinaryInputStream::ReadBytes(PRUint32 aLength, char* *_rval) PRUint32 bytesRead; char* s; - s = reinterpret_cast(nsMemory::Alloc(aLength)); + s = reinterpret_cast(moz_malloc(aLength)); if (!s) return NS_ERROR_OUT_OF_MEMORY; rv = Read(s, aLength, &bytesRead); if (NS_FAILED(rv)) { - nsMemory::Free(s); + moz_free(s); return rv; } if (bytesRead != aLength) { - nsMemory::Free(s); + moz_free(s); return NS_ERROR_FAILURE; } From a7894633903977d648e8211a9b619be66b8e996a Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 7 Feb 2012 13:11:35 -0500 Subject: [PATCH 04/28] Bug 724948 - Remove a pixel check that fails on some devices with larger screen sizes. r=jmaher --- mobile/android/base/tests/test_bug720538.java.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mobile/android/base/tests/test_bug720538.java.in b/mobile/android/base/tests/test_bug720538.java.in index 071fabd852e7..8f19912d6398 100644 --- a/mobile/android/base/tests/test_bug720538.java.in +++ b/mobile/android/base/tests/test_bug720538.java.in @@ -51,11 +51,10 @@ public class test_bug720538 extends BaseTest { meh.doubleTap(100, mDriver.getGeckoWidth() / 2); paintExpecter.blockUntilClear(500); - // and now we check some pixels at the bottom of the view to ensure that we have the page + // and now we check a pixel at the bottom of the view to ensure that we have the page // background and not some checkerboarding. use the second-last row of pixels instead of // the last row because the last row is subject to rounding and clipping errors painted = mDriver.getPaintedSurface(); mAsserter.ispixel(painted[mDriver.getGeckoHeight() - 2][0], 0, 0x80, 0, "Checking bottom-left corner of viewport"); - mAsserter.ispixel(painted[mDriver.getGeckoHeight() - 2][mDriver.getGeckoWidth() - 1], 0, 0x80, 0, "Checking bottom-right corner of viewport"); } } From 783fb0cb1f9794008724c8062b776e9aaddf101b Mon Sep 17 00:00:00 2001 From: Sriram Ramasubramanian Date: Tue, 7 Feb 2012 10:30:38 -0800 Subject: [PATCH 05/28] Bug 721847: Tabs-tray should use transparent placeholders for thumbnails. [r=mfinkle] --- mobile/android/base/Tab.java | 2 +- .../drawable-hdpi/tab_thumbnail_default.png | Bin 3954 -> 3720 bytes .../drawable-hdpi/tab_thumbnail_shadow.png | Bin 810 -> 503 bytes .../tab_thumbnail_default.png | Bin 5378 -> 5228 bytes .../tab_thumbnail_shadow.png | Bin 889 -> 681 bytes .../drawable/tab_thumbnail_default.png | Bin 2388 -> 2349 bytes .../drawable/tab_thumbnail_shadow.png | Bin 586 -> 369 bytes .../layout/abouthome_topsite_item.xml | 2 +- .../base/resources/layout/tabs_row.xml | 2 +- 9 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 21672a08d2ea..eec8f3cee740 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -60,7 +60,7 @@ import java.util.List; public final class Tab { private static final String LOGTAG = "GeckoTab"; private static final int kThumbnailWidth = 136; - private static final int kThumbnailHeight = 77; + private static final int kThumbnailHeight = 78; private static float sMinDim = 0; private static float sDensity = 1; diff --git a/mobile/android/base/resources/drawable-hdpi/tab_thumbnail_default.png b/mobile/android/base/resources/drawable-hdpi/tab_thumbnail_default.png index 4004ab0edc98f4406e3d0ced69b53ec9d95131a5..e0c83f6743cfd357cc537321d0143229a6e70786 100644 GIT binary patch literal 3720 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 delta 785 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 VgJj9k7*B8kwAA(0Dpc&A{~N)IdENj3 literal 5378 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 diff --git a/mobile/android/base/resources/drawable-xhdpi-v11/tab_thumbnail_shadow.png b/mobile/android/base/resources/drawable-xhdpi-v11/tab_thumbnail_shadow.png index 3b715b9aae6e6ed7c005a02e5892fd4297d3b704..fb57ed8b12d3f877952d335e3d09007cce6ea5c7 100644 GIT binary patch literal 681 zcmeAS@N?(olHy`uVBq!ia0y~yU=#te7jUov$+Iz&*8wTX64!{5;QX|b^2DN42FH~A zq*MjZ+{E1KTp)4mp~~483M?%Ri#sA^WHx`} zkXv}+Dsu(bb*J+egiFr8c<|^^TtOMQ;wK*_uM0& zcoJrbRCn*4=6iY0`moirx8=@Wq>+}#>|@Atc;D+?&z~0koDmvYdi8tll9NKkgP1eGs9! zbbal%#Zu9ezSq|8znd2?)_wGH-uBzpq6SBQq;85^zyA5>pRRJPmc9SqCFQOukxN+g gJwm+thd=^DR&wNX!LMDOz_iEU>FVdQ&MBb@0P2g{2LJ#7 literal 889 zcmeAS@N?(olHy`uVBq!ia0y~yV3Yu|7jUov$?FNl3P4J-#5JNMI6tkVJh3R1!7(L2 zDOJHUH!(dmC^a#qvhZZ84FdzSw5N+>NX4x;cWtvp0!11g{=79)_ECDWV#kCu@gq*r zr`N7+UYel)PhjJ=Z}KbF&P`a}I(6#O3AYwR9{GB&v86-9=@}1?4Zn|&ZvMxjb8p_| zM23b2iyYlr605;*?4xOH``N6my9$5Q7OsE!?VXiy0*`%P|GoQrZtc+(&)vSS{={plgp1=S!qKVOBZUPG%SV6;N&a{db z<=0E)e(}yoX!z-4z{Grn^_XuQP$$qNpiWUvgM$ozBqhKa(~jCo0A;|g5(X(#aLV(Z ztbKXg+UExu!c-g*7#am7KqRAuu!MdvGZXWfE`DpKjlw+#7?>cQfl|V3$E4o^U87(C zmN2+~fQSF@yWjURfnGv2MqZRbiRb*2wXbh^GAurwYj}{MP`Ibsz=7p^Lc@&%GeDLB zgG9yP@bZ<@Pd^2R36cQ<5a$R;B%C_%!^9Nmc@{Q@goYmncCa(MoXGvSgAL{%sKbKU zj{Qz(;Mg}?3g~ZR-X2ai0g#s`>^#X043I{~AGQYX4l)SdX=Vfm4p0ghZZp}_euOpd zdtbYn0UF-NSeU_11BSAi1TZ3j7W){0qG3KLERbSGloRX#ptc{sSspLE@6hqKEW3&E zk(va^Uo#RD_FUxaFxb=eL;@JE(9i-?KGG8Uq0EAfD~l8I-j?mYR>+Wi($_P0=iMdp zufM#v`g?r&m*2A&GoD-N9~-&!{_j+-(^J13pIma+^ZLBajOSiz#L8d#`er%y7NY)x%|?OZBI@j5;BQJtC4V328GY&??LB5ERdurLBW zkkSja#nk8E28J)p*FTScT<lBTfq}-+Jfb;R{%P^(9tvZu!~z&^qpG{mxGf-c&^eZ-#HrzMsAE;d{h3aOig)ke4UBFYN^Z za`X?VK#E-wSwvbEjMPG)AR=iXO&Ja7|EhnS?C$^-S=5tvDS z4{JOU+-}?8$KuEr{}^{VmN&uSa9m?!H?+VxLdh)Y+0UP$GZ7mRP+1x8mk}otnz(`k zZMIouT@f*_o1X$lv1s&l)T4npBSXVnF#?kP+{@QDlh&j!FV@r2YWitm{iFN$-Uq{T zyo;xLPRzd zlg#Q?R?+OSt(CGiYcp%@ZfO{|+RfljHa;JEwYJa`p5NzwKKhiAVL{^RCTn>9$8;9F zZJF^-Hm#lDcO?N=@%ZsqjJwa6Q>knkw6_=NT^8_0zhA=ZCv3EPgq4lTZ?J~m6a z4?dyQ*47$kqh9oTo>kc9U-nb~I{)n~X9=&2AzNy+i6s*V1a17!r=2_73x3q`iW0!% z*=jT%uVZ9ngx;$t-hAgM-lozcte~XLU)O3k>0^*vHqPa42fJam<8Ii`v!;6A>4*Z5 z!?}P%=bg;F&t$aMTvch*l0F1%5ueQnY|~;*``j%h5hGqZlC}~Gii&qn!nq)g01w_` z_Kzdes>gPH+(T&G8=Q|?38cr20r+;J__XyVZw7N@&mM?fWMb12J8bhiZUdC|_1;lWaiSttJ?CiV6n3p4CbI!GeH&@pHadXQ?H5| zoc_#Ew!eaZUuNJ~l={mA$s0>N!bx7MxMCbX?&>C`MP1c(+UA4rpK_jP>>OIA?}-a^Ic44+uI&nhdU- zVLnoV5)R6XQLdEL_xJ)H9wSJY>jX00oPVuj6b?#Vl|8W414FoZ9^q-e^u9aN(go*_ zF5(zUp&_ZpqR4jhJDNyE%D99@QY}&sMj#X-i&4-N^KwSeS-q*sZ_WK^KO7a2uHpov z++gfe9;dwiXqbW@5qH`l1MWkLmXF037rE_C^YEqv$&>-ACoPDQWLm% z7>~4~|FU`eul-8E&kpJ^E&ALy>Aec8l2*NU?zqfi|8?pinA6xWu$|xrmZ$_ zPe)cBYEs>_o75q!o_bbGj(Yu9RcUc?Aaho3WU8}7W#{MKH-{M?!%Q5Z(=k2SHNfSa`}`3|?t9dETZHUl6E4>?GPl2V zj~!m77UgQlAX8a zOK*vyvR*(S8AG=~wPtFP*MscCL`4m=As}U5T$3|H7%VXU7+aaB^#!r41G;jmZEuZ9 zVr<{<=^{$KuG@LKgcgFInj9{t9Bx!JGGtJ(mH&wPabhWVhrSgSVo3M&!1$`Ie=uRY z0N~uAN5fP22YL!loXw^ZbHAFaf_b8h>iEZvrI{-Y`x7xt0cL?*T8iAg zr~^qPe;eyQeXca4tItI_Fps;w?ret5+=9Gs#L869(^tZ#&4sJqmSqJw0lmVGrIG%m zOWBHkjtzxD)`G3UHXCroSdik36)fP;s5{u delta 2375 zcmV-N3ApyH64VkQiBL{Q4GJ0x0000DNk~Le0001t0000`2nGNE0Lwg`YLOu*fA&d4 zK~#9!?3`;)8~GW<$76eZzhIk-v4JHfghFs23uF_>ZKo+SQYlIS73ngN$YDqX z42ggt5ileIhD0J@NCXUtfFThuq!N}wsZ`QTFNqw6M8J>;7!mMj5Yly^oJEKtd7Fl_V ztWcOX88m*TSO{&7%L|NyPiLICL>5`G;AL{Wr`BE*?(XhR03v~ak)YPr)?`|(wz#?O z@p$yJ{-us0%k>$w+LT$xe+?H4`Huul(k|N~O{)m&?V)5ex?7V`F2B*?gg4ZZX)KOeDtDZ0<>Ie{C&)?%cTq0V4s; z&dzc_{Qdc~<`p z(O8T9n&CL?ojZ4?>Rc_qAM?Iu*rM=PyH)=;xJ}$_HkU=2lHa;@t5S+zY+_=f^5d?L zuaAt58<0d}Qg`wQCJ1Sqr-zE0f72c%FZc z1|frSdUsokzW0-^BPe%mucqE>{d4aY;5I2hCHYV+*|r)jGUsY+Xz&GAtj}LfG5lI; zy)@;KW_^KRAEPq%=``{`o;h>Iijwp6Ajd@63L^B@-@bkOe=j$0-fTMQZmlvI4gC)v zKV?v|zPWhuVo>V9NOs~b7K`N+3WcTr-0Awdy817dBgwDCC6q{gRb#h*`K!+c^coe% zXS3O{gB}wZC&#kv1agrx5D0uHZgYK9$Nct-&-x?DOfOtbLk?4_wN@@?WHQdC?{r&xY`;9Z1D?XoJiFf)}6VvO!C}eM|s;Zs=7=Ew)B9X}3j*gCQgy3Po zbK`^4G#nj&J|_JPBgIEFQH~4_hvV|5!|V!-(ZGCufBj0+oG)O^r1(c>gErXI)TDun z>^sMT>lQ+IF2ZFt9ZiFbBS`&`SIIfd!oq@DZ?d%G1{w}BUd<`)Cc?`PySlpS;TAQa zQ0UEm`=DzQCj`S#aVwN3#X_MV zUhit6(I^**M6|hFPLAUUa3CIV{ad*T1Ohs@+ugVQ94J*j5g4gmQ%O4vf~XF`a~wBq zf6w6C*4i@g=Wp*hP4kPxN9ycT;>&fQ|7Nt@iJawYwPsffO3vAyPU>RL5 z*M%KQ-OGxK3Q30;$pNF)YI(G_<0g~o#$Mx020e5AN`L3jQ%~n3k9WPban{;3=TCP} zs5s?!W}{&RF5?spWg9H&oWQu@E>+u)e;Eu0#YZU$XzdA@64D~_g#+D^A2-#p*TAYu zEAz|im%}l%*XtdYAoy|9UpLe_n^h{6qF5}hC6meRb1;^!lPB7D=$uAS!b~V2B=Pf`{kl=fB4IYVkd%(dcl~yKwV8iA2J`eLRk-^aWNNfA#hC zr*;Uz7JMEBXgMhZBgw}b)M|BNWo2a;Zm{R$lVbRFI-S3Piv+M90zeaJvA;zI@^8x~ zYB9H9oW*Q1_3Z-cw9#nPGZL31Ck>Dx-lO~J!0RKxGqBfKK9zRQF9v_73WhxuCjAR! zAT{6-uoUw7y!cxs&B)lRo=7a-f27rDj{f@Qz>Z}O~7EoS%2H&gBxZ;}%Ron{!@!dkUD6$q|6mX9Fqx=v|yM zD3{A4QU^vBkH=diS!-FALzmNsGLkSD3}4`V!+!fjV+m$x_y;B)PcmtAe>!3)KK`?2 zruj(2UT$_5CF^^$*=&c)JeGP%k}NPALDVdh$xfiOs1Zib0-B%fyNdY;;dl^`j3K); z0G!8gnXJ@-k@S!80ZJ5*%!#wM2(y0>1mXLi>d+`XgMe|~WHL3D3oMd%m`!mU=Kwfu z=zeCg-U)!NV6)-BfaM!Ff6NRVECq*o#gviC0+`M61Xz0DI&s|aB-Z;7Qh$DkL!{s$ zV<P>utRIsmZ@I6_!g1B5h|%!eUzf9poKWfBbvpL6)k z4|kdpvk_CKDobFtwca0X{LM-HX^9SCXz`j8{}#33FhPKlEoVpw7!tMBBm#!?|JZGo z>;G{_B8MRnFeCzoM8J>;7!rwqArUYn0)|AukR&68N~NNiUJ^MBiGU#yFeCzoM8J?p tTY*W?Wgs0;QCYD_9bGl);QFTk0{}p}6j^}cEzJM`002ovPDHLkV1jp_dBFex diff --git a/mobile/android/base/resources/drawable/tab_thumbnail_shadow.png b/mobile/android/base/resources/drawable/tab_thumbnail_shadow.png index 0d51494c4ac4c0bf9403524cff9602eb80656b74..aeab5ae6144a2e257b478861a1f98b0a739512d2 100644 GIT binary patch literal 369 zcmeAS@N?(olHy`uVBq!ia0vp^T|gYb!3HFcDZSMNQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS=07#M{;T^vIyZoRo`$aTm-q~W0pcdL^l%lZr66%BfQ_YQYk z?)nnK*P3Uh)|a%fJ9s~TT;}!b^OtZb&iN$s=?KGq+ts_)^mM%FTz!WgLR>TNJ~?I02hMg#0V2}a@2sz z0$CiaV0B>D!UmuY2unc(!D?ZGi2;>5;5Pi9kk{>PNr{0yhEv>DB}$yDz29Calb(Gu wj3Il9hnKS9(vs8n#jgu+pJMz}!EL~BtH{1!{Y+(JU>GuZy85}Sb4q9e071!sj{pDw delta 559 zcmV+~0?_^O0?Gs-iBL{Q4GJ0x0000DNk~Le0001t0000`2nGNE0Lwg`YLOu*f9y#_ zK~#9!?ARf>?he(>7z*Q7v1y5#H z7Uk|N(nJQ{lZ-H;8h!fapRr{(p?`b!_uYrj3m6r*ACJSg=JlJS)9u}#UlxQhP4}w3 zy1Y1PyJm53IhYJgykE3!n;)&!f1t<^n6B%r1k(-EG#ZBa4HNc(iI#(jhKWPV*pjd% zVd&*R8b)9!m9ru+XczJSFwEPM zGz=PsdV$d}Xc(%^Fk#;?F7B8*Qb$`7wj>O_2nc^ z>IKG4FGc#kchie434!4dJY3wlhe5+2FgRB8ge;d!$7&ui^a>1S7^*bP@ZzImHKh&X z?G2b=FvD;_4dWB2D6(3u+{0jop>|*#t8uJG!(fI%!(fI14THe&U$!JK4NzpgUi)$i zY)MoMjD|tOpkWXgY)QO~e}>6FV2ba2 diff --git a/mobile/android/base/resources/layout/tabs_row.xml b/mobile/android/base/resources/layout/tabs_row.xml index b32076c171e6..7c7c5e1b00bf 100644 --- a/mobile/android/base/resources/layout/tabs_row.xml +++ b/mobile/android/base/resources/layout/tabs_row.xml @@ -8,7 +8,7 @@ Date: Tue, 7 Feb 2012 10:55:21 -0500 Subject: [PATCH 06/28] bug 724563 network.http.spdy.enabled = true r=jduell --HG-- extra : rebase_source : 1793638b88fa029d3d16760e5014c646bec8d7fa --- modules/libpref/src/init/all.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 04aee8df81bf..086e3bfe778f 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -804,7 +804,7 @@ pref("network.http.connection-retry-timeout", 250); pref("network.http.fast-fallback-to-IPv4", true); // Try and use SPDY when using SSL -pref("network.http.spdy.enabled", false); +pref("network.http.spdy.enabled", true); pref("network.http.spdy.chunk-size", 4096); pref("network.http.spdy.timeout", 180); pref("network.http.spdy.coalesce-hostnames", true); From c2f9f6404d77c6a35acb58617705203ae0e71584 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 7 Feb 2012 12:57:16 -0600 Subject: [PATCH 07/28] Bug 699565 - Part 1 - for-of loop basics. r=Waldo. --- js/src/frontend/Parser.cpp | 62 ++++++++++++++++---- js/src/frontend/Parser.h | 1 + js/src/jit-test/tests/for-of/decompiler.js | 36 ++++++++++++ js/src/jit-test/tests/for-of/generators-1.js | 11 ++++ js/src/jit-test/tests/for-of/generators-2.js | 15 +++++ js/src/jit-test/tests/for-of/generators-3.js | 18 ++++++ js/src/jit-test/tests/for-of/generators-4.js | 15 +++++ js/src/jit-test/tests/for-of/generators-5.js | 18 ++++++ js/src/jit-test/tests/for-of/generators-6.js | 43 ++++++++++++++ js/src/jit-test/tests/for-of/non-iterable.js | 21 +++++++ js/src/jit-test/tests/for-of/proxy-1.js | 28 +++++++++ js/src/jit-test/tests/for-of/proxy-2.js | 12 ++++ js/src/jit-test/tests/for-of/proxy-3.js | 6 ++ js/src/jit-test/tests/for-of/proxy-4.js | 10 ++++ js/src/jit-test/tests/for-of/syntax-1.js | 22 +++++++ js/src/jit-test/tests/for-of/syntax-2.js | 9 +++ js/src/js.msg | 3 +- js/src/jsapi.h | 7 ++- js/src/jsatom.cpp | 1 + js/src/jsatom.h | 1 + js/src/jsfriendapi.h | 1 + js/src/jsiter.cpp | 34 ++++++++--- js/src/jsopcode.cpp | 21 +++++-- 23 files changed, 370 insertions(+), 25 deletions(-) create mode 100644 js/src/jit-test/tests/for-of/decompiler.js create mode 100644 js/src/jit-test/tests/for-of/generators-1.js create mode 100644 js/src/jit-test/tests/for-of/generators-2.js create mode 100644 js/src/jit-test/tests/for-of/generators-3.js create mode 100644 js/src/jit-test/tests/for-of/generators-4.js create mode 100644 js/src/jit-test/tests/for-of/generators-5.js create mode 100644 js/src/jit-test/tests/for-of/generators-6.js create mode 100644 js/src/jit-test/tests/for-of/non-iterable.js create mode 100644 js/src/jit-test/tests/for-of/proxy-1.js create mode 100644 js/src/jit-test/tests/for-of/proxy-2.js create mode 100644 js/src/jit-test/tests/for-of/proxy-3.js create mode 100644 js/src/jit-test/tests/for-of/proxy-4.js create mode 100644 js/src/jit-test/tests/for-of/syntax-1.js create mode 100644 js/src/jit-test/tests/for-of/syntax-2.js diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index a2b907aff6d3..70e856eade41 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1867,6 +1867,7 @@ Parser::statements() tc->blockNode = saveBlock; pn->pn_pos.end = tokenStream.currentToken().pos.end; + JS_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end); return pn; } @@ -3136,6 +3137,23 @@ Parser::switchStatement() return pn; } +bool +Parser::matchInOrOf(bool *isForOfp) +{ + if (tokenStream.matchToken(TOK_IN)) { + *isForOfp = false; + return true; + } + if (tokenStream.matchToken(TOK_NAME)) { + if (tokenStream.currentToken().name() == context->runtime->atomState.ofAtom) { + *isForOfp = true; + return true; + } + tokenStream.ungetToken(); + } + return false; +} + ParseNode * Parser::forStatement() { @@ -3241,18 +3259,27 @@ Parser::forStatement() ParseNode *forHead; /* initialized by both branches. */ StmtInfo letStmt; /* used if blockObj != NULL. */ ParseNode *pn2, *pn3; /* forHead->pn_kid1 and pn_kid2. */ - if (pn1 && tokenStream.matchToken(TOK_IN)) { + bool forOf; + if (pn1 && matchInOrOf(&forOf)) { /* - * Parse the rest of the for/in head. + * Parse the rest of the for/in or for/of head. * - * Here pn1 is everything to the left of 'in'. At the end of this block, - * pn1 is a decl or NULL, pn2 is the assignment target that receives the - * enumeration value each iteration, and pn3 is the rhs of 'in'. + * Here pn1 is everything to the left of 'in' or 'of'. At the end of + * this block, pn1 is a decl or NULL, pn2 is the assignment target that + * receives the enumeration value each iteration, and pn3 is the rhs of + * 'in'. */ - pn->pn_iflags |= JSITER_ENUMERATE; forStmt.type = STMT_FOR_IN_LOOP; - /* Check that the left side of the 'in' is valid. */ + /* Set pn_iflags and rule out invalid combinations. */ + if (forOf && pn->pn_iflags != 0) { + JS_ASSERT(pn->pn_iflags == JSITER_FOREACH); + reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP); + return NULL; + } + pn->pn_iflags |= (forOf ? JSITER_FOR_OF : JSITER_ENUMERATE); + + /* Check that the left side of the 'in' or 'of' is valid. */ if (forDecl ? (pn1->pn_count > 1 || pn1->isOp(JSOP_DEFCONST) #if JS_HAS_DESTRUCTURING @@ -4323,7 +4350,9 @@ Parser::variables(ParseNodeKind kind, StaticBlockObject *blockObj, VarContext va if (!CheckDestructuring(context, &data, pn2, tc)) return NULL; - if ((tc->flags & TCF_IN_FOR_INIT) && tokenStream.peekToken() == TOK_IN) { + bool ignored; + if ((tc->flags & TCF_IN_FOR_INIT) && matchInOrOf(&ignored)) { + tokenStream.ungetToken(); pn->append(pn2); continue; } @@ -5373,7 +5402,7 @@ Parser::comprehensionTail(ParseNode *kid, uintN blockid, bool isGenexp, /* * FOR node is binary, left is loop control and right is body. Use * index to count each block-local let-variable on the left-hand side - * of the IN. + * of the in/of. */ pn2 = BinaryNode::create(PNK_FOR, tc); if (!pn2) @@ -5427,7 +5456,20 @@ Parser::comprehensionTail(ParseNode *kid, uintN blockid, bool isGenexp, return NULL; } - MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME); + bool forOf; + if (!matchInOrOf(&forOf)) { + reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_IN_AFTER_FOR_NAME); + return NULL; + } + if (forOf) { + if (pn2->pn_iflags != JSITER_ENUMERATE) { + JS_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE)); + reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP); + return NULL; + } + pn2->pn_iflags = JSITER_FOR_OF; + } + ParseNode *pn4 = expr(); if (!pn4) return NULL; diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index f29002cf1ecb..01a31c46389d 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -280,6 +280,7 @@ struct Parser : private AutoGCRooter #endif /* JS_HAS_XML_SUPPORT */ bool setAssignmentLhsOps(ParseNode *pn, JSOp op); + bool matchInOrOf(bool *isForOfp); }; inline bool diff --git a/js/src/jit-test/tests/for-of/decompiler.js b/js/src/jit-test/tests/for-of/decompiler.js new file mode 100644 index 000000000000..7517f1313902 --- /dev/null +++ b/js/src/jit-test/tests/for-of/decompiler.js @@ -0,0 +1,36 @@ +// The decompiler correctly handles for-of loops. + +function tokens(code) { + var arr = []; + var s = code.replace(/\w+|[^\s]/g, function (tok) { arr.push(tok); return ""; }); + assertEq(s.trim(), "", "tokens() should find all tokens in code: " + uneval(code)); + return arr; +} + +function test(code) { + var before = "function f() { " + code + " }"; + var after = eval("(" + before + ")").toString(); + assertEq(tokens(before).join(" "), tokens(after).join(" "), "decompiler failed to round-trip"); +} + +// statements +test("for (a of b) { f(a); }"); +test("for (a of b) { f(a); g(a); }"); + +// for-of with "in" operator nearby +test("for (a of b in c ? c : c.items()) { f(a); }"); + +// destructuring +test("for ([a, b] of c) { a.m(b); }"); + +// for-let-of +test("for (let a of b) { f(a); }"); +test("for (let [a, b] of c) { a.m(b); }"); + +// array comprehensions +test("return [a for (a of b)];"); +test("return [[b, a] for ([a, b] of c.items())];"); + +// generator expressions +test("return (a for (a of b));"); + diff --git a/js/src/jit-test/tests/for-of/generators-1.js b/js/src/jit-test/tests/for-of/generators-1.js new file mode 100644 index 000000000000..05761f5e64eb --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-1.js @@ -0,0 +1,11 @@ +// for-of works with generators. + +function range(n) { + for (var i = 0; i < n; i++) + yield i; +} + +var s = ''; +for (var a of range(4)) + s += a; +assertEq(s, '0123'); diff --git a/js/src/jit-test/tests/for-of/generators-2.js b/js/src/jit-test/tests/for-of/generators-2.js new file mode 100644 index 000000000000..c804fa8dae02 --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-2.js @@ -0,0 +1,15 @@ +// Generator-iterators are consumed the first time they are iterated. + +function range(n) { + for (var i = 0; i < n; i++) + yield i; +} + +var r = range(10); +var i = 0; +for (var x in r) + assertEq(x, i++); +assertEq(i, 10); +for (var y in r) + throw "FAIL"; +assertEq(y, undefined); diff --git a/js/src/jit-test/tests/for-of/generators-3.js b/js/src/jit-test/tests/for-of/generators-3.js new file mode 100644 index 000000000000..1d4cdccbef2e --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-3.js @@ -0,0 +1,18 @@ +// Nested for-of loops can use the same generator-iterator. + +function range(n) { + for (var i = 0; i < n; i++) + yield i; +} + +var r = range(10); +for (var a of r) + for (var b of r) + for (var c of r) + for (var d of r) + ; + +assertEq(a, 0); +assertEq(b, 1); +assertEq(c, 2); +assertEq(d, 9); diff --git a/js/src/jit-test/tests/for-of/generators-4.js b/js/src/jit-test/tests/for-of/generators-4.js new file mode 100644 index 000000000000..66275448ff30 --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-4.js @@ -0,0 +1,15 @@ +// for-of can iterate over generator-iterators produced by generator-expressions. + +function g() { + yield 1; + yield 2; +} + +var it = g(); +for (var i = 0; i < 10; i++) { + let prev = it; + it = (k + 1 for (k of prev)); +} + +var arr = [v for (v of it)]; +assertEq(arr.join(), "11,12"); diff --git a/js/src/jit-test/tests/for-of/generators-5.js b/js/src/jit-test/tests/for-of/generators-5.js new file mode 100644 index 000000000000..b74d924fd69d --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-5.js @@ -0,0 +1,18 @@ +// Breaking out of a for-of loop over a generator-iterator closes the iterator. + +function range(n) { + for (var i = 0; i < n; i++) + yield i; +} + +var r = range(10); +var s = ''; +for (var x of r) { + s += x; + if (x == 4) + break; +} +s += '/'; +for (var y of r) + s += y; +assertEq(s, '01234/'); diff --git a/js/src/jit-test/tests/for-of/generators-6.js b/js/src/jit-test/tests/for-of/generators-6.js new file mode 100644 index 000000000000..08badf18f942 --- /dev/null +++ b/js/src/jit-test/tests/for-of/generators-6.js @@ -0,0 +1,43 @@ +// Named break closes the right generator-iterators. + +function g() { + for (;;) + yield 1; +} + +function isClosed(it) { + try { + it.next(); + return false; + } catch (exc) { + if (exc !== StopIteration) + throw exc; + return true; + } +} + +var a = g(), b = g(), c = g(), d = g(), e = g(), f = g(); + +for (var aa of a) { + b_: for (var bb of b) { + c_: for (var cc of c) { + d_: for (var dd of d) { + e_: for (var ee of e) { + for (var ff of f) + break c_; + } + } + } + assertEq(isClosed(a), false); + assertEq(isClosed(b), false); + assertEq(isClosed(c), true); + assertEq(isClosed(d), true); + assertEq(isClosed(e), true); + assertEq(isClosed(f), true); + break b_; + } + assertEq(isClosed(a), false); + assertEq(isClosed(b), true); + break; +} +assertEq(isClosed(a), true); diff --git a/js/src/jit-test/tests/for-of/non-iterable.js b/js/src/jit-test/tests/for-of/non-iterable.js new file mode 100644 index 000000000000..36b3b123ac8a --- /dev/null +++ b/js/src/jit-test/tests/for-of/non-iterable.js @@ -0,0 +1,21 @@ +// Iterating over non-iterable values throws a TypeError. + +load(libdir + "asserts.js"); + +var misc = [ + {}, {x: 1}, Math, isNaN, + Object.create(null), + Object.create(Array.prototype), + null, undefined, + true, 0, 3.1416, "", "ponies", + new Boolean(true), new Number(0), new String("ponies")]; + +for (var i = 0; i < misc.length; i++) { + let v = misc[i]; + var testfn = function () { + for (var _ of v) + throw 'FAIL'; + throw 'BAD'; + }; + assertThrowsInstanceOf(testfn, TypeError); +} diff --git a/js/src/jit-test/tests/for-of/proxy-1.js b/js/src/jit-test/tests/for-of/proxy-1.js new file mode 100644 index 000000000000..bbe5cd117db9 --- /dev/null +++ b/js/src/jit-test/tests/for-of/proxy-1.js @@ -0,0 +1,28 @@ +// Basic for-of test with Proxy. + +function iter(arr) { + var i = 0; + return { + next: function () { + if (i < arr.length) + return arr[i++]; + throw StopIteration; + } + }; +} + +function iterableProxy(arr) { + return Proxy.create({iterate: function () { return iter(arr); }}); +} + +var s = ''; +var arr = ['a', 'b', 'c', 'd']; +var p = iterableProxy(arr); + +// Test the same proxy twice. Its iterate method should be called each time. +for (var i = 0; i < 2; i++) { + var j = 0; + for (var x of p) + assertEq(x, arr[j++]); + assertEq(j, arr.length); +} diff --git a/js/src/jit-test/tests/for-of/proxy-2.js b/js/src/jit-test/tests/for-of/proxy-2.js new file mode 100644 index 000000000000..0d487598e8c8 --- /dev/null +++ b/js/src/jit-test/tests/for-of/proxy-2.js @@ -0,0 +1,12 @@ +// Basic for-of test with Proxy whose iterate method is a generator. + +var arr = ['a', 'b', 'c', 'd']; +var proxy = Proxy.create({ + iterate: function () { + for (var i = 0; i < arr.length; i++) + yield arr[i]; + } +}); + +for (var i = 0; i < 2; i++) + assertEq([v for (v of proxy)].join(","), "a,b,c,d"); diff --git a/js/src/jit-test/tests/for-of/proxy-3.js b/js/src/jit-test/tests/for-of/proxy-3.js new file mode 100644 index 000000000000..aefe678348c3 --- /dev/null +++ b/js/src/jit-test/tests/for-of/proxy-3.js @@ -0,0 +1,6 @@ +// An exception thrown from an iterate trap is propagated. + +load(libdir + "asserts.js"); + +var p = Proxy.create({iterate: function () { throw "fit"; }}); +assertThrowsValue(function () { for (var v of p) {} }, "fit"); diff --git a/js/src/jit-test/tests/for-of/proxy-4.js b/js/src/jit-test/tests/for-of/proxy-4.js new file mode 100644 index 000000000000..2a8cbeb330b2 --- /dev/null +++ b/js/src/jit-test/tests/for-of/proxy-4.js @@ -0,0 +1,10 @@ +// for-of on a fixed (non-trapping) proxy does not call the iterate trap. + +load(libdir + "asserts.js"); + +var p = Proxy.create({ + iterate: function () { throw "FAIL"; }, + fix: function () { return {}; } +}); +Object.preventExtensions(p); +assertThrowsInstanceOf(function () { for (var v of p) {} }, TypeError); diff --git a/js/src/jit-test/tests/for-of/syntax-1.js b/js/src/jit-test/tests/for-of/syntax-1.js new file mode 100644 index 000000000000..0c8b63aafd56 --- /dev/null +++ b/js/src/jit-test/tests/for-of/syntax-1.js @@ -0,0 +1,22 @@ +// We correctly reject bogus for-of loop syntax. + +load(libdir + "asserts.js"); + +function assertSyntaxError(code) { + assertThrowsInstanceOf(function () { Function(code); }, SyntaxError, "Function:" + code); + assertThrowsInstanceOf(function () { eval(code); }, SyntaxError, "eval:" + code); + var ieval = eval; + assertThrowsInstanceOf(function () { ieval(code); }, SyntaxError, "indirect eval:" + code); +} + +function test(badForHead) { + assertSyntaxError(badForHead + " {}"); // apply directly to forHead + assertSyntaxError("[0 " + badForHead + "];"); +} + +var a, b, c; +test("for (a in b of c)"); +test("for each (a of b)"); +test("for (a of b of c)"); +test("for (let (a = 1) a of b)"); +test("for (let {a: 1} of b)"); diff --git a/js/src/jit-test/tests/for-of/syntax-2.js b/js/src/jit-test/tests/for-of/syntax-2.js new file mode 100644 index 000000000000..2f320408b092 --- /dev/null +++ b/js/src/jit-test/tests/for-of/syntax-2.js @@ -0,0 +1,9 @@ +// "of" is not a keyword. + +var of; + +Function("var of;"); + +let (of = 12) {} + +function of(of) {} diff --git a/js/src/js.msg b/js/src/js.msg index b999da4c4ef0..cab9b55819be 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -294,7 +294,7 @@ MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value") MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (") -MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for") +MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for") MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 212, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value") MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace") MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}") @@ -374,3 +374,4 @@ MSG_DEF(JSMSG_CANT_WATCH_PROP, 287, 0, JSEXN_TYPEERR, "properties whose n MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 288, 0, JSEXN_ERR, "call to eval() blocked by CSP") MSG_DEF(JSMSG_DEBUG_NO_SCOPE_OBJECT, 289, 0, JSEXN_TYPEERR, "declarative Environments don't have binding objects") MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 290, 0, JSEXN_SYNTAXERR, "mistyped ; after conditional?") +MSG_DEF(JSMSG_NOT_ITERABLE, 291, 1, JSEXN_TYPEERR, "{0} is not iterable") diff --git a/js/src/jsapi.h b/js/src/jsapi.h index e950d111b369..0d5c2f460f55 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3438,7 +3438,12 @@ struct JSClass { #define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \ JSCLASS_RESERVED_SLOTS_WIDTH) -#define JSCLASS_INTERNAL_FLAG1 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) +/* + * Call the iteratorObject hook only to iterate over contents (for-of), not to + * enumerate properties (for-in, for-each, Object.keys, etc.) + */ +#define JSCLASS_FOR_OF_ITERATION (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) + #define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) #define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) #define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3)) diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index ada194371f28..65521917ea5c 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -154,6 +154,7 @@ const char *const js_common_atom_names[] = { js_noSuchMethod_str, /* noSuchMethodAtom */ "[object Null]", /* objectNullAtom */ "[object Undefined]", /* objectUndefinedAtom */ + "of", /* ofAtom */ js_proto_str, /* protoAtom */ js_set_str, /* setAtom */ js_source_str, /* sourceAtom */ diff --git a/js/src/jsatom.h b/js/src/jsatom.h index 4c0adf5ff5e2..8db20290a8f4 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -314,6 +314,7 @@ struct JSAtomState js::PropertyName *noSuchMethodAtom; js::PropertyName *objectNullAtom; js::PropertyName *objectUndefinedAtom; + js::PropertyName *ofAtom; js::PropertyName *protoAtom; js::PropertyName *setAtom; js::PropertyName *sourceAtom; diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 4ba9bbf617b3..20554931641b 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -517,6 +517,7 @@ IsObjectInContextCompartment(const JSObject *obj, const JSContext *cx); #define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ #define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */ #define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */ +#define JSITER_FOR_OF 0x20 /* harmony for-of loop */ inline uintptr_t GetContextStackLimit(const JSContext *cx) diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index bf8f0b0dbc66..897da05efc09 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -423,6 +423,16 @@ GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) { JS_CHECK_RECURSION(cx, return false); + /* + * for-of iteration does not fall back on __iterator__ or property + * enumeration. This is more conservative than the current proposed spec. + */ + if (flags == JSITER_FOR_OF) { + js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_ITERABLE, + JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL, NULL, NULL); + return false; + } + /* Check whether we have a valid __iterator__ method. */ JSAtom *atom = cx->runtime->atomState.iteratorAtom; if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp)) @@ -658,12 +668,22 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) if (obj) { /* Enumerate Iterator.prototype directly. */ if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) { - JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH)); - if (!iterobj) - return false; - vp->setObject(*iterobj); - types::MarkIteratorUnknown(cx); - return true; + /* + * Arrays and other classes representing iterable collections have + * the JSCLASS_FOR_OF_ITERATION flag. This flag means that the + * object responds to all other kinds of enumeration (for-in, + * for-each, Object.keys, Object.getOwnPropertyNames, etc.) in the + * default way, ignoring the hook. The hook is used only when + * iterating in the style of a for-of loop. + */ + if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) { + JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF))); + if (!iterobj) + return false; + vp->setObject(*iterobj); + types::MarkIteratorUnknown(cx); + return true; + } } if (keysOnly) { @@ -1260,7 +1280,7 @@ Class js::GeneratorClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - iterator_iterator, + iterator_iterator, NULL /* unused */ } }; diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 9cbb9bccda48..ba32fb133e8c 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -2712,6 +2712,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) saveop = JSOP_NOP; sn = NULL; rval = NULL; + bool forOf = false; #if JS_HAS_XML_SUPPORT foreach = inXML = quoteAttr = JS_FALSE; #endif @@ -3854,6 +3855,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_ITER: + forOf = (GET_UINT8(pc) == JSITER_FOR_OF); foreach = (GET_UINT8(pc) & (JSITER_FOREACH | JSITER_KEYVALUE)) == JSITER_FOREACH; todo = -2; @@ -3925,9 +3927,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = POP_STR(); if (ss->top >= 1 && ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) { ss->sprinter.setOffset(ss->offsets[ss->top] - PAREN_SLOP); - if (Sprint(&ss->sprinter, " %s (%s in %s)", + if (Sprint(&ss->sprinter, " %s (%s %s %s)", foreach ? js_for_each_str : js_for_str, - lval, rval) < 0) { + lval, + forOf ? "of" : "in", + rval) < 0) { return NULL; } @@ -3939,9 +3943,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) */ todo = ss->offsets[ss->top - 1]; } else { - todo = Sprint(&ss->sprinter, " %s (%s in %s)", + todo = Sprint(&ss->sprinter, " %s (%s %s %s)", foreach ? js_for_each_str : js_for_str, - lval, rval); + lval, + forOf ? "of" : "in", + rval); } if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL)) return NULL; @@ -3953,9 +3959,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) */ rval = GetStr(ss, ss->top - 1); xval = VarPrefix(js_GetSrcNote(jp->script, pc + next)); - js_printf(jp, "\t%s (%s%s in %s) {\n", + js_printf(jp, "\t%s (%s%s %s %s) {\n", foreach ? js_for_each_str : js_for_str, - xval, lval, rval); + xval, + lval, + forOf ? "of" : "in", + rval); jp->indent += 4; DECOMPILE_CODE(pc + next + JSOP_POP_LENGTH, cond - next - JSOP_POP_LENGTH); jp->indent -= 4; From 86a2aac95669783f14d3ab06463414a26ec981e4 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 7 Feb 2012 12:57:16 -0600 Subject: [PATCH 08/28] Bug 699565 - Part 2 - for-of loops on arrays. r=bhackett. --- .../tests/for-of/array-comprehension.js | 3 + js/src/jit-test/tests/for-of/array-holes-1.js | 10 ++ js/src/jit-test/tests/for-of/array-holes-2.js | 8 + js/src/jit-test/tests/for-of/array-holes-3.js | 8 + js/src/jit-test/tests/for-of/array-holes-4.js | 10 ++ .../jit-test/tests/for-of/array-holes-slow.js | 13 ++ .../jit-test/tests/for-of/array-prototype.js | 11 ++ js/src/jit-test/tests/for-of/arrays-1.js | 7 + js/src/jit-test/tests/for-of/arrays-2.js | 10 ++ js/src/jit-test/tests/for-of/arrays-3.js | 9 + js/src/jit-test/tests/for-of/arrays-4.js | 8 + js/src/jit-test/tests/for-of/arrays-5.js | 4 + .../jit-test/tests/for-of/arrays-growing-1.js | 10 ++ .../jit-test/tests/for-of/arrays-growing-2.js | 10 ++ .../tests/for-of/arrays-shrinking-1.js | 13 ++ .../tests/for-of/arrays-shrinking-2.js | 9 + js/src/jit-test/tests/for-of/arrays-slow-1.js | 8 + js/src/jit-test/tests/for-of/arrays-slow-2.js | 10 ++ js/src/jit-test/tests/for-of/arrays-slow-3.js | 9 + js/src/jit-test/tests/for-of/arrays-slow-4.js | 6 + js/src/jit-test/tests/for-of/arrays-slow-5.js | 10 ++ js/src/jit-test/tests/for-of/break-1.js | 9 + js/src/jit-test/tests/for-of/break-2.js | 10 ++ js/src/jit-test/tests/for-of/break-3.js | 12 ++ js/src/jit-test/tests/for-of/return.js | 14 ++ js/src/jit-test/tests/for-of/throw.js | 20 +++ js/src/jsarray.cpp | 38 +++- js/src/jsiter.cpp | 170 ++++++++++++++++-- js/src/jsiter.h | 61 +++++++ js/src/jsobj.h | 7 +- js/src/jsobjinlines.h | 1 + 31 files changed, 510 insertions(+), 18 deletions(-) create mode 100644 js/src/jit-test/tests/for-of/array-comprehension.js create mode 100644 js/src/jit-test/tests/for-of/array-holes-1.js create mode 100644 js/src/jit-test/tests/for-of/array-holes-2.js create mode 100644 js/src/jit-test/tests/for-of/array-holes-3.js create mode 100644 js/src/jit-test/tests/for-of/array-holes-4.js create mode 100644 js/src/jit-test/tests/for-of/array-holes-slow.js create mode 100644 js/src/jit-test/tests/for-of/array-prototype.js create mode 100644 js/src/jit-test/tests/for-of/arrays-1.js create mode 100644 js/src/jit-test/tests/for-of/arrays-2.js create mode 100644 js/src/jit-test/tests/for-of/arrays-3.js create mode 100644 js/src/jit-test/tests/for-of/arrays-4.js create mode 100644 js/src/jit-test/tests/for-of/arrays-5.js create mode 100644 js/src/jit-test/tests/for-of/arrays-growing-1.js create mode 100644 js/src/jit-test/tests/for-of/arrays-growing-2.js create mode 100644 js/src/jit-test/tests/for-of/arrays-shrinking-1.js create mode 100644 js/src/jit-test/tests/for-of/arrays-shrinking-2.js create mode 100644 js/src/jit-test/tests/for-of/arrays-slow-1.js create mode 100644 js/src/jit-test/tests/for-of/arrays-slow-2.js create mode 100644 js/src/jit-test/tests/for-of/arrays-slow-3.js create mode 100644 js/src/jit-test/tests/for-of/arrays-slow-4.js create mode 100644 js/src/jit-test/tests/for-of/arrays-slow-5.js create mode 100644 js/src/jit-test/tests/for-of/break-1.js create mode 100644 js/src/jit-test/tests/for-of/break-2.js create mode 100644 js/src/jit-test/tests/for-of/break-3.js create mode 100644 js/src/jit-test/tests/for-of/return.js create mode 100644 js/src/jit-test/tests/for-of/throw.js diff --git a/js/src/jit-test/tests/for-of/array-comprehension.js b/js/src/jit-test/tests/for-of/array-comprehension.js new file mode 100644 index 000000000000..1641d0cd0a4a --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-comprehension.js @@ -0,0 +1,3 @@ +// for-of can be used in array comprehensions. + +assertEq([x*x for (x of [1, 2, 3])].join(), "1,4,9"); diff --git a/js/src/jit-test/tests/for-of/array-holes-1.js b/js/src/jit-test/tests/for-of/array-holes-1.js new file mode 100644 index 000000000000..3d8612219263 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-1.js @@ -0,0 +1,10 @@ +// for-of does not skip Array holes. The value at a hole is undefined. + +var a = [0, , 2, 3]; +var log = []; +for (var x of a) { + assertEq(x, a[log.length]); + log.push(x); +} +assertEq(log[1], undefined); +assertEq(log.join(), "0,,2,3"); diff --git a/js/src/jit-test/tests/for-of/array-holes-2.js b/js/src/jit-test/tests/for-of/array-holes-2.js new file mode 100644 index 000000000000..7090e636eaea --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-2.js @@ -0,0 +1,8 @@ +// for-of does not consult Object.prototype when it encounters a hole. + +Object.prototype[1] = 'FAIL'; +var log = []; +for (var x of [0, , 2, 3]) + log.push(x); +assertEq(log[1], undefined); +assertEq(log.join(), "0,,2,3"); diff --git a/js/src/jit-test/tests/for-of/array-holes-3.js b/js/src/jit-test/tests/for-of/array-holes-3.js new file mode 100644 index 000000000000..7eecfe3816d2 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-3.js @@ -0,0 +1,8 @@ +// for-of does not consult Array.prototype when it encounters a hole. + +Array.prototype[1] = 'FAIL'; +var log = []; +for (var x of [0, , 2, 3]) + log.push(x); +assertEq(log[1], undefined); +assertEq(log.join(), "0,,2,3"); diff --git a/js/src/jit-test/tests/for-of/array-holes-4.js b/js/src/jit-test/tests/for-of/array-holes-4.js new file mode 100644 index 000000000000..871ed685a1f3 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-4.js @@ -0,0 +1,10 @@ +// for-of on an Array does not consult the prototype chain when it encounters a hole. + +var m = {1: 'FAIL'}; +var a = [0, , 2, 3]; +a.__proto__ = m; +var log = []; +for (var x of a) + log.push(x); +assertEq(log[1], undefined); +assertEq(log.join(), "0,,2,3"); diff --git a/js/src/jit-test/tests/for-of/array-holes-slow.js b/js/src/jit-test/tests/for-of/array-holes-slow.js new file mode 100644 index 000000000000..c4961398c707 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-holes-slow.js @@ -0,0 +1,13 @@ +// for-of on a slow Array does not consult the prototype chain when it encounters a hole. + +var a = [0, , , 3]; +a.slow = true; +Object.prototype[1] = 'FAIL1'; +Array.prototype[2] = 'FAIL2'; + +var log = []; +for (var x of a) + log.push(x); +assertEq(log[1], undefined); +assertEq(log[2], undefined); +assertEq(log.join(), "0,,,3"); diff --git a/js/src/jit-test/tests/for-of/array-prototype.js b/js/src/jit-test/tests/for-of/array-prototype.js new file mode 100644 index 000000000000..074420e871fb --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-prototype.js @@ -0,0 +1,11 @@ +// for-of works on Array.prototype. + +var v; +for (v of Array.prototype) + throw "FAIL"; + +var s = ''; +Array.prototype.push('a', 'b'); +for (v of Array.prototype) + s += v; +assertEq(s, 'ab'); diff --git a/js/src/jit-test/tests/for-of/arrays-1.js b/js/src/jit-test/tests/for-of/arrays-1.js new file mode 100644 index 000000000000..b75a78d1911a --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-1.js @@ -0,0 +1,7 @@ +// for-of works on arrays. + +var a = ['a', 'b', 'c']; +var s = ''; +for (var v of a) + s += v; +assertEq(s, 'abc'); diff --git a/js/src/jit-test/tests/for-of/arrays-2.js b/js/src/jit-test/tests/for-of/arrays-2.js new file mode 100644 index 000000000000..df20f92232d9 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-2.js @@ -0,0 +1,10 @@ +// A for-of loop over an array does not take a snapshot of the array elements. +// Instead, each time the loop needs an element from the array, it gets its current value. + +var a = [3, 5, 5, 4, 0, 5]; +var s = ''; +for (var i of a) { + s += i; + a[i] = 'X'; +} +assertEq(s, '355X0X'); diff --git a/js/src/jit-test/tests/for-of/arrays-3.js b/js/src/jit-test/tests/for-of/arrays-3.js new file mode 100644 index 000000000000..b0dedbcc1070 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-3.js @@ -0,0 +1,9 @@ +// Two for-of loops on the same array get distinct iterators. + +var a = [1, 2, 3]; +var s = ''; +for (var x of a) + s += x; +for (var y of a) + s += y; +assertEq(s, '123123'); diff --git a/js/src/jit-test/tests/for-of/arrays-4.js b/js/src/jit-test/tests/for-of/arrays-4.js new file mode 100644 index 000000000000..8567f2b60e7d --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-4.js @@ -0,0 +1,8 @@ +// Nested for-of loops on the same array get distinct iterators. + +var a = [1, 2, 3]; +var s = ''; +for (var x of a) + for (var y of a) + s += '' + x + y + ','; +assertEq(s, '11,12,13,21,22,23,31,32,33,'); diff --git a/js/src/jit-test/tests/for-of/arrays-5.js b/js/src/jit-test/tests/for-of/arrays-5.js new file mode 100644 index 000000000000..266eb1d948d5 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-5.js @@ -0,0 +1,4 @@ +// for-of on an empty array does nothing. + +for (var x of []) + fail(); diff --git a/js/src/jit-test/tests/for-of/arrays-growing-1.js b/js/src/jit-test/tests/for-of/arrays-growing-1.js new file mode 100644 index 000000000000..4b5c19e1ea4f --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-growing-1.js @@ -0,0 +1,10 @@ +// A for-of loop over an array continues to the end if the array grows during iteration. + +var a = [0, 1, 1, 0, 1, 0, 0]; +var s = ''; +for (var v of a) { + s += v; + if (v === 1) + a.push(2); +} +assertEq(s, '0110100222'); diff --git a/js/src/jit-test/tests/for-of/arrays-growing-2.js b/js/src/jit-test/tests/for-of/arrays-growing-2.js new file mode 100644 index 000000000000..e9867714ce9d --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-growing-2.js @@ -0,0 +1,10 @@ +// Inserting values in an array does not change the next index of an existing iterator. + +var a = [1, 2, 3, 4]; +var s = ''; +for (var v of a) { + s += v; + if (s.length === 2) + a.unshift('x'); +} +assertEq(s, '12234'); diff --git a/js/src/jit-test/tests/for-of/arrays-shrinking-1.js b/js/src/jit-test/tests/for-of/arrays-shrinking-1.js new file mode 100644 index 000000000000..b7210c29c077 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-shrinking-1.js @@ -0,0 +1,13 @@ +// A for-of loop over an array stops at the new end of the array if it shrinks during iteration. + +function ispal(arr) { + for (var v of arr) { + if (v !== arr.pop()) + return false; + } + return true; +} + +assertEq(ispal([1, 2, 3, 4, 3, 2, 1]), true); +assertEq(ispal([1, 2, 3, 3, 2, 1]), true); +assertEq(ispal([1, 2, 3, 4, 2, 1]), false); diff --git a/js/src/jit-test/tests/for-of/arrays-shrinking-2.js b/js/src/jit-test/tests/for-of/arrays-shrinking-2.js new file mode 100644 index 000000000000..86c77303d017 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-shrinking-2.js @@ -0,0 +1,9 @@ +// Using shift to cut values out of an array does not change the next index of an existing iterator. + +var a = [1, 2, 3, 4, 5, 6, 7, 8]; +var s = ''; +for (var v of a) { + s += v; + a.shift(); +} +assertEq(s, '1357'); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-1.js b/js/src/jit-test/tests/for-of/arrays-slow-1.js new file mode 100644 index 000000000000..cab5a49a096c --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-1.js @@ -0,0 +1,8 @@ +// for-of works on slow arrays. + +var a = ['a', 'b', 'c']; +a.slow = true; +var log = ''; +for (var x of a) + log += x; +assertEq(log, 'abc'); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-2.js b/js/src/jit-test/tests/for-of/arrays-slow-2.js new file mode 100644 index 000000000000..dc4cf4fa796b --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-2.js @@ -0,0 +1,10 @@ +// Two for-of loops on the same slow array get distinct iterators. + +var a = [1, 2, 3]; +a.slow = true; +var s = ''; +for (var x of a) + s += x; +for (var y of a) + s += y; +assertEq(s, '123123'); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-3.js b/js/src/jit-test/tests/for-of/arrays-slow-3.js new file mode 100644 index 000000000000..8a00fd51b074 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-3.js @@ -0,0 +1,9 @@ +// Nested for-of loops on the same slow array get distinct iterators. + +var a = [1, 2, 3]; +a.slow = true; +var s = ''; +for (var x of a) + for (var y of a) + s += '' + x + y + ','; +assertEq(s, '11,12,13,21,22,23,31,32,33,'); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-4.js b/js/src/jit-test/tests/for-of/arrays-slow-4.js new file mode 100644 index 000000000000..d2bceef8762d --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-4.js @@ -0,0 +1,6 @@ +// for-of on an empty slow array does nothing. + +var a = []; +a.slow = true; +for (var x of a) + fail(); diff --git a/js/src/jit-test/tests/for-of/arrays-slow-5.js b/js/src/jit-test/tests/for-of/arrays-slow-5.js new file mode 100644 index 000000000000..36173e352bf2 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arrays-slow-5.js @@ -0,0 +1,10 @@ +// Slowifying an array while it is being iterated does not affect iteration. + +var a = [9, 8, 7, 6, 5, 4, 3]; +var log = ''; +for (var x of a) { + log += x; + if (x === 6) + a.slow = true; +} +assertEq(log, "9876543"); diff --git a/js/src/jit-test/tests/for-of/break-1.js b/js/src/jit-test/tests/for-of/break-1.js new file mode 100644 index 000000000000..1a1e528eb893 --- /dev/null +++ b/js/src/jit-test/tests/for-of/break-1.js @@ -0,0 +1,9 @@ +// A break statement leaves a for-of loop. + +var log = ''; +for (var x of ['a', 'b', 'c']) { + log += x; + if (x === 'b') + break; +} +assertEq(log, "ab"); diff --git a/js/src/jit-test/tests/for-of/break-2.js b/js/src/jit-test/tests/for-of/break-2.js new file mode 100644 index 000000000000..4eea55bface2 --- /dev/null +++ b/js/src/jit-test/tests/for-of/break-2.js @@ -0,0 +1,10 @@ +// A break statement leaves only a single for-of loop. + +var log = ''; +for (var x of [1, 2, 3]) { + for (var y of ['.', ':']) { + log += x + y; + break; + } +} +assertEq(log, "1.2.3."); diff --git a/js/src/jit-test/tests/for-of/break-3.js b/js/src/jit-test/tests/for-of/break-3.js new file mode 100644 index 000000000000..f5f873197ff1 --- /dev/null +++ b/js/src/jit-test/tests/for-of/break-3.js @@ -0,0 +1,12 @@ +// A labeled break statement can leave multiple for-loops + +var log = ''; +for (var i = 0; i < 3; i++) { + a: for (var x of [1, 2, 3]) { + for (var y of ['.', ':']) { + log += x + y; + break a; + } + } +} +assertEq(log, "1.1.1."); diff --git a/js/src/jit-test/tests/for-of/return.js b/js/src/jit-test/tests/for-of/return.js new file mode 100644 index 000000000000..3e39df49cfe5 --- /dev/null +++ b/js/src/jit-test/tests/for-of/return.js @@ -0,0 +1,14 @@ +// Control can exit a for-of loop via return. + +function f() { + for (var a of [1, 2, 3]) { + for (var b of [1, 2, 3]) { + for (var c of [1, 2, 3]) { + if (a !== b && b !== c && c !== a) + return "" + a + b + c; + } + } + } +} + +assertEq(f(), "123"); diff --git a/js/src/jit-test/tests/for-of/throw.js b/js/src/jit-test/tests/for-of/throw.js new file mode 100644 index 000000000000..2576ce135637 --- /dev/null +++ b/js/src/jit-test/tests/for-of/throw.js @@ -0,0 +1,20 @@ +// Control can exit a for-of loop via throw. + +function f() { + for (var a of [1, 2, 3]) { + for (var b of [1, 2, 3]) { + for (var c of [1, 2, 3]) { + if (a !== b && b !== c && c !== a) + throw [a, b, c]; + } + } + } +} + +var x = null; +try { + f(); +} catch (exc) { + x = exc.join(""); +} +assertEq(x, "123"); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index b32edd5e031f..75559a6516ec 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -679,6 +679,13 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value return true; } +static JSObject * +array_iterator(JSContext *cx, JSObject *obj, JSBool keysonly) +{ + JS_ASSERT(!keysonly); + return ElementIteratorObject::create(cx, obj); +} + /* Returns true if the dense array has an own property at the index. */ static inline bool IsDenseArrayIndex(JSObject *obj, uint32_t index) @@ -1215,7 +1222,7 @@ array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props) Class js::ArrayClass = { "Array", - Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), + Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION, JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ @@ -1231,7 +1238,14 @@ Class js::ArrayClass = { NULL, /* xdrObject */ NULL, /* hasInstance */ array_trace, /* trace */ - JS_NULL_CLASS_EXT, + { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + array_iterator, + NULL, /* unused */ + false, /* isWrappedNative */ + }, { array_lookupGeneric, array_lookupProperty, @@ -1272,14 +1286,30 @@ Class js::ArrayClass = { Class js::SlowArrayClass = { "Array", - JSCLASS_HAS_CACHED_PROTO(JSProto_Array), + JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION, slowarray_addProperty, JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ JS_StrictPropertyStub, /* setProperty */ JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub + JS_ConvertStub, + NULL, + NULL, /* reserved0 */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + NULL, /* trace */ + { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + array_iterator, + NULL, /* unused */ + false, /* isWrappedNative */ + } }; bool diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 897da05efc09..d3c3a160b47a 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -110,7 +110,34 @@ Class js::IteratorClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - iterator_iterator, + iterator_iterator, + NULL /* unused */ + } +}; + +Class js::ElementIteratorClass = { + "ElementIterator", + JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots), + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + NULL, /* finalize */ + NULL, /* reserved */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* xdrObject */ + NULL, /* hasInstance */ + NULL, /* trace */ + { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + iterator_iterator, NULL /* unused */ } }; @@ -1064,6 +1091,110 @@ js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_ return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end)); } +const uint32_t CLOSED_INDEX = UINT32_MAX; + +JSObject * +ElementIteratorObject::create(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(obj); + JSObject *iterobj = NewObjectWithGivenProto(cx, &ElementIteratorClass, NULL, obj); + if (iterobj) { + iterobj->setReservedSlot(TargetSlot, ObjectValue(*obj)); + iterobj->setReservedSlot(IndexSlot, Int32Value(0)); + } + return iterobj; +} + +inline uint32_t +ElementIteratorObject::getIndex() const +{ + return uint32_t(getReservedSlot(IndexSlot).toInt32()); +} + +inline JSObject * +ElementIteratorObject::getTargetObject() const +{ + return &getReservedSlot(TargetSlot).toObject(); +} + +inline void +ElementIteratorObject::setIndex(uint32_t index) +{ + setReservedSlot(IndexSlot, Int32Value(int32_t(index))); +} + +bool +ElementIteratorObject::iteratorNext(JSContext *cx, Value *vp) +{ + uint32_t i, length; + JSObject *obj = getTargetObject(); + if (!js_GetLengthProperty(cx, obj, &length)) + goto error; + + i = getIndex(); + if (i >= length) { + setIndex(CLOSED_INDEX); + vp->setMagic(JS_NO_ITER_VALUE); + return true; + } + + JS_ASSERT(i + 1 > i); + + /* Simple fast path for dense arrays. */ + if (obj->isDenseArray()) { + *vp = obj->getDenseArrayElement(i); + if (vp->isMagic(JS_ARRAY_HOLE)) + vp->setUndefined(); + } else { + /* Make a jsid for this index. */ + jsid id; + if (i < uint32_t(INT32_MAX) && INT_FITS_IN_JSID(i)) { + id = INT_TO_JSID(i); + } else { + Value v = DoubleValue(i); + if (!js_ValueToStringId(cx, v, &id)) + goto error; + } + + /* Find out if this object has an element i. */ + bool has; + if (obj->isProxy()) { + /* js_HasOwnProperty does not work on proxies. */ + if (!Proxy::hasOwn(cx, obj, id, &has)) + goto error; + } else { + JSObject *obj2; + JSProperty *prop; + if (!js_HasOwnProperty(cx, obj->getOps()->lookupGeneric, obj, id, &obj2, &prop)) + goto error; + has = !!prop; + } + + /* Populate *vp. */ + if (has) { + if (!obj->getElement(cx, obj, i, vp)) + goto error; + } else { + vp->setUndefined(); + } + } + + /* On success, bump the index. */ + setIndex(i + 1); + return true; + + error: + setIndex(CLOSED_INDEX); + return false; +} + +inline js::ElementIteratorObject * +JSObject::asElementIterator() +{ + JS_ASSERT(isElementIterator()); + return static_cast(this); +} + JSBool js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) { @@ -1089,7 +1220,31 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) JS_CHECK_RECURSION(cx, return false); /* Fetch and cache the next value from the iterator. */ - if (!ni) { + if (ni) { + JS_ASSERT(!ni->isKeyIter()); + jsid id; + if (!ValueToId(cx, StringValue(*ni->current()), &id)) + return false; + id = js_CheckForStringIndex(id); + ni->incCursor(); + if (!ni->obj->getGeneric(cx, id, rval)) + return false; + if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval)) + return false; + } else if (iterobj->isElementIterator()) { + /* + * Like native iterators, element iterators do not have a .next + * method, so this fast path is necessary for correctness. + */ + if (!iterobj->asElementIterator()->iteratorNext(cx, rval)) + return false; + if (rval->isMagic(JS_NO_ITER_VALUE)) { + cx->iterValue.setMagic(JS_NO_ITER_VALUE); + rval->setBoolean(false); + return true; + } + } else { + /* Call the iterator object's .next method. */ jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval)) return false; @@ -1103,17 +1258,6 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) rval->setBoolean(false); return true; } - } else { - JS_ASSERT(!ni->isKeyIter()); - jsid id; - if (!ValueToId(cx, StringValue(*ni->current()), &id)) - return false; - id = js_CheckForStringIndex(id); - ni->incCursor(); - if (!ni->obj->getGeneric(cx, id, rval)) - return false; - if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval)) - return false; } /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */ diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 3f744d9bc8d5..d8a8bff04cfc 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -101,6 +101,67 @@ struct NativeIterator { void mark(JSTracer *trc); }; +class ElementIteratorObject : public JSObject { + public: + enum { + TargetSlot, + IndexSlot, + NumSlots + }; + + static JSObject *create(JSContext *cx, JSObject *target); + + inline uint32_t getIndex() const; + inline void setIndex(uint32_t index); + inline JSObject *getTargetObject() const; + + /* + Array iterators are like this: + + Array.prototype[iterate] = function () { + for (var i = 0; i < (this.length >>> 0); i++) { + var desc = Object.getOwnPropertyDescriptor(this, i); + yield desc === undefined ? undefined : this[i]; + } + } + + This has the following implications: + + - Array iterators are generic; Array.prototype[iterate] can be transferred to + any other object to create iterators over it. + + - The next() method of an Array iterator is non-reentrant. Trying to reenter, + e.g. by using it on an object with a length getter that calls it.next() on + the same iterator, causes a TypeError. + + - The iterator fetches obj.length every time its next() method is called. + + - The iterator converts obj.length to a whole number using ToUint32. As a + consequence the iterator can't go on forever; it can yield at most 2^32-1 + values. Then i will be 0xffffffff, and no possible length value will be + greater than that. + + - The iterator does not skip "array holes". When it encounters a hole, it + yields undefined. + + - The iterator never consults the prototype chain. + + - If an element has a getter which throws, the exception is propagated, and + the iterator is closed (that is, all future calls to next() will simply + throw StopIteration). + + Note that if next() were reentrant, even more details of its inner + workings would be observable. + */ + + /* + * If there are any more elements to visit, store the value of the next + * element in *vp, increment the index, and return true. If not, call + * vp->setMagic(JS_NO_ITER_VALUE) and return true. Return false on error. + */ + bool iteratorNext(JSContext *cx, Value *vp); +}; + bool VectorToIdArray(JSContext *cx, js::AutoIdVector &props, JSIdArray **idap); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 00c8040ac195..48ca25873faf 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -354,6 +354,7 @@ extern Class BooleanClass; extern Class CallableObjectClass; extern Class DateClass; extern Class ErrorClass; +extern Class ElementIteratorClass; extern Class GeneratorClass; extern Class IteratorClass; extern Class JSONClass; @@ -377,6 +378,7 @@ class BlockObject; class BooleanObject; class ClonedBlockObject; class DeclEnvObject; +class ElementIteratorObject; class GlobalObject; class NestedScopeObject; class NewObjectCache; @@ -995,7 +997,7 @@ struct JSObject : js::gc::Cell bool isSealed(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, SEAL, resultp); } bool isFrozen(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, FREEZE, resultp); } - + /* * Primitive-specific getters and setters. */ @@ -1046,6 +1048,8 @@ struct JSObject : js::gc::Cell return sizeof(JSObject) + sizeof(js::ObjectElements); } + inline js::ElementIteratorObject *asElementIterator(); + /* * Array-specific getters and setters (for both dense and slow arrays). */ @@ -1406,6 +1410,7 @@ struct JSObject : js::gc::Cell inline bool isArray() const; inline bool isDate() const; inline bool isDenseArray() const; + inline bool isElementIterator() const; inline bool isError() const; inline bool isFunction() const; inline bool isGenerator() const; diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 8e016cc91069..395d06a5e3df 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -926,6 +926,7 @@ inline bool JSObject::isCall() const { return hasClass(&js::CallClass); } inline bool JSObject::isClonedBlock() const { return isBlock() && !!getProto(); } inline bool JSObject::isDate() const { return hasClass(&js::DateClass); } inline bool JSObject::isDeclEnv() const { return hasClass(&js::DeclEnvClass); } +inline bool JSObject::isElementIterator() const { return hasClass(&js::ElementIteratorClass); } inline bool JSObject::isError() const { return hasClass(&js::ErrorClass); } inline bool JSObject::isFunction() const { return hasClass(&js::FunctionClass); } inline bool JSObject::isFunctionProxy() const { return hasClass(&js::FunctionProxyClass); } From 84ac816f69622c048851c0261ede8fe893acad43 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 7 Feb 2012 12:57:16 -0600 Subject: [PATCH 09/28] Bug 699565 - Part 3 - for-of loops on wrappers. r=bhackett. --- js/src/jit-test/tests/for-of/wrapper-1.js | 7 +++++++ js/src/jsiter.cpp | 7 +++++++ js/src/jsproxy.cpp | 15 ++++++++++++++ js/src/jsproxy.h | 2 ++ js/src/jswrapper.cpp | 24 +++++++++++++++++++++++ js/src/jswrapper.h | 2 ++ 6 files changed, 57 insertions(+) create mode 100644 js/src/jit-test/tests/for-of/wrapper-1.js diff --git a/js/src/jit-test/tests/for-of/wrapper-1.js b/js/src/jit-test/tests/for-of/wrapper-1.js new file mode 100644 index 000000000000..4b3d30a2ad68 --- /dev/null +++ b/js/src/jit-test/tests/for-of/wrapper-1.js @@ -0,0 +1,7 @@ +// for-of works on cross-compartment wrappers of Arrays. + +var g = newGlobal('new-compartment'); +var s = ''; +for (var x of g.Array(1, 1, 2, 3, 5)) + s += x; +assertEq(s, '11235'); diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index d3c3a160b47a..f3a57f37d6f7 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1243,6 +1243,13 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) rval->setBoolean(false); return true; } + } else if (iterobj->isProxy()) { + if (!Proxy::iteratorNext(cx, iterobj, rval)) + return false; + if (rval->isMagic(JS_NO_ITER_VALUE)) { + rval->setBoolean(false); + return true; + } } else { /* Call the iterator object's .next method. */ jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index f5024caf63ec..599c125ded60 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -314,6 +314,13 @@ ProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *v return DefaultValue(cx, proxy, hint, vp); } +bool +ProxyHandler::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp) +{ + vp->setMagic(JS_NO_ITER_VALUE); + return true; +} + bool ProxyHandler::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp) { @@ -969,6 +976,14 @@ Proxy::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp) return GetProxyHandler(proxy)->defaultValue(cx, proxy, hint, vp); } +bool +Proxy::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp) +{ + JS_CHECK_RECURSION(cx, return NULL); + AutoPendingProxyOperation pending(cx, proxy); + return GetProxyHandler(proxy)->iteratorNext(cx, proxy, vp); +} + static JSObject * proxy_innerObject(JSContext *cx, JSObject *obj) { diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index fb21c9e1ad9c..baf5a4e86cbc 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -86,6 +86,7 @@ class JS_FRIEND_API(ProxyHandler) { virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent); virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy); virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp); + virtual bool iteratorNext(JSContext *cx, JSObject *proxy, Value *vp); virtual void finalize(JSContext *cx, JSObject *proxy); virtual void trace(JSTracer *trc, JSObject *proxy); virtual bool getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver, @@ -140,6 +141,7 @@ class Proxy { static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent); static RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy); static bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp); + static bool iteratorNext(JSContext *cx, JSObject *proxy, Value *vp); }; inline bool IsObjectProxy(const JSObject *obj) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 254a2ab214c7..1e0f5af650f9 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -349,6 +349,21 @@ Wrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) return ToPrimitive(cx, hint, vp); } +bool +Wrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) +{ + if (!js_IteratorMore(cx, wrappedObject(wrapper), vp)) + return false; + + if (vp->toBoolean()) { + *vp = cx->iterValue; + cx->iterValue.setUndefined(); + } else { + vp->setMagic(JS_NO_ITER_VALUE); + } + return true; +} + void Wrapper::trace(JSTracer *trc, JSObject *wrapper) { @@ -848,6 +863,15 @@ CrossCompartmentWrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType h return call.origin->wrap(cx, vp); } +bool +CrossCompartmentWrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) +{ + PIERCE(cx, wrapper, GET, + NOTHING, + Wrapper::iteratorNext(cx, wrapper, vp), + call.origin->wrap(cx, vp)); +} + void CrossCompartmentWrapper::trace(JSTracer *trc, JSObject *wrapper) { diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 8edf4e4f3830..6549b3369a3e 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -96,6 +96,7 @@ class JS_FRIEND_API(Wrapper) : public ProxyHandler virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) MOZ_OVERRIDE; virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE; + virtual bool iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) MOZ_OVERRIDE; virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE; @@ -156,6 +157,7 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE; + virtual bool iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp); virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE; From 2ee2e8f0d9df908893d3e993945e7126ad7a3b9e Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 7 Feb 2012 12:57:17 -0600 Subject: [PATCH 10/28] Bug 699565 - Part 4 - for-of loops on array-like DOM object. r=bz. --- browser/devtools/webconsole/test/Makefile.in | 2 ++ .../test/browser_webconsole_for_of.js | 33 +++++++++++++++++++ .../devtools/webconsole/test/test-for-of.html | 7 ++++ dom/tests/mochitest/general/Makefile.in | 1 + dom/tests/mochitest/general/test_for_of.html | 25 ++++++++++++++ js/src/jsapi.cpp | 9 +++++ js/src/jsapi.h | 7 ++++ js/xpconnect/src/dombindings.cpp | 7 ++++ 8 files changed, 91 insertions(+) create mode 100644 browser/devtools/webconsole/test/browser_webconsole_for_of.js create mode 100644 browser/devtools/webconsole/test/test-for-of.html create mode 100644 dom/tests/mochitest/general/test_for_of.html diff --git a/browser/devtools/webconsole/test/Makefile.in b/browser/devtools/webconsole/test/Makefile.in index a850e13917a9..39dfc61d0875 100644 --- a/browser/devtools/webconsole/test/Makefile.in +++ b/browser/devtools/webconsole/test/Makefile.in @@ -64,6 +64,7 @@ _BROWSER_TEST_FILES = \ browser_webconsole_consoleonpage.js \ browser_webconsole_chrome.js \ browser_webconsole_execution_scope.js \ + browser_webconsole_for_of.js \ browser_webconsole_history.js \ browser_webconsole_hud_getters.js \ browser_webconsole_js_input_and_output_styling.js \ @@ -226,6 +227,7 @@ _BROWSER_TEST_PAGES = \ browser_gcli_inspect.html \ test-bug-658368-time-methods.html \ test-webconsole-error-observer.html \ + test-for-of.html \ $(NULL) libs:: $(_BROWSER_TEST_FILES) diff --git a/browser/devtools/webconsole/test/browser_webconsole_for_of.js b/browser/devtools/webconsole/test/browser_webconsole_for_of.js new file mode 100644 index 000000000000..5c8648c15b46 --- /dev/null +++ b/browser/devtools/webconsole/test/browser_webconsole_for_of.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// A for-of loop in Web Console code can loop over a content NodeList. + +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-for-of.html"; + +registerCleanupFunction(function() { + Services.prefs.clearUserPref("devtools.gcli.enable"); +}); + +function test() { + Services.prefs.setBoolPref("devtools.gcli.enable", false); + addTab(TEST_URI); + browser.addEventListener("DOMContentLoaded", testForOf, false); +} + +function testForOf() { + browser.removeEventListener("DOMContentLoaded", testForOf, false); + + openConsole(); + var hud = HUDService.getHudByWindow(content); + var jsterm = hud.jsterm; + jsterm.execute("{ [x.tagName for (x of document.body.childNodes) if (x.nodeType === 1)].join(' '); }"); + + let node = hud.outputNode.querySelector(".webconsole-msg-output"); + ok(/H1 DIV H2 P/.test(node.textContent), + "for-of loop should find all top-level nodes"); + + jsterm.clearOutput(); + jsterm.history.splice(0, jsterm.history.length); // workaround for bug 592552 + finishTest(); +} diff --git a/browser/devtools/webconsole/test/test-for-of.html b/browser/devtools/webconsole/test/test-for-of.html new file mode 100644 index 000000000000..5d72a5aacd1a --- /dev/null +++ b/browser/devtools/webconsole/test/test-for-of.html @@ -0,0 +1,7 @@ + + + +

a

+

b

+

c

+

d

diff --git a/dom/tests/mochitest/general/Makefile.in b/dom/tests/mochitest/general/Makefile.in index 28886e9d24e5..be2d1fe163c8 100644 --- a/dom/tests/mochitest/general/Makefile.in +++ b/dom/tests/mochitest/general/Makefile.in @@ -79,6 +79,7 @@ _TEST_FILES = \ test_browserFrame4.html \ test_browserFrame5.html \ test_browserFrame6.html \ + test_for_of.html \ $(NULL) _CHROME_FILES = \ diff --git a/dom/tests/mochitest/general/test_for_of.html b/dom/tests/mochitest/general/test_for_of.html new file mode 100644 index 000000000000..0f8ddc603ea7 --- /dev/null +++ b/dom/tests/mochitest/general/test_for_of.html @@ -0,0 +1,25 @@ + + + + Tests for for-of loops + + + + +

+ + + + diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 62bb0c01a383..6a05607d8f52 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4410,6 +4410,15 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) return JS_TRUE; } +JS_PUBLIC_API(JSObject *) +JS_NewElementIterator(JSContext *cx, JSObject *obj) +{ + AssertNoGC(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj); + return ElementIteratorObject::create(cx, obj); +} + JS_PUBLIC_API(JSBool) JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 0d5c2f460f55..a0e92b2da4af 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4047,6 +4047,13 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); +/* + * Create an object to iterate over the elements of obj in for-of order. This + * can be used to implement the iteratorObject hook for an array-like Class. + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewElementIterator(JSContext *cx, JSObject *obj); + extern JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp); diff --git a/js/xpconnect/src/dombindings.cpp b/js/xpconnect/src/dombindings.cpp index ad38af70d63e..e3779f450abc 100644 --- a/js/xpconnect/src/dombindings.cpp +++ b/js/xpconnect/src/dombindings.cpp @@ -1249,6 +1249,13 @@ template bool ListBase::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp) { + if (flags == JSITER_FOR_OF) { + JSObject *iterobj = JS_NewElementIterator(cx, proxy); + if (!iterobj) + return false; + vp->setObject(*iterobj); + return true; + } return ProxyHandler::iterate(cx, proxy, flags, vp); } From 487db5a582f205fcdd75c388a9b8cee507afe84c Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 7 Feb 2012 12:57:17 -0600 Subject: [PATCH 11/28] Bug 699565 - Part 5 - for-of loops on arguments objects. r=bhackett. --- js/src/jit-test/tests/for-of/arguments-1.js | 15 ++++++++++++ js/src/jit-test/tests/for-of/arguments-2.js | 11 +++++++++ js/src/jit-test/tests/for-of/arguments-3.js | 16 +++++++++++++ js/src/jit-test/tests/for-of/arguments-4.js | 15 ++++++++++++ js/src/jit-test/tests/for-of/arguments-5.js | 16 +++++++++++++ js/src/jit-test/tests/for-of/arguments-6.js | 12 ++++++++++ js/src/jit-test/tests/for-of/arguments-7.js | 13 +++++++++++ js/src/jsapi.cpp | 7 ++++++ js/src/jsapi.h | 8 +++++++ js/src/jsarray.cpp | 11 ++------- js/src/jsfun.cpp | 26 +++++++++++++++++---- 11 files changed, 137 insertions(+), 13 deletions(-) create mode 100644 js/src/jit-test/tests/for-of/arguments-1.js create mode 100644 js/src/jit-test/tests/for-of/arguments-2.js create mode 100644 js/src/jit-test/tests/for-of/arguments-3.js create mode 100644 js/src/jit-test/tests/for-of/arguments-4.js create mode 100644 js/src/jit-test/tests/for-of/arguments-5.js create mode 100644 js/src/jit-test/tests/for-of/arguments-6.js create mode 100644 js/src/jit-test/tests/for-of/arguments-7.js diff --git a/js/src/jit-test/tests/for-of/arguments-1.js b/js/src/jit-test/tests/for-of/arguments-1.js new file mode 100644 index 000000000000..945c6b221c70 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-1.js @@ -0,0 +1,15 @@ +// for-of can iterate arguments objects. + +var s; +function test() { + for (var v of arguments) + s += v; +} + +s = ''; +test(); +assertEq(s, ''); + +s = ''; +test('x', 'y'); +assertEq(s, 'xy'); diff --git a/js/src/jit-test/tests/for-of/arguments-2.js b/js/src/jit-test/tests/for-of/arguments-2.js new file mode 100644 index 000000000000..f1781c91f2e9 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-2.js @@ -0,0 +1,11 @@ +// for-of can iterate arguments objects after returning. + +function f() { + return arguments; +} + +var s = ''; +var args = f('a', 'b', 'c'); +for (var v of args) + s += v; +assertEq(s, 'abc'); diff --git a/js/src/jit-test/tests/for-of/arguments-3.js b/js/src/jit-test/tests/for-of/arguments-3.js new file mode 100644 index 000000000000..8550eff347a3 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-3.js @@ -0,0 +1,16 @@ +// for-of can iterate strict arguments objects. + +var s; +function test() { + "use strict"; + for (var v of arguments) + s += v; +} + +s = ''; +test(); +assertEq(s, ''); + +s = ''; +test('a', 'b'); +assertEq(s, 'ab'); diff --git a/js/src/jit-test/tests/for-of/arguments-4.js b/js/src/jit-test/tests/for-of/arguments-4.js new file mode 100644 index 000000000000..08266ba905a6 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-4.js @@ -0,0 +1,15 @@ +// for-of can iterate arguments objects for other active frames. + +var s; +function g(obj) { + for (var v of obj) + s += v; +} + +function f() { + g(arguments); +} + +s = ''; +f(1, 2, 3); +assertEq(s, '123'); diff --git a/js/src/jit-test/tests/for-of/arguments-5.js b/js/src/jit-test/tests/for-of/arguments-5.js new file mode 100644 index 000000000000..fc88020d6ab4 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-5.js @@ -0,0 +1,16 @@ +// for-of can iterate strict arguments objects in non-strict code. + +var s; +function g(obj) { + for (var v of obj) + s += v; +} + +function f() { + "use strict"; + g(arguments); +} + +s = ''; +f(1, 2, 3); +assertEq(s, '123'); diff --git a/js/src/jit-test/tests/for-of/arguments-6.js b/js/src/jit-test/tests/for-of/arguments-6.js new file mode 100644 index 000000000000..a5aed1e35de9 --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-6.js @@ -0,0 +1,12 @@ +// Changing arguments.length affects a for-of loop iterating over arguments. + +var s; +function f() { + arguments.length = 2; + for (var v of arguments) + s += v; +} + +s = ''; +f('a', 'b', 'c', 'd', 'e'); +assertEq(s, 'ab'); diff --git a/js/src/jit-test/tests/for-of/arguments-7.js b/js/src/jit-test/tests/for-of/arguments-7.js new file mode 100644 index 000000000000..5f6488f8a28f --- /dev/null +++ b/js/src/jit-test/tests/for-of/arguments-7.js @@ -0,0 +1,13 @@ +// Changing arguments.length during a for-of loop iterating over arguments affects the loop. + +var s; +function f() { + for (var v of arguments) { + s += v; + arguments.length--; + } +} + +s = ''; +f('a', 'b', 'c', 'd', 'e'); +assertEq(s, 'abc'); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 6a05607d8f52..8d320dcdd385 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4419,6 +4419,13 @@ JS_NewElementIterator(JSContext *cx, JSObject *obj) return ElementIteratorObject::create(cx, obj); } +JS_PUBLIC_API(JSObject *) +JS_ElementIteratorStub(JSContext *cx, JSObject *obj, JSBool keysonly) +{ + JS_ASSERT(!keysonly); + return JS_NewElementIterator(cx, obj); +} + JS_PUBLIC_API(JSBool) JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index a0e92b2da4af..de49e7246a9c 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4054,6 +4054,14 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); extern JS_PUBLIC_API(JSObject *) JS_NewElementIterator(JSContext *cx, JSObject *obj); +/* + * To make your array-like class iterable using the for-of loop, set the + * JSCLASS_FOR_OF_ITERATION bit in the class's flags field and set its + * .ext.iteratorObject hook to this function. + */ +extern JS_PUBLIC_API(JSObject *) +JS_ElementIteratorStub(JSContext *cx, JSObject *obj, JSBool keysonly); + extern JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 75559a6516ec..dc1e0bbbc033 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -679,13 +679,6 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value return true; } -static JSObject * -array_iterator(JSContext *cx, JSObject *obj, JSBool keysonly) -{ - JS_ASSERT(!keysonly); - return ElementIteratorObject::create(cx, obj); -} - /* Returns true if the dense array has an own property at the index. */ static inline bool IsDenseArrayIndex(JSObject *obj, uint32_t index) @@ -1242,7 +1235,7 @@ Class js::ArrayClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - array_iterator, + JS_ElementIteratorStub, NULL, /* unused */ false, /* isWrappedNative */ }, @@ -1306,7 +1299,7 @@ Class js::SlowArrayClass = { NULL, /* equality */ NULL, /* outerObject */ NULL, /* innerObject */ - array_iterator, + JS_ElementIteratorStub, NULL, /* unused */ false, /* isWrappedNative */ } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index afb11fb7e53d..7a0b48bc33d0 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -559,7 +559,8 @@ Class js::NormalArgumentsObjectClass = { "Arguments", JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), + JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | + JSCLASS_FOR_OF_ITERATION, JS_PropertyStub, /* addProperty */ args_delProperty, JS_PropertyStub, /* getProperty */ @@ -574,7 +575,15 @@ Class js::NormalArgumentsObjectClass = { NULL, /* construct */ NULL, /* xdrObject */ NULL, /* hasInstance */ - args_trace + args_trace, + { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + JS_ElementIteratorStub, + NULL, /* unused */ + false, /* isWrappedNative */ + } }; /* @@ -586,7 +595,8 @@ Class js::StrictArgumentsObjectClass = { "Arguments", JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), + JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | + JSCLASS_FOR_OF_ITERATION, JS_PropertyStub, /* addProperty */ args_delProperty, JS_PropertyStub, /* getProperty */ @@ -601,7 +611,15 @@ Class js::StrictArgumentsObjectClass = { NULL, /* construct */ NULL, /* xdrObject */ NULL, /* hasInstance */ - args_trace + args_trace, + { + NULL, /* equality */ + NULL, /* outerObject */ + NULL, /* innerObject */ + JS_ElementIteratorStub, + NULL, /* unused */ + false, /* isWrappedNative */ + } }; namespace js { From 7d08e94bef0fd8c7e4f5437bbdb7fe6d88be9252 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 7 Feb 2012 12:57:17 -0600 Subject: [PATCH 12/28] Bug 699565 - Part 6 - for-of loops on typed arrays. r=bhackett. --- js/src/jit-test/tests/for-of/typedarrays-1.js | 7 +++++++ js/src/jit-test/tests/for-of/typedarrays-2.js | 11 +++++++++++ js/src/jit-test/tests/for-of/typedarrays-3.js | 4 ++++ js/src/jit-test/tests/for-of/typedarrays-4.js | 7 +++++++ js/src/jit-test/tests/for-of/typedarrays-5.js | 7 +++++++ js/src/jit-test/tests/for-of/typedarrays-6.js | 9 +++++++++ js/src/jstypedarray.cpp | 10 +++++++++- 7 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/for-of/typedarrays-1.js create mode 100644 js/src/jit-test/tests/for-of/typedarrays-2.js create mode 100644 js/src/jit-test/tests/for-of/typedarrays-3.js create mode 100644 js/src/jit-test/tests/for-of/typedarrays-4.js create mode 100644 js/src/jit-test/tests/for-of/typedarrays-5.js create mode 100644 js/src/jit-test/tests/for-of/typedarrays-6.js diff --git a/js/src/jit-test/tests/for-of/typedarrays-1.js b/js/src/jit-test/tests/for-of/typedarrays-1.js new file mode 100644 index 000000000000..a48a52591df7 --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-1.js @@ -0,0 +1,7 @@ +// for-of can iterate over typed arrays. + +var a = Int8Array([0, 1, -7, 3]) +var s = ''; +for (var v of a) + s += v + ','; +assertEq(s, '0,1,-7,3,'); diff --git a/js/src/jit-test/tests/for-of/typedarrays-2.js b/js/src/jit-test/tests/for-of/typedarrays-2.js new file mode 100644 index 000000000000..47eeec3ad8e2 --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-2.js @@ -0,0 +1,11 @@ +// The body of a for-of loop does not run if the target is an empty typed array. + +for (x of Int16Array(0)) + throw "FAIL"; +for (x of Float32Array(0)) + throw "FAIL"; + +var a = Int8Array([0, 1, 2, 3]).subarray(2, 2); +assertEq(a.length, 0); +for (v of a) + throw "FAIL"; diff --git a/js/src/jit-test/tests/for-of/typedarrays-3.js b/js/src/jit-test/tests/for-of/typedarrays-3.js new file mode 100644 index 000000000000..10ea7ae479e1 --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-3.js @@ -0,0 +1,4 @@ +// Destructuring does not occur when the target of for-of is an empty typed array. + +for (var [[x]] of Int32Array(0)) + throw "FAIL"; diff --git a/js/src/jit-test/tests/for-of/typedarrays-4.js b/js/src/jit-test/tests/for-of/typedarrays-4.js new file mode 100644 index 000000000000..ec2f8ffb5458 --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-4.js @@ -0,0 +1,7 @@ +// for-of throws if the target is a typed array prototype object. + +load(libdir + "asserts.js"); +assertThrowsInstanceOf(function () { + for (var v of Int8Array.prototype) + throw "FAIL"; +}, TypeError); diff --git a/js/src/jit-test/tests/for-of/typedarrays-5.js b/js/src/jit-test/tests/for-of/typedarrays-5.js new file mode 100644 index 000000000000..8026db428247 --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-5.js @@ -0,0 +1,7 @@ +// for-of throws if the target is an ArrayBuffer. + +load(libdir + "asserts.js"); +assertThrowsInstanceOf(function () { + for (var v of Int8Array([0, 1, 2, 3]).buffer) + throw "FAIL"; +}, TypeError); diff --git a/js/src/jit-test/tests/for-of/typedarrays-6.js b/js/src/jit-test/tests/for-of/typedarrays-6.js new file mode 100644 index 000000000000..d91d8f3026de --- /dev/null +++ b/js/src/jit-test/tests/for-of/typedarrays-6.js @@ -0,0 +1,9 @@ +// for-of can iterate over float typed arrays containing infinities or NaNs. + +var values = [Infinity, -Infinity, -0, NaN]; +for (var C of [Float32Array, Float64Array]) { + var i = 0; + for (var v of C(values)) + assertEq(v, values[i++]); + assertEq(i, values.length); +} diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 7e853f3fecde..f3ac1049072a 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -2299,6 +2299,7 @@ JSFunctionSpec _typedArray::jsfuncs[] = { \ #_typedArray, \ JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \ JSCLASS_HAS_PRIVATE | \ + JSCLASS_FOR_OF_ITERATION | \ Class::NON_NATIVE, \ JS_PropertyStub, /* addProperty */ \ JS_PropertyStub, /* delProperty */ \ @@ -2315,7 +2316,14 @@ JSFunctionSpec _typedArray::jsfuncs[] = { \ NULL, /* xdrObject */ \ NULL, /* hasInstance */ \ _typedArray::obj_trace, /* trace */ \ - JS_NULL_CLASS_EXT, \ + { \ + NULL, /* equality */ \ + NULL, /* outerObject */ \ + NULL, /* innerObject */ \ + JS_ElementIteratorStub, \ + NULL, /* unused */ \ + false, /* isWrappedNative */ \ + }, \ { \ _typedArray::obj_lookupGeneric, \ _typedArray::obj_lookupProperty, \ From ba2512e7ca74e32b3387c4554afc991efa1394d9 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 7 Feb 2012 14:10:58 -0500 Subject: [PATCH 13/28] Bug 724949 - Extract a PixelTest class to reuse common pixel-checking operations. r=jmaher --- mobile/android/base/tests/PixelTest.java.in | 55 +++++++++++++++++++ mobile/android/base/tests/robocop_boxes.html | 16 +++++- mobile/android/base/tests/testLoad.java.in | 18 +++--- mobile/android/base/tests/testPan.java.in | 12 ++-- .../base/tests/testPanCorrectness.java.in | 49 +++++------------ .../android/base/tests/test_bug720538.java.in | 15 ++--- 6 files changed, 106 insertions(+), 59 deletions(-) create mode 100644 mobile/android/base/tests/PixelTest.java.in diff --git a/mobile/android/base/tests/PixelTest.java.in b/mobile/android/base/tests/PixelTest.java.in new file mode 100644 index 000000000000..fdda895aed5d --- /dev/null +++ b/mobile/android/base/tests/PixelTest.java.in @@ -0,0 +1,55 @@ +#filter substitution +package @ANDROID_PACKAGE_NAME@.tests; + +import @ANDROID_PACKAGE_NAME@.*; + +class PixelTest extends BaseTest { + private static final long PAINT_CLEAR_DELAY = 500; // milliseconds + + protected final int[][] loadAndPaint(String url) { + Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint(); + loadUrl(url); + paintExpecter.blockUntilClear(PAINT_CLEAR_DELAY); + return mDriver.getPaintedSurface(); + } + + protected final int[][] waitForPaint(Actions.RepeatedEventExpecter expecter) { + expecter.blockUntilClear(PAINT_CLEAR_DELAY); + return mDriver.getPaintedSurface(); + } + + // this matches the algorithm in robocop_boxes.html + protected final int[] getBoxColorAt(int x, int y) { + x -= (x % 100); + y -= (y % 100); + int r = (x + y) % 255; + int g = 255 - (y / 10); + int b = 255 - (x / 10); + return new int[] { r, g, b }; + } + + /** + * Checks the top-left corner of the visible area of the page is at (x,y) of robocop_boxes.html. + */ + protected final void checkScrollWithBoxes(int[][] painted, int x, int y) { + int[] color = getBoxColorAt(x, y); + mAsserter.ispixel(painted[0][0], color[0], color[1], color[2], "Pixel at 0, 0"); + color = getBoxColorAt(x + 100, y); + mAsserter.ispixel(painted[0][100], color[0], color[1], color[2], "Pixel at 100, 0"); + color = getBoxColorAt(x, y + 100); + mAsserter.ispixel(painted[100][0], color[0], color[1], color[2], "Pixel at 0, 100"); + color = getBoxColorAt(x + 100, y + 100); + mAsserter.ispixel(painted[100][100], color[0], color[1], color[2], "Pixel at 100, 100"); + } + + /** + * Loads the robocop_boxes.html file and verifies that we are positioned at (0,0) on it. + * @param url URL of the robocop_boxes.html file. + * @return The painted surface after rendering the file. + */ + protected final int[][] loadAndVerifyBoxes(String url) { + int[][] painted = loadAndPaint(url); + checkScrollWithBoxes(painted, 0, 0); + return painted; + } +} diff --git a/mobile/android/base/tests/robocop_boxes.html b/mobile/android/base/tests/robocop_boxes.html index 85873e670260..3c97867997e2 100644 --- a/mobile/android/base/tests/robocop_boxes.html +++ b/mobile/android/base/tests/robocop_boxes.html @@ -1,5 +1,19 @@ + -Browser Box test + + Browser Box test + + + + + +
Mozilla Bug 394769 +

+ +
+
+
+ + From 5cc429ad7e0e0848e1dcb06d7291ec72ea95c4b6 Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Tue, 7 Feb 2012 12:32:56 -0800 Subject: [PATCH 27/28] Bug 724152 - Honor URL_SAFE flag for base64 encoding/decoding. r=blassey --- mobile/android/base/AwesomeBarTabs.java | 2 +- mobile/android/base/GeckoApp.java | 2 +- mobile/android/base/GeckoAppShell.java | 31 ++++++++++++------- mobile/android/base/Tabs.java | 2 +- .../android/base/db/BrowserProvider.java.in | 2 +- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/mobile/android/base/AwesomeBarTabs.java b/mobile/android/base/AwesomeBarTabs.java index 69942cbd2e9d..c903020cbc6b 100644 --- a/mobile/android/base/AwesomeBarTabs.java +++ b/mobile/android/base/AwesomeBarTabs.java @@ -556,7 +556,7 @@ public class AwesomeBarTabs extends TabHost { String base64 = dataURI.substring(dataURI.indexOf(',') + 1); Drawable drawable = null; try { - byte[] bytes = GeckoAppShell.decodeBase64(base64); + byte[] bytes = GeckoAppShell.decodeBase64(base64, GeckoAppShell.BASE64_DEFAULT); ByteArrayInputStream stream = new ByteArrayInputStream(bytes); drawable = Drawable.createFromStream(stream, "src"); stream.close(); diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 025798f32afd..2c98c5125d36 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -399,7 +399,7 @@ abstract public class GeckoApp final MenuItem mi = aMenu.add(Menu.NONE, item.id, Menu.NONE, item.label); if (item.icon != null) { if (item.icon.startsWith("data")) { - byte[] raw = GeckoAppShell.decodeBase64(item.icon.substring(22)); + byte[] raw = GeckoAppShell.decodeBase64(item.icon.substring(22), GeckoAppShell.BASE64_DEFAULT); Bitmap bitmap = BitmapFactory.decodeByteArray(raw, 0, raw.length); BitmapDrawable drawable = new BitmapDrawable(bitmap); mi.setIcon(drawable); diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 319453be16d4..1ac7748ad7f2 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -1868,19 +1868,24 @@ public class GeckoAppShell GeckoNetworkManager.getInstance().disableNotifications(); } - private static final int GUID_ENCODE_FLAGS = Base64.URL_SAFE | Base64.NO_WRAP; + // values taken from android's Base64 + public static final int BASE64_DEFAULT = 0; + public static final int BASE64_URL_SAFE = 8; /** * taken from http://www.source-code.biz/base64coder/java/Base64Coder.java.txt and modified (MIT License) */ // Mapping table from 6-bit nibbles to Base64 characters. private static final byte[] map1 = new byte[64]; + private static final byte[] map1_urlsafe; static { int i=0; for (byte c='A'; c<='Z'; c++) map1[i++] = c; for (byte c='a'; c<='z'; c++) map1[i++] = c; for (byte c='0'; c<='9'; c++) map1[i++] = c; - map1[i++] = '-'; map1[i++] = '_'; + map1[i++] = '+'; map1[i++] = '/'; + map1_urlsafe = map1.clone(); + map1_urlsafe[62] = '-'; map1_urlsafe[63] = '_'; } // Mapping table from Base64 characters to 6-bit nibbles. @@ -1888,6 +1893,7 @@ public class GeckoAppShell static { for (int i=0; i