Bug 767186: Cache the keyPath for objectStores and indexes. r=bent
authorKyle Huey <khuey@kylehuey.com>
Fri, 22 Jun 2012 14:00:53 -0700
changeset 102194 4cbc78fd6eef1c2ce5a2a457f2354d4175680164
parent 102193 66deabf32a65f26adb7ffa621729e756535a99b0
child 102195 05e8a5d923457f0ffecd2973dcee1dc840738af7
push id1316
push userakeybl@mozilla.com
push dateMon, 27 Aug 2012 22:37:00 +0000
treeherdermozilla-beta@db4b09302ee2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs767186
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 767186: Cache the keyPath for objectStores and indexes. r=bent
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBIndex.h
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/indexedDB/test/unit/test_complex_keyPaths.js
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -6,16 +6,17 @@
 
 #include "base/basictypes.h"
 
 #include "IDBIndex.h"
 
 #include "nsIIDBKeyRange.h"
 #include "nsIJSContextStack.h"
 
+#include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
 #include "mozilla/storage.h"
 #include "xpcpublic.h"
 
 #include "AsyncConnectionHelper.h"
 #include "IDBCursor.h"
@@ -391,26 +392,33 @@ IDBIndex::Create(IDBObjectStore* aObject
   return index.forget();
 }
 
 IDBIndex::IDBIndex()
 : mId(LL_MININT),
   mActorChild(nsnull),
   mActorParent(nsnull),
   mKeyPath(0),
+  mCachedKeyPath(JSVAL_VOID),
   mUnique(false),
-  mMultiEntry(false)
+  mMultiEntry(false),
+  mRooted(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBIndex::~IDBIndex()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
+
+  if (mRooted) {
+    NS_DROP_JS_OBJECTS(this, IDBIndex);
+  }
+
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
 }
 
 nsresult
@@ -645,22 +653,34 @@ IDBIndex::OpenCursorFromChildProcess(
   NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
 
   cursor.forget(_retval);
   return NS_OK;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex)
 
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBIndex)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKeyPath)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObjectStore)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex)
   // Don't unlink mObjectStore!
+
+  tmp->mCachedKeyPath = JSVAL_VOID;
+
+  if (tmp->mRooted) {
+    NS_DROP_JS_OBJECTS(tmp, IDBIndex);
+    tmp->mRooted = false;
+  }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex)
   NS_INTERFACE_MAP_ENTRY(nsIIDBIndex)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBIndex)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
@@ -687,17 +707,31 @@ IDBIndex::GetStoreName(nsAString& aStore
 }
 
 NS_IMETHODIMP
 IDBIndex::GetKeyPath(JSContext* aCx,
                      jsval* aVal)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  return GetKeyPath().ToJSVal(aCx, aVal);
+  if (!JSVAL_IS_VOID(mCachedKeyPath)) {
+    *aVal = mCachedKeyPath;
+    return NS_OK;
+  }
+
+  nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (JSVAL_IS_GCTHING(mCachedKeyPath)) {
+    NS_HOLD_JS_OBJECTS(this, IDBIndex);
+    mRooted = true;
+  }
+
+  *aVal = mCachedKeyPath;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBIndex::GetUnique(bool* aUnique)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   *aUnique = mUnique;
--- a/dom/indexedDB/IDBIndex.h
+++ b/dom/indexedDB/IDBIndex.h
@@ -31,17 +31,17 @@ class Key;
 struct IndexInfo;
 
 class IDBIndex MOZ_FINAL : public nsIIDBIndex
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIIDBINDEX
 
-  NS_DECL_CYCLE_COLLECTION_CLASS(IDBIndex)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBIndex)
 
   static already_AddRefed<IDBIndex>
   Create(IDBObjectStore* aObjectStore,
          const IndexInfo* aIndexInfo,
          bool aCreating);
 
   IDBObjectStore* ObjectStore()
   {
@@ -144,19 +144,21 @@ private:
   IDBIndex();
   ~IDBIndex();
 
   nsRefPtr<IDBObjectStore> mObjectStore;
 
   PRInt64 mId;
   nsString mName;
   KeyPath mKeyPath;
+  JS::Value mCachedKeyPath;
 
   IndexedDBIndexChild* mActorChild;
   IndexedDBIndexParent* mActorParent;
 
   bool mUnique;
   bool mMultiEntry;
+  bool mRooted;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbindex_h__
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1305,32 +1305,38 @@ IDBObjectStore::ConvertFileIdsToArray(co
   }
 
   return NS_OK;
 }
 
 IDBObjectStore::IDBObjectStore()
 : mId(LL_MININT),
   mKeyPath(0),
+  mCachedKeyPath(JSVAL_VOID),
+  mRooted(false),
   mAutoIncrement(false),
   mActorChild(nsnull),
   mActorParent(nsnull)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBObjectStore::~IDBObjectStore()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
+
+  if (mRooted) {
+    NS_DROP_JS_OBJECTS(this, IDBObjectStore);
+  }
 }
 
 nsresult
 IDBObjectStore::GetAddInfo(JSContext* aCx,
                            jsval aValue,
                            jsval aKeyVal,
                            StructuredCloneWriteInfo& aCloneWriteInfo,
                            Key& aKey,
@@ -1745,30 +1751,42 @@ IDBObjectStore::IndexInternal(const nsAS
   }
 
   retval.forget(_retval);
   return NS_OK;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
 
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKeyPath)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction,
                                                        nsIDOMEventTarget)
 
   for (PRUint32 i = 0; i < tmp->mCreatedIndexes.Length(); i++) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedIndexes[i]");
     cb.NoteXPCOMChild(static_cast<nsIIDBIndex*>(tmp->mCreatedIndexes[i].get()));
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
   // Don't unlink mTransaction!
 
   tmp->mCreatedIndexes.Clear();
+
+  tmp->mCachedKeyPath = JSVAL_VOID;
+
+  if (tmp->mRooted) {
+    NS_DROP_JS_OBJECTS(tmp, IDBObjectStore);
+    tmp->mRooted = false;
+  }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)
   NS_INTERFACE_MAP_ENTRY(nsIIDBObjectStore)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBObjectStore)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
@@ -1787,17 +1805,31 @@ IDBObjectStore::GetName(nsAString& aName
 }
 
 NS_IMETHODIMP
 IDBObjectStore::GetKeyPath(JSContext* aCx,
                            jsval* aVal)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  return GetKeyPath().ToJSVal(aCx, aVal);
+  if (!JSVAL_IS_VOID(mCachedKeyPath)) {
+    *aVal = mCachedKeyPath;
+    return NS_OK;
+  }
+
+  nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (JSVAL_IS_GCTHING(mCachedKeyPath)) {
+    NS_HOLD_JS_OBJECTS(this, IDBObjectStore);
+    mRooted = true;
+  }
+
+  *aVal = mCachedKeyPath;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBObjectStore::GetTransaction(nsIIDBTransaction** aTransaction)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIIDBTransaction> transaction(mTransaction);
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -37,17 +37,17 @@ struct StructuredCloneReadInfo;
 struct StructuredCloneWriteInfo;
 
 class IDBObjectStore MOZ_FINAL : public nsIIDBObjectStore
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIIDBOBJECTSTORE
 
-  NS_DECL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBObjectStore)
 
   static already_AddRefed<IDBObjectStore>
   Create(IDBTransaction* aTransaction,
          ObjectStoreInfo* aInfo,
          nsIAtom* aDatabaseId,
          bool aCreating);
 
   static nsresult
@@ -233,16 +233,18 @@ protected:
                     IDBRequest** _retval);
 
 private:
   nsRefPtr<IDBTransaction> mTransaction;
 
   PRInt64 mId;
   nsString mName;
   KeyPath mKeyPath;
+  JS::Value mCachedKeyPath;
+  bool mRooted;
   bool mAutoIncrement;
   nsCOMPtr<nsIAtom> mDatabaseId;
   nsRefPtr<ObjectStoreInfo> mInfo;
   PRUint32 mStructuredCloneVersion;
 
   nsTArray<nsRefPtr<IDBIndex> > mCreatedIndexes;
 
   IndexedDBObjectStoreChild* mActorChild;
--- a/dom/indexedDB/test/unit/test_complex_keyPaths.js
+++ b/dom/indexedDB/test/unit/test_complex_keyPaths.js
@@ -81,16 +81,18 @@ function testSteps()
     let test = " for objectStore test " + JSON.stringify(info);
     let indexName = JSON.stringify(info.keyPath);
     if (!stores[indexName]) {
       try {
         let objectStore = db.createObjectStore(indexName, { keyPath: info.keyPath });
         ok(!("exception" in info), "shouldn't throw" + test);
         is(JSON.stringify(objectStore.keyPath), JSON.stringify(info.keyPath),
            "correct keyPath property" + test);
+        ok(objectStore.keyPath === objectStore.keyPath,
+           "object identity should be preserved");
         stores[indexName] = objectStore;
       } catch (e) {
         ok("exception" in info, "should throw" + test);
         is(e.name, "SyntaxError", "expect a SyntaxError" + test);
         ok(e instanceof DOMException, "Got a DOM Exception" + test);
         is(e.code, DOMException.SYNTAX_ERR, "expect a syntax error" + test);
         continue;
       }
@@ -134,16 +136,18 @@ function testSteps()
     let info = keyPaths[i];
     let indexName = JSON.stringify(info.keyPath);
     if (!indexes[indexName]) {
       try {
         let index = store.createIndex(indexName, info.keyPath);
         ok(!("exception" in info), "shouldn't throw" + test);
         is(JSON.stringify(index.keyPath), JSON.stringify(info.keyPath),
            "index has correct keyPath property" + test);
+        ok(index.keyPath === index.keyPath,
+           "object identity should be preserved");
         indexes[indexName] = index;
       } catch (e) {
         ok("exception" in info, "should throw" + test);
         is(e.name, "SyntaxError", "expect a SyntaxError" + test);
         ok(e instanceof DOMException, "Got a DOM Exception" + test);
         is(e.code, DOMException.SYNTAX_ERR, "expect a syntax error" + test);
         continue;
       }