merge the last green changeset on m-c to fx-team
authorTim Taubert <tim.taubert@gmx.de>
Wed, 07 Sep 2011 10:04:58 +0200
changeset 77973 02f42784ccb28b942b345d44d3465daf4ed78376
parent 77972 f9c574a28a7a33a63fcc80ffbac9ab65d552c92b (current diff)
parent 77901 445b1e86590c42f2a5a66eacd15164fb6125cd46 (diff)
child 77974 e4ab06518d81c805bf0b6f80ca97b93b29761278
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone9.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
merge the last green changeset on m-c to fx-team
extensions/spellcheck/tests/Makefile.in
extensions/spellcheck/tests/chrome/Makefile.in
extensions/spellcheck/tests/chrome/base/Makefile.in
extensions/spellcheck/tests/chrome/base/base_utf.aff
extensions/spellcheck/tests/chrome/base/base_utf.dic
extensions/spellcheck/tests/chrome/map/Makefile.in
extensions/spellcheck/tests/chrome/map/maputf.aff
extensions/spellcheck/tests/chrome/map/maputf.dic
extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -316,18 +316,20 @@ nsContextMenu.prototype = {
                   !this.onTextInput && top.gBidiUI);
   },
 
   initSpellingItems: function() {
     var canSpell = InlineSpellCheckerUI.canSpellCheck;
     var onMisspelling = InlineSpellCheckerUI.overMisspelling;
     this.showItem("spell-check-enabled", canSpell);
     this.showItem("spell-separator", canSpell || this.onEditableArea);
-    document.getElementById("spell-check-enabled")
-            .setAttribute("checked", canSpell && InlineSpellCheckerUI.enabled);
+    if (canSpell) {
+      document.getElementById("spell-check-enabled")
+              .setAttribute("checked", InlineSpellCheckerUI.enabled);
+    }
 
     this.showItem("spell-add-to-dictionary", onMisspelling);
 
     // suggestion list
     this.showItem("spell-suggestions-separator", onMisspelling);
     if (onMisspelling) {
       var suggestionsSeparator =
         document.getElementById("spell-add-to-dictionary");
--- a/editor/composer/src/nsEditorSpellCheck.cpp
+++ b/editor/composer/src/nsEditorSpellCheck.cpp
@@ -645,24 +645,16 @@ nsEditorSpellCheck::SetCurrentDictionary
 
     // Also store it in as a preference. It will be used as a default value
     // when everything else fails.
     Preferences::SetString("spellchecker.dictionary", aDictionary);
   }
   return mSpellChecker->SetCurrentDictionary(aDictionary);
 }
 
-NS_IMETHODIMP
-nsEditorSpellCheck::GetSpellChecker(nsISpellChecker **aSpellChecker)
-{
-  *aSpellChecker = mSpellChecker;
-  NS_IF_ADDREF(*aSpellChecker);
-  return NS_OK;
-}
-
 NS_IMETHODIMP    
 nsEditorSpellCheck::UninitSpellChecker()
 {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
   // Cleanup - kill the spell checker
   DeleteSuggestedWordList();
   mDictionaryList.Clear();
@@ -821,17 +813,21 @@ nsEditorSpellCheck::UpdateCurrentDiction
   // lang attribute, we try to get a dictionary. First try, en-US. If it does
   // not work, pick the first one.
   if (mPreferredLang.IsEmpty()) {
     nsAutoString currentDictionary;
     rv = GetCurrentDictionary(currentDictionary);
     if (NS_FAILED(rv) || currentDictionary.IsEmpty()) {
       rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US"));
       if (NS_FAILED(rv)) {
-        mSpellChecker->CheckCurrentDictionary();
+        nsTArray<nsString> dictList;
+        rv = mSpellChecker->GetDictionaryList(&dictList);
+        if (NS_SUCCEEDED(rv) && dictList.Length() > 0) {
+          SetCurrentDictionary(dictList[0]);
+        }
       }
     }
   }
 
   // If an error was thrown while setting the dictionary, just
   // fail silently so that the spellchecker dialog is allowed to come
   // up. The user can manually reset the language to their choice on
   // the dialog if it is wrong.
--- a/editor/idl/nsIEditorSpellCheck.idl
+++ b/editor/idl/nsIEditorSpellCheck.idl
@@ -35,30 +35,21 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
  
 #include "nsISupports.idl"
 
 interface nsIEditor;
 interface nsITextServicesFilter;
-%{C++
-#include "nsISpellChecker.h"
-%}
-[ptr] native nsISpellChecker(nsISpellChecker);
 
-[scriptable, uuid(334946c3-0e93-4aac-b662-e1b56f95d68b)]
+[scriptable, uuid(af84da62-588f-409f-847d-feecc991bd93)]
 interface nsIEditorSpellCheck : nsISupports
 {
 
-  /**
-   * Get the spell checker used by this editor.
-   */
-  [noscript] readonly attribute nsISpellChecker spellChecker;
-
  /**
    * Returns true if we can enable spellchecking. If there are no available
    * dictionaries, this will return false.
    */
   boolean       canSpellCheck();
 
   /**
    * Turns on the spell checker for the given editor. enableSelectionChecking
--- a/editor/libeditor/base/Makefile.in
+++ b/editor/libeditor/base/Makefile.in
@@ -89,10 +89,9 @@ FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES	+= \
 		-I$(topsrcdir)/editor/libeditor/text \
 		-I$(topsrcdir)/content/base/src \
 		-I$(topsrcdir)/content/events/src \
 		-I$(topsrcdir)/layout/style \
-		-I$(topsrcdir)/extensions/spellcheck/src \
 		$(NULL)
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -19,17 +19,16 @@
  * Portions created by the Initial Developer are Copyright (C) 1998
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Pierre Phaneuf <pp@ludusdesign.com>
  *   Daniel Glazman <glazman@netscape.com>
  *   Masayuki Nakano <masayuki@d-toybox.com>
  *   Mats Palmgren <matspal@gmail.com>
- *   Jesper Kristensen <mail@jesperkristensen.dk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -44,21 +43,16 @@
 #include "nsIDOMDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMNSHTMLElement.h"
 #include "nsIDOMNSEvent.h"
 #include "nsIMEStateManager.h"
 #include "nsFocusManager.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
-#include "nsIObserverService.h"
-#include "mozilla/Services.h"
-#include "mozISpellCheckingEngine.h"
-#include "nsIEditorSpellCheck.h"
-#include "mozInlineSpellChecker.h"
 
 #include "nsIDOMText.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMAttr.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsIDOMNamedNodeMap.h"
 #include "nsIDOMNodeList.h"
@@ -208,17 +202,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEventListener)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor)
  NS_INTERFACE_MAP_ENTRY(nsIPhonetic)
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport)
  NS_INTERFACE_MAP_ENTRY(nsIEditor)
- NS_INTERFACE_MAP_ENTRY(nsIObserver)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditor)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor)
 
 
 NS_IMETHODIMP
@@ -302,23 +295,16 @@ nsEditor::PostCreate()
 
     // nuke the modification count, so the doc appears unmodified
     // do this before we notify listeners
     ResetModificationCount();
 
     // update the UI with our state
     NotifyDocumentListeners(eDocumentCreated);
     NotifyDocumentListeners(eDocumentStateChanged);
-
-    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    if (obs) {
-      obs->AddObserver(this,
-                       SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION,
-                       PR_FALSE);
-    }
   }
 
   // update nsTextStateManager and caret if we have focus
   nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
   if (focusedContent) {
     nsCOMPtr<nsIPresShell> ps = GetPresShell();
     NS_ASSERTION(ps, "no pres shell even though we have focus");
     NS_ENSURE_TRUE(ps, NS_ERROR_UNEXPECTED);
@@ -421,22 +407,16 @@ nsEditor::GetDesiredSpellCheckState()
 }
 
 NS_IMETHODIMP
 nsEditor::PreDestroy(PRBool aDestroyingFrames)
 {
   if (mDidPreDestroy)
     return NS_OK;
 
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    obs->RemoveObserver(this,
-                        SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION);
-  }
-
   // Let spellchecker clean up its observers etc. It is important not to
   // actually free the spellchecker here, since the spellchecker could have
   // caused flush notifications, which could have gotten here if a textbox
   // is being removed. Setting the spellchecker to NULL could free the
   // object that is still in use! It will be freed when the editor is
   // destroyed.
   if (mInlineSpellChecker)
     mInlineSpellChecker->Cleanup(aDestroyingFrames);
@@ -1298,23 +1278,16 @@ NS_IMETHODIMP nsEditor::GetInlineSpellCh
 
   if (mDidPreDestroy) {
     // Don't allow people to get or create the spell checker once the editor
     // is going away.
     *aInlineSpellChecker = nsnull;
     return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK;
   }
 
-  // We don't want to show the spell checking UI if there are no spell check dictionaries available.
-  PRBool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking();
-  if (!canSpell) {
-    *aInlineSpellChecker = nsnull;
-    return NS_ERROR_FAILURE;
-  }
-
   nsresult rv;
   if (!mInlineSpellChecker && autoCreate) {
     mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (mInlineSpellChecker) {
     rv = mInlineSpellChecker->Init(this);
@@ -1323,59 +1296,27 @@ NS_IMETHODIMP nsEditor::GetInlineSpellCh
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP nsEditor::Observe(nsISupports* aSubj, const char *aTopic,
-                                const PRUnichar *aData)
-{
-  NS_ASSERTION(!strcmp(aTopic,
-                       SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION),
-               "Unexpected observer topic");
-
-  // When mozInlineSpellChecker::CanEnableInlineSpellChecking changes
-  SyncRealTimeSpell();
-
-  // When nsIEditorSpellCheck::GetCurrentDictionary changes
-  if (mInlineSpellChecker) {
-    // if the current dictionary is no longer available, find another one
-    nsCOMPtr<nsIEditorSpellCheck> editorSpellCheck;
-    mInlineSpellChecker->GetSpellChecker(getter_AddRefs(editorSpellCheck));
-    if (editorSpellCheck) {
-      nsCOMPtr<nsISpellChecker> spellChecker;
-      editorSpellCheck->GetSpellChecker(getter_AddRefs(spellChecker));
-      spellChecker->CheckCurrentDictionary();
-    }
-
-    // update the inline spell checker to reflect the new current dictionary
-    mInlineSpellChecker->SpellCheckRange(nsnull); // causes recheck
-  }
-
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsEditor::SyncRealTimeSpell()
 {
   NS_TIME_FUNCTION;
 
   PRBool enable = GetDesiredSpellCheckState();
 
-  // Initializes mInlineSpellChecker
   nsCOMPtr<nsIInlineSpellChecker> spellChecker;
   GetInlineSpellChecker(enable, getter_AddRefs(spellChecker));
 
-  if (mInlineSpellChecker) {
-    // We might have a mInlineSpellChecker even if there are no dictionaries
-    // available since we don't destroy the mInlineSpellChecker when the last
-    // dictionariy is removed, but in that case spellChecker is null
-    mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker);
+  if (spellChecker) {
+    spellChecker->SetEnableRealTimeSpell(enable);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsEditor::SetSpellcheckUserOverride(PRBool enable)
 {
   mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -61,17 +61,16 @@
 #include "nsIDOMElement.h"
 #include "nsSelectionState.h"
 #include "nsIEditorSpellCheck.h"
 #include "nsIInlineSpellChecker.h"
 #include "nsIDOMEventTarget.h"
 #include "nsStubMutationObserver.h"
 #include "nsIViewManager.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsIObserver.h"
 
 class nsIDOMCharacterData;
 class nsIDOMRange;
 class nsIPresShell;
 class ChangeAttributeTxn;
 class CreateElementTxn;
 class InsertElementTxn;
 class DeleteElementTxn;
@@ -96,17 +95,16 @@ class nsIDOMNSEvent;
 /** implementation of an editor object.  it will be the controller/focal point 
  *  for the main editor services. i.e. the GUIManager, publishing, transaction 
  *  manager, event interfaces. the idea for the event interfaces is to have them 
  *  delegate the actual commands to the editor independent of the XPFE implementation.
  */
 class nsEditor : public nsIEditor,
                  public nsIEditorIMESupport,
                  public nsSupportsWeakReference,
-                 public nsIObserver,
                  public nsIPhonetic
 {
 public:
 
   enum IterDirection
   {
     kIterForward,
     kIterBackward
@@ -150,19 +148,16 @@ public:
   already_AddRefed<nsIPresShell> GetPresShell();
   void NotifyEditorObservers();
 
   /* ------------ nsIEditor methods -------------- */
   NS_DECL_NSIEDITOR
   /* ------------ nsIEditorIMESupport methods -------------- */
   NS_DECL_NSIEDITORIMESUPPORT
   
-  /* ------------ nsIObserver methods -------------- */
-  NS_DECL_NSIOBSERVER
-
   // nsIPhonetic
   NS_DECL_NSIPHONETIC
 
 public:
 
   
   NS_IMETHOD InsertTextImpl(const nsAString& aStringToInsert, 
                                nsCOMPtr<nsIDOMNode> *aInOutNode, 
--- a/editor/txtsvc/public/nsISpellChecker.h
+++ b/editor/txtsvc/public/nsISpellChecker.h
@@ -39,19 +39,19 @@
 #define nsISpellChecker_h__
 
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 #define NS_SPELLCHECKER_CONTRACTID "@mozilla.org/spellchecker;1"
 
 #define NS_ISPELLCHECKER_IID                    \
-{ /* 27bff957-b486-40ae-9f5d-af0cdd211868 */    \
-0x27bff957, 0xb486, 0x40ae, \
-  { 0x9f, 0x5d, 0xaf, 0x0c, 0xdd, 0x21, 0x18, 0x68 } }
+{ /* E75AC48C-E948-452E-8DB3-30FEE29FE3D2 */    \
+0xe75ac48c, 0xe948, 0x452e, \
+  { 0x8d, 0xb3, 0x30, 0xfe, 0xe2, 0x9f, 0xe3, 0xd2 } }
 
 class nsITextServicesDocument;
 class nsString;
 
 /**
  * A generic interface for a spelling checker.
  */
 class nsISpellChecker  : public nsISupports{
@@ -141,20 +141,14 @@ public:
 
   /**
    * Tells the spellchecker to use a specific dictionary.
    * @param aDictionary a string that is in the list returned
    * by GetDictionaryList() or an empty string. If aDictionary is 
    * empty string, spellchecker will be disabled.
    */
   NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary) = 0;
-
-  /**
-   * Call this on any change in installed dictionaries to ensure that the spell
-   * checker is not using a current dictionary which is no longer available.
-   */
-  NS_IMETHOD CheckCurrentDictionary() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsISpellChecker, NS_ISPELLCHECKER_IID)
 
 #endif // nsISpellChecker_h__
 
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -5,17 +5,17 @@
       android:installLocation="auto"
       android:versionCode="@ANDROID_VERSION_CODE@"
       android:versionName="@MOZ_APP_VERSION@"
 #ifdef MOZ_ANDROID_SHARED_ID
       android:sharedUserId="@MOZ_ANDROID_SHARED_ID@"
 #endif
       >
     <uses-sdk android:minSdkVersion="5"
-              android:targetSdkVersion="11"/>
+              android:targetSdkVersion="5"/>
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
 
--- a/extensions/spellcheck/Makefile.in
+++ b/extensions/spellcheck/Makefile.in
@@ -39,13 +39,9 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= spellchecker
 DIRS		= idl locales hunspell src
 
-ifdef ENABLE_TESTS
-DIRS		+= tests
-endif
-
 include $(topsrcdir)/config/rules.mk
--- a/extensions/spellcheck/hunspell/src/Makefile.in
+++ b/extensions/spellcheck/hunspell/src/Makefile.in
@@ -66,13 +66,11 @@ CPPSRCS         += affentry.cpp \
 
 # This variable is referenced in configure.in.  Make sure to change that file
 # too if you need to change this variable.
 DEFINES = -DHUNSPELL_STATIC
 endif
 
 include $(topsrcdir)/config/rules.mk
 
-INCLUDES        += -I$(topsrcdir)/extensions/spellcheck/src
-
 ifdef MOZ_NATIVE_HUNSPELL
 CXXFLAGS += $(MOZ_HUNSPELL_CFLAGS)
 endif
--- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp
@@ -36,17 +36,16 @@
  *                 Varga Daniel
  *                 Chris Halls
  *                 Rene Engelhard
  *                 Bram Moolenaar
  *                 Dafydd Jones
  *                 Harri Pitkanen
  *                 Andras Timar
  *                 Tor Lillqvist
- *                 Jesper Kristensen (mail@jesperkristensen.dk)
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -66,18 +65,16 @@
 #include "nsIFile.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "mozISpellI18NManager.h"
 #include "nsICharsetConverterManager.h"
 #include "nsUnicharUtilCIID.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
-#include "mozInlineSpellChecker.h"
-#include "mozilla/Services.h"
 #include <stdlib.h>
 #include "nsIMemoryReporter.h"
 
 static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
 static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID);
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell)
@@ -120,17 +117,18 @@ NS_MEMORY_REPORTER_IMPLEMENT(Hunspell,
 nsresult
 mozHunspell::Init()
 {
   if (!mDictionaries.Init())
     return NS_ERROR_OUT_OF_MEMORY;
 
   LoadDictionaryList();
 
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  nsCOMPtr<nsIObserverService> obs =
+    do_GetService("@mozilla.org/observer-service;1");
   if (obs) {
     obs->AddObserver(this, "profile-do-change", PR_TRUE);
   }
 
   mHunspellReporter = new NS_MEMORY_REPORTER_NAME(Hunspell);
   NS_RegisterMemoryReporter(mHunspellReporter);
 
   return NS_OK;
@@ -144,58 +142,60 @@ mozHunspell::~mozHunspell()
   NS_UnregisterMemoryReporter(mHunspellReporter);
 }
 
 /* attribute wstring dictionary; */
 NS_IMETHODIMP mozHunspell::GetDictionary(PRUnichar **aDictionary)
 {
   NS_ENSURE_ARG_POINTER(aDictionary);
 
+  if (mDictionary.IsEmpty())
+    return NS_ERROR_NOT_INITIALIZED;
+
   *aDictionary = ToNewUnicode(mDictionary);
   return *aDictionary ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 /* set the Dictionary.
  * This also Loads the dictionary and initializes the converter using the dictionaries converter
  */
 NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary)
 {
   NS_ENSURE_ARG_POINTER(aDictionary);
 
+  if (mDictionary.Equals(aDictionary))
+    return NS_OK;
+
   nsIFile* affFile = mDictionaries.GetWeak(nsDependentString(aDictionary));
   if (!affFile)
     return NS_ERROR_FILE_NOT_FOUND;
 
   nsCAutoString dictFileName, affFileName;
 
   // XXX This isn't really good. nsIFile->NativePath isn't safe for all
   // character sets on Windows.
   // A better way would be to QI to nsILocalFile, and get a filehandle
   // from there. Only problem is that hunspell wants a path
 
   nsresult rv = affFile->GetNativePath(affFileName);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (mAffixFileName.Equals(affFileName.get()))
-    return NS_OK;
-
   dictFileName = affFileName;
   PRInt32 dotPos = dictFileName.RFindChar('.');
   if (dotPos == -1)
     return NS_ERROR_FAILURE;
 
   dictFileName.SetLength(dotPos);
   dictFileName.AppendLiteral(".dic");
 
   // SetDictionary can be called multiple times, so we might have a
   // valid mHunspell instance which needs cleaned up.
   delete mHunspell;
 
   mDictionary = aDictionary;
-  mAffixFileName = affFileName;
 
   mHunspell = new Hunspell(affFileName.get(),
                          dictFileName.get());
   if (!mHunspell)
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsCOMPtr<nsICharsetConverterManager> ccm =
     do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
@@ -217,23 +217,16 @@ NS_IMETHODIMP mozHunspell::SetDictionary
   if (pos == -1)
     pos = mDictionary.FindChar('_');
 
   if (pos == -1)
     mLanguage.Assign(mDictionary);
   else
     mLanguage = Substring(mDictionary, 0, pos);
 
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    obs->NotifyObservers(nsnull,
-                         SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION,
-                         nsnull);
-  }
-
   return NS_OK;
 }
 
 /* readonly attribute wstring language; */
 NS_IMETHODIMP mozHunspell::GetLanguage(PRUnichar **aLanguage)
 {
   NS_ENSURE_ARG_POINTER(aLanguage);
 
@@ -335,37 +328,28 @@ NS_IMETHODIMP mozHunspell::GetDictionary
   }
 
   *aDictionaries = ans.dics;
   *aCount = ans.count;
 
   return NS_OK;
 }
 
-static PLDHashOperator
-FindFirstString(const nsAString& aString, nsIFile* aFile, void* aClosure)
-{
-  nsAString *dic = (nsAString*) aClosure;
-  dic->Assign(aString);
-  return PL_DHASH_STOP;
-}
-
 void
 mozHunspell::LoadDictionaryList()
 {
   mDictionaries.Clear();
 
   nsresult rv;
 
   nsCOMPtr<nsIProperties> dirSvc =
     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   if (!dirSvc)
     return;
 
-  // find built in dictionaries
   nsCOMPtr<nsIFile> dictDir;
   rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY,
                    NS_GET_IID(nsIFile), getter_AddRefs(dictDir));
   if (NS_SUCCEEDED(rv)) {
     LoadDictionariesFromDir(dictDir);
   }
   else {
     // try to load gredir/dictionaries
@@ -383,77 +367,31 @@ mozHunspell::LoadDictionaryList()
                      NS_GET_IID(nsIFile), getter_AddRefs(appDir));
     PRBool equals;
     if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) {
       appDir->AppendNative(NS_LITERAL_CSTRING("dictionaries"));
       LoadDictionariesFromDir(appDir);
     }
   }
 
-  // find dictionaries from extensions requiring restart
   nsCOMPtr<nsISimpleEnumerator> dictDirs;
   rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY_LIST,
                    NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dictDirs));
   if (NS_FAILED(rv))
     return;
 
   PRBool hasMore;
   while (NS_SUCCEEDED(dictDirs->HasMoreElements(&hasMore)) && hasMore) {
     nsCOMPtr<nsISupports> elem;
     dictDirs->GetNext(getter_AddRefs(elem));
 
     dictDir = do_QueryInterface(elem);
     if (dictDir)
       LoadDictionariesFromDir(dictDir);
   }
-
-  // find dictionaries from restartless extensions
-  for (PRUint32 i = 0; i < mDynamicDirectories.Count(); i++) {
-    LoadDictionariesFromDir(mDynamicDirectories[i]);
-  }
-
-  // Now we have finished updating the list of dictionaries, update the current
-  // dictionary and any editors which may use it.
-  mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
-
-  // If the current dictionary has gone, try to replace it with another
-  // dictionary of the same language
-  if (!mDictionary.IsEmpty()) {
-    rv = SetDictionary(mDictionary.get());
-    if (NS_SUCCEEDED(rv))
-      return;
-  }
-
-  // If we didn't find a dictionary equal to the current dictionary or we had
-  // no current dictionary, just pick an arbitrary dictionary.
-  nsAutoString firstDictionary;
-  mDictionaries.EnumerateRead(FindFirstString, &firstDictionary);
-  if (!firstDictionary.IsEmpty()) {
-    rv = SetDictionary(firstDictionary.get());
-    if (NS_SUCCEEDED(rv))
-      return;
-  }
-
-  // If there are no dictionaries, set no current dictionary
-  if (!mDictionary.IsEmpty()) {
-    delete mHunspell;
-    mHunspell = nsnull;
-    mDictionary.AssignLiteral("");
-    mAffixFileName.AssignLiteral("");
-    mLanguage.AssignLiteral("");
-    mDecoder = nsnull;
-    mEncoder = nsnull;
-
-    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    if (obs) {
-      obs->NotifyObservers(nsnull,
-                           SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION,
-                           nsnull);
-    }
-  }
 }
 
 NS_IMETHODIMP
 mozHunspell::LoadDictionariesFromDir(nsIFile* aDir)
 {
   nsresult rv;
 
   PRBool check = PR_FALSE;
@@ -599,24 +537,8 @@ mozHunspell::Observe(nsISupports* aSubj,
 {
   NS_ASSERTION(!strcmp(aTopic, "profile-do-change"),
                "Unexpected observer topic");
 
   LoadDictionaryList();
 
   return NS_OK;
 }
-
-/* void addDirectory(in nsIFile dir); */
-NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir)
-{
-  mDynamicDirectories.AppendObject(aDir);
-  LoadDictionaryList();
-  return NS_OK;
-}
-
-/* void removeDirectory(in nsIFile dir); */
-NS_IMETHODIMP mozHunspell::RemoveDirectory(nsIFile *aDir)
-{
-  mDynamicDirectories.RemoveObject(aDir);
-  LoadDictionaryList();
-  return NS_OK;
-}
--- a/extensions/spellcheck/hunspell/src/mozHunspell.h
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.h
@@ -36,17 +36,16 @@
  *                 Varga Daniel
  *                 Chris Halls
  *                 Rene Engelhard
  *                 Bram Moolenaar
  *                 Dafydd Jones
  *                 Harri Pitkanen
  *                 Andras Timar
  *                 Tor Lillqvist
- *                 Jesper Kristensen (mail@jesperkristensen.dk)
  * 
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -60,17 +59,16 @@
 #ifndef mozHunspell_h__
 #define mozHunspell_h__
 
 #include <hunspell.hxx>
 #include "mozISpellCheckingEngine.h"
 #include "mozIPersonalDictionary.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
-#include "nsCOMArray.h"
 #include "nsIObserver.h"
 #include "nsIUnicodeEncoder.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsInterfaceHashtable.h"
 #include "nsWeakReference.h"
 #include "nsCycleCollectionParticipant.h"
 
 #define MOZ_HUNSPELL_CONTRACTID "@mozilla.org/spellchecker/engine;1"
@@ -106,19 +104,15 @@ protected:
   nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary;
   nsCOMPtr<nsIUnicodeEncoder>      mEncoder; 
   nsCOMPtr<nsIUnicodeDecoder>      mDecoder; 
 
   // Hashtable matches dictionary name to .aff file
   nsInterfaceHashtable<nsStringHashKey, nsIFile> mDictionaries;
   nsString  mDictionary;
   nsString  mLanguage;
-  nsCString mAffixFileName;
-
-  // dynamic dirs used to search for dictionaries
-  nsCOMArray<nsIFile> mDynamicDirectories;
 
   Hunspell  *mHunspell;
 
   nsIMemoryReporter* mHunspellReporter;
 };
 
 #endif
--- a/extensions/spellcheck/idl/mozISpellCheckingEngine.idl
+++ b/extensions/spellcheck/idl/mozISpellCheckingEngine.idl
@@ -15,17 +15,16 @@
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is
  * David Einstein.
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Jesper Kristensen <mail@jesperkristensen.dk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -36,41 +35,30 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIFile;
 interface mozIPersonalDictionary;
 
-[scriptable, uuid(8ba643a4-7ddc-4662-b976-7ec123843f10)]
+[scriptable, uuid(6eb307d6-3567-481a-971a-feb666b8ae72)]
 
 /**
  * This interface represents a SpellChecker.
  */
 
 interface mozISpellCheckingEngine : nsISupports {
   /**
    * The name of the current dictionary
-   *
-   * Whenever getDictionaryList is not empty, this attribute contains a value
-   * from that list. Whenever getDictionaryList is empty, this attribute
-   * contains the empty string. Setting this attribute to a value not in
-   * getDictionaryList will throw NS_ERROR_FILE_NOT_FOUND.
-   *
-   * The spellcheck engine will send a notification with
-   * "spellcheck-dictionary-update" as topic when this changes.
    */
   attribute wstring dictionary;
 
   /**
    * The language this spellchecker is using when checking
-   *
-   * The spellcheck engine will send a notification with
-   * "spellcheck-dictionary-update" as topic when this changes.
    */
   readonly attribute wstring language;
 
   /**
    * Does the engine provide its own personal dictionary?
    */
   readonly attribute boolean providesPersonalDictionary;
 
@@ -96,45 +84,26 @@ interface mozISpellCheckingEngine : nsIS
 
   /**
    * Get the list of dictionaries
    */
   void getDictionaryList([array, size_is(count)] out wstring dictionaries, out PRUint32 count);
 
   /**
    * check a word
-   *
-   * The spellcheck engine will send a notification with
-   * "spellcheck-dictionary-update" as topic when this changes.
    */
   boolean check(in wstring word);
 
   /**
    * get a list of suggestions for a misspelled word
-   *
-   * The spellcheck engine will send a notification with
-   * "spellcheck-dictionary-update" as topic when this changes.
    */
   void suggest(in wstring word,[array, size_is(count)] out wstring suggestions, out PRUint32 count);
 
   /**
    * Load dictionaries from the specified dir
    */
   void loadDictionariesFromDir(in nsIFile dir);
-
-  /**
-   * Add dictionaries from a directory to the spell checker
-   */
-  void addDirectory(in nsIFile dir);
-
-  /**
-   * Remove dictionaries from a directory from the spell checker
-   */
-  void removeDirectory(in nsIFile dir);
 };
 
 %{C++
 #define DICTIONARY_SEARCH_DIRECTORY "DictD"
 #define DICTIONARY_SEARCH_DIRECTORY_LIST "DictDL"
-
-#define SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION \
-  "spellcheck-dictionary-update"
 %}
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -595,19 +595,19 @@ nsresult mozInlineSpellChecker::Cleanup(
 //    This function can be called to see if it seems likely that we can enable
 //    spellchecking before actually creating the InlineSpellChecking objects.
 //
 //    The problem is that we can't get the dictionary list without actually
 //    creating a whole bunch of spellchecking objects. This function tries to
 //    do that and caches the result so we don't have to keep allocating those
 //    objects if there are no dictionaries or spellchecking.
 //
-//    Whenever dictionaries are added or removed at runtime, this value must be
-//    updated before an observer notification is sent out about the change, to
-//    avoid editors getting a wrong cached result.
+//    This caching will prevent adding dictionaries at runtime if we start out
+//    with no dictionaries! Installing dictionaries as extensions will require
+//    a restart anyway, so it shouldn't be a problem.
 
 PRBool // static
 mozInlineSpellChecker::CanEnableInlineSpellChecking()
 {
   nsresult rv;
   if (gCanEnableSpellChecking == SpellCheck_Uninitialized) {
     gCanEnableSpellChecking = SpellCheck_NotAvailable;
 
@@ -620,22 +620,16 @@ mozInlineSpellChecker::CanEnableInlineSp
     NS_ENSURE_SUCCESS(rv, PR_FALSE);
 
     if (canSpellCheck)
       gCanEnableSpellChecking = SpellCheck_Available;
   }
   return (gCanEnableSpellChecking == SpellCheck_Available);
 }
 
-void // static
-mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking()
-{
-  gCanEnableSpellChecking = SpellCheck_Uninitialized;
-}
-
 // mozInlineSpellChecker::RegisterEventListeners
 //
 //    The inline spell checker listens to mouse events and keyboard navigation+ //    events.
 
 nsresult
 mozInlineSpellChecker::RegisterEventListeners()
 {
   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
--- a/extensions/spellcheck/src/mozInlineSpellChecker.h
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.h
@@ -224,20 +224,18 @@ private:
 public:
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIEDITACTIONLISTENER
   NS_DECL_NSIINLINESPELLCHECKER
   NS_DECL_NSIDOMEVENTLISTENER
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker, nsIDOMEventListener)
 
-  // returns true if there are any spell checking dictionaries available
+  // returns true if it looks likely that we can enable real-time spell checking
   static PRBool CanEnableInlineSpellChecking();
-  // update the cached value whenever the list of available dictionaries changes
-  static void UpdateCanEnableInlineSpellChecking();
 
   nsresult Blur(nsIDOMEvent* aEvent);
   nsresult MouseClick(nsIDOMEvent* aMouseEvent);
   nsresult KeyPress(nsIDOMEvent* aKeyEvent);
 
   mozInlineSpellChecker();
   virtual ~mozInlineSpellChecker();
 
--- a/extensions/spellcheck/src/mozSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozSpellChecker.cpp
@@ -13,17 +13,16 @@
  *
  * The Original Code is Mozilla Spellchecker Component.
  *
  * The Initial Developer of the Original Code is David Einstein.
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s): David Einstein Deinst@world.std.com
- *   Jesper Kristensen <mail@jesperkristensen.dk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -73,16 +72,19 @@ mozSpellChecker::~mozSpellChecker()
 }
 
 nsresult 
 mozSpellChecker::Init()
 {
   mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
   
   mSpellCheckingEngine = nsnull;
+  mCurrentEngineContractId = nsnull;
+  mDictionariesMap.Init();
+  InitSpellCheckDictionaryMap();
 
   return NS_OK;
 } 
 
 NS_IMETHODIMP 
 mozSpellChecker::SetDocument(nsITextServicesDocument *aDoc, PRBool aFromStartofDoc)
 {
   mTsDoc = aDoc;
@@ -300,55 +302,45 @@ mozSpellChecker::GetPersonalDictionary(n
   nsAutoString word;
   while (NS_SUCCEEDED(words->HasMore(&hasMore)) && hasMore) {
     words->GetNext(word);
     aWordList->AppendElement(word);
   }
   return NS_OK;
 }
 
+struct AppendNewStruct
+{
+  nsTArray<nsString> *dictionaryList;
+  PRBool failed;
+};
+
+static PLDHashOperator
+AppendNewString(const nsAString& aString, nsCString*, void* aClosure)
+{
+  AppendNewStruct *ans = (AppendNewStruct*) aClosure;
+
+  if (!ans->dictionaryList->AppendElement(aString))
+  {
+    ans->failed = PR_TRUE;
+    return PL_DHASH_STOP;
+  }
+
+  return PL_DHASH_NEXT;
+}
+
 NS_IMETHODIMP 
 mozSpellChecker::GetDictionaryList(nsTArray<nsString> *aDictionaryList)
 {
-  nsresult rv;
-
-  // For catching duplicates
-  nsClassHashtable<nsStringHashKey, nsCString> dictionaries;
-  dictionaries.Init();
-
-  nsCOMArray<mozISpellCheckingEngine> spellCheckingEngines;
-  rv = GetEngineList(&spellCheckingEngines);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) {
-    nsCOMPtr<mozISpellCheckingEngine> engine = spellCheckingEngines[i];
+  AppendNewStruct ans = {aDictionaryList, PR_FALSE};
 
-    PRUint32 count = 0;
-    PRUnichar **words = NULL;
-    engine->GetDictionaryList(&words, &count);
-    for (PRUint32 k = 0; k < count; k++) {
-      nsAutoString dictName;
-
-      dictName.Assign(words[k]);
+  mDictionariesMap.EnumerateRead(AppendNewString, &ans);
 
-      // Skip duplicate dictionaries. Only take the first one
-      // for each name.
-      if (dictionaries.Get(dictName, NULL))
-        continue;
-
-      dictionaries.Put(dictName, NULL);
-
-      if (!aDictionaryList->AppendElement(dictName)) {
-        NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words);
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-    }
-
-    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words);
-  }
+  if (ans.failed)
+    return NS_ERROR_OUT_OF_MEMORY;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary)
 {
   nsXPIDLString dictname;
@@ -359,97 +351,54 @@ mozSpellChecker::GetCurrentDictionary(ns
   mSpellCheckingEngine->GetDictionary(getter_Copies(dictname));
   aDictionary = dictname;
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
 {
-  mSpellCheckingEngine = nsnull;
+  nsresult rv;
+  nsCString *contractId;
 
   if (aDictionary.IsEmpty()) {
+    mCurrentEngineContractId = nsnull;
+    mSpellCheckingEngine = nsnull;
     return NS_OK;
   }
 
-  nsresult rv;
-  nsCOMArray<mozISpellCheckingEngine> spellCheckingEngines;
-  rv = GetEngineList(&spellCheckingEngines);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) {
-    nsCOMPtr<mozISpellCheckingEngine> engine = spellCheckingEngines[i];
+  if (!mDictionariesMap.Get(aDictionary, &contractId)){
+    NS_WARNING("Dictionary not found");
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
-    rv = engine->SetDictionary(PromiseFlatString(aDictionary).get());
-    if (NS_SUCCEEDED(rv)) {
-      mSpellCheckingEngine = engine;
+  if (!mCurrentEngineContractId || !mCurrentEngineContractId->Equals(*contractId)){
+    mSpellCheckingEngine = do_GetService(contractId->get(), &rv);
+    if (NS_FAILED(rv))
+      return rv;
 
-      nsCOMPtr<mozIPersonalDictionary> personalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
-      mSpellCheckingEngine->SetPersonalDictionary(personalDictionary.get());
-
-      return NS_OK;
-    }
+    mCurrentEngineContractId = contractId;
   }
 
-  // We could not find any engine with the requested dictionary
-  return NS_ERROR_NOT_AVAILABLE;
-}
-
-NS_IMETHODIMP 
-mozSpellChecker::CheckCurrentDictionary()
-{
-  // Check if the current engine has any dictionaries available. If not,
-  // the last dictionary has just been uninstalled, and we need to stop using
-  // the engine.
-  if (mSpellCheckingEngine) {
-    nsXPIDLString dictname;
-
-    mSpellCheckingEngine->GetDictionary(getter_Copies(dictname));
-
-    // We still have a dictionary, so keep using that.
-    if (!dictname.IsEmpty()) {
-      return NS_OK;
-    }
-
-    // Our current dictionary has gone, so we cannot use the engine anymore.
-    mSpellCheckingEngine = nsnull;
+  nsresult res;
+  res = mSpellCheckingEngine->SetDictionary(PromiseFlatString(aDictionary).get());
+  if(NS_FAILED(res)){
+    NS_WARNING("Dictionary load failed");
+    return res;
   }
 
-  // We have no current engine. Pick one.
-  nsresult rv;
-  nsCOMArray<mozISpellCheckingEngine> spellCheckingEngines;
-  rv = GetEngineList(&spellCheckingEngines);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) {
-    nsCOMPtr<mozISpellCheckingEngine> engine = spellCheckingEngines[i];
-
-    nsXPIDLString dictname;
-
-    engine->GetDictionary(getter_Copies(dictname));
-
-    if (!dictname.IsEmpty()) {
-      mSpellCheckingEngine = engine;
+  mSpellCheckingEngine->SetPersonalDictionary(mPersonalDictionary);
 
-      nsCOMPtr<mozIPersonalDictionary> personalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
-      mSpellCheckingEngine->SetPersonalDictionary(personalDictionary.get());
-
-      nsXPIDLString language;
-      nsresult rv;
-      nsCOMPtr<mozISpellI18NManager> serv = do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &rv);
-      if(serv && NS_SUCCEEDED(rv)) {
-        serv->GetUtil(language.get(), getter_AddRefs(mConverter));
-      }
-
-      return NS_OK;
-    }
+  nsXPIDLString language;
+  
+  nsCOMPtr<mozISpellI18NManager> serv(do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &res));
+  if(serv && NS_SUCCEEDED(res)){
+    res = serv->GetUtil(language.get(),getter_AddRefs(mConverter));
   }
-
-  // There are no dictionaries available
-  return NS_OK;
+  return res;
 }
 
 nsresult
 mozSpellChecker::SetupDoc(PRInt32 *outBlockOffset)
 {
   nsresult  rv;
 
   nsITextServicesDocument::TSDBlockSelectionStatus blockStatus;
@@ -523,20 +472,21 @@ mozSpellChecker::GetCurrentBlockIndex(ns
   } while (NS_SUCCEEDED(result) && !isDone);
   
   *outBlockIndex = blockIndex;
 
   return result;
 }
 
 nsresult
-mozSpellChecker::GetEngineList(nsCOMArray<mozISpellCheckingEngine>* aSpellCheckingEngines)
+mozSpellChecker::InitSpellCheckDictionaryMap()
 {
   nsresult rv;
   PRBool hasMoreEngines;
+  nsTArray<nsCString> contractIds;
 
   nsCOMPtr<nsICategoryManager> catMgr = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
   if (!catMgr)
     return NS_ERROR_NULL_POINTER;
 
   nsCOMPtr<nsISimpleEnumerator> catEntries;
 
   // Get contract IDs of registrated external spell-check engines and
@@ -553,29 +503,57 @@ mozSpellChecker::GetEngineList(nsCOMArra
     if (NS_FAILED(rv))
       return rv;
 
     nsCString contractId;
     rv = entry->GetData(contractId);
     if (NS_FAILED(rv))
       return rv;
 
+    contractIds.AppendElement(contractId);
+  }
+
+  contractIds.AppendElement(NS_LITERAL_CSTRING(DEFAULT_SPELL_CHECKER));
+
+  // Retrieve dictionaries from all available spellcheckers and
+  // fill mDictionariesMap hash (only the first dictionary with the
+  // each name is used).
+  for (PRUint32 i=0;i < contractIds.Length();i++){
+    PRUint32 count,k;
+    PRUnichar **words;
+
+    const nsCString& contractId = contractIds[i];
+
     // Try to load spellchecker engine. Ignore errors silently
     // except for the last one (HunSpell).
     nsCOMPtr<mozISpellCheckingEngine> engine =
       do_GetService(contractId.get(), &rv);
-    if (NS_SUCCEEDED(rv)) {
-      aSpellCheckingEngines->AppendObject(engine);
+    if (NS_FAILED(rv)){
+      // Fail if not succeeded to load HunSpell. Ignore errors
+      // for external spellcheck engines.
+      if (i==contractIds.Length()-1){
+        return rv;
+      }
+
+      continue;
     }
-  }
+
+    engine->GetDictionaryList(&words,&count);
+    for(k=0;k<count;k++){
+      nsAutoString dictName;
 
-  // Try to load HunSpell spellchecker engine.
-  nsCOMPtr<mozISpellCheckingEngine> engine =
-    do_GetService(DEFAULT_SPELL_CHECKER, &rv);
-  if (NS_FAILED(rv)) {
-    // Fail if not succeeded to load HunSpell. Ignore errors
-    // for external spellcheck engines.
-    return rv;
+      dictName.Assign(words[k]);
+
+      nsCString dictCName = NS_ConvertUTF16toUTF8(dictName);
+
+      // Skip duplicate dictionaries. Only take the first one
+      // for each name.
+      if (mDictionariesMap.Get(dictName, NULL))
+        continue;
+
+      mDictionariesMap.Put(dictName, new nsCString(contractId));
+    }
+
+    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words);
   }
-  aSpellCheckingEngines->AppendObject(engine);
 
   return NS_OK;
 }
--- a/extensions/spellcheck/src/mozSpellChecker.h
+++ b/extensions/spellcheck/src/mozSpellChecker.h
@@ -15,17 +15,16 @@
  * The Original Code is Mozilla Spellchecker Component.
  *
  * The Initial Developer of the Original Code is
  * David Einstein.
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s): David Einstein Deinst@world.std.com
- *   Jesper Kristensen <mail@jesperkristensen.dk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -35,17 +34,16 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozSpellChecker_h__
 #define mozSpellChecker_h__
 
 #include "nsCOMPtr.h"
-#include "nsCOMArray.h"
 #include "nsISpellChecker.h"
 #include "nsString.h"
 #include "nsITextServicesDocument.h"
 #include "mozIPersonalDictionary.h"
 #include "mozISpellCheckingEngine.h"
 #include "nsClassHashtable.h"
 #include "nsVoidArray.h"
 #include "nsTArray.h"
@@ -72,25 +70,29 @@ public:
 
   NS_IMETHOD AddWordToPersonalDictionary(const nsAString &aWord);
   NS_IMETHOD RemoveWordFromPersonalDictionary(const nsAString &aWord);
   NS_IMETHOD GetPersonalDictionary(nsTArray<nsString> *aWordList);
 
   NS_IMETHOD GetDictionaryList(nsTArray<nsString> *aDictionaryList);
   NS_IMETHOD GetCurrentDictionary(nsAString &aDictionary);
   NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary);
-  NS_IMETHOD CheckCurrentDictionary();
 
 protected:
   nsCOMPtr<mozISpellI18NUtil> mConverter;
   nsCOMPtr<nsITextServicesDocument> mTsDoc;
   nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary;
 
+  // Hastable maps directory name to the spellchecker contract ID
+  nsClassHashtable<nsStringHashKey, nsCString> mDictionariesMap;
+
+  nsString mDictionaryName;
+  nsCString *mCurrentEngineContractId;
   nsCOMPtr<mozISpellCheckingEngine>  mSpellCheckingEngine;
   PRBool mFromStart;
 
   nsresult SetupDoc(PRInt32 *outBlockOffset);
 
   nsresult GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *outBlockIndex);
 
-  nsresult GetEngineList(nsCOMArray<mozISpellCheckingEngine> *aDictionaryList);
+  nsresult InitSpellCheckDictionaryMap();
 };
 #endif // mozSpellChecker_h__
--- a/extensions/spellcheck/src/mozSpellCheckerFactory.cpp
+++ b/extensions/spellcheck/src/mozSpellCheckerFactory.cpp
@@ -54,17 +54,49 @@ 0x8227F019, 0xAFC7, 0x461e,             
 0x9fe5d975, 0x9bd, 0x44aa,                      \
 { 0xa0, 0x1a, 0x66, 0x40, 0x2e, 0xa2, 0x86, 0x57} }
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozHunspell, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(mozHunspellDirProvider)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozSpellChecker, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozPersonalDictionary, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(mozSpellI18NManager)
-NS_GENERIC_FACTORY_CONSTRUCTOR(mozInlineSpellChecker)
+
+// This special constructor for the inline spell checker asks the inline
+// spell checker if we can create spell checking objects at all (ie, if there
+// are any dictionaries loaded) before trying to create one. The static
+// CanEnableInlineSpellChecking caches the value so this will be faster (we
+// have to run this code for every edit box we create, as well as for every
+// right click in those edit boxes).
+static nsresult
+mozInlineSpellCheckerConstructor(nsISupports *aOuter, REFNSIID aIID,
+                                 void **aResult)
+{
+  if (! mozInlineSpellChecker::CanEnableInlineSpellChecking())
+    return NS_ERROR_FAILURE;
+
+  nsresult rv;
+
+  *aResult = NULL;
+  if (NULL != aOuter) {
+    rv = NS_ERROR_NO_AGGREGATION;
+    return rv;
+  }
+
+  mozInlineSpellChecker* inst = new mozInlineSpellChecker();
+  if (NULL == inst) {
+    rv = NS_ERROR_OUT_OF_MEMORY;
+    return rv;
+  }
+  NS_ADDREF(inst);
+  rv = inst->QueryInterface(aIID, aResult);
+  NS_RELEASE(inst);
+
+  return rv;
+}
 
 NS_DEFINE_NAMED_CID(MOZ_HUNSPELL_CID);
 NS_DEFINE_NAMED_CID(HUNSPELLDIRPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_SPELLCHECKER_CID);
 NS_DEFINE_NAMED_CID(MOZ_PERSONALDICTIONARY_CID);
 NS_DEFINE_NAMED_CID(MOZ_SPELLI18NMANAGER_CID);
 NS_DEFINE_NAMED_CID(MOZ_INLINESPELLCHECKER_CID);
 
deleted file mode 100644
--- a/extensions/spellcheck/tests/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-# http://www.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
-#
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of the GNU General Public License Version 2 or later (the "GPL"),
-# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
-
-DEPTH		= ../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-relativesrcdir  = extensions/spellcheck/tests
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS		= chrome
-
-include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/extensions/spellcheck/tests/chrome/Makefile.in
+++ /dev/null
@@ -1,54 +0,0 @@
-#
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-# http://www.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
-#
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of the GNU General Public License Version 2 or later (the "GPL"),
-# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
-
-DEPTH		= ../../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-relativesrcdir  = extensions/spellcheck/tests/chrome
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS = base map
-
-include $(topsrcdir)/config/rules.mk
-
-_TEST_FILES = 	test_add_remove_dictionaries.xul \
-				$(NULL)
-
-libs:: $(_TEST_FILES)
-	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
deleted file mode 100644
--- a/extensions/spellcheck/tests/chrome/base/Makefile.in
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-# http://www.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
-#
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of the GNU General Public License Version 2 or later (the "GPL"),
-# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
-
-DEPTH		= ../../../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-relativesrcdir  = extensions/spellcheck/tests/chrome/base
-
-include $(DEPTH)/config/autoconf.mk
-include $(topsrcdir)/config/rules.mk
-
-_TEST_FILES = 	base_utf.dic \
-				base_utf.aff \
-				$(NULL)
-
-libs:: $(_TEST_FILES)
-	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
deleted file mode 100644
--- a/extensions/spellcheck/tests/chrome/base/base_utf.aff
+++ /dev/null
@@ -1,198 +0,0 @@
-# OpenOffice.org’s en_US.aff file
-# with Unicode apostrophe: ’
-
-SET UTF-8
-TRY esianrtolcdugmphbyfvkwzESIANRTOLCDUGMPHBYFVKWZ'
-
-MAXNGRAMSUGS 1
-WORDCHARS .'’
-
-PFX A Y 1
-PFX A   0     re         .
-
-PFX I Y 1
-PFX I   0     in         .
-
-PFX U Y 1
-PFX U   0     un         .
-
-PFX C Y 1
-PFX C   0     de          .
-
-PFX E Y 1
-PFX E   0     dis         .
-
-PFX F Y 1
-PFX F   0     con         .
-
-PFX K Y 1
-PFX K   0     pro         .
-
-SFX V N 2
-SFX V   e     ive        e
-SFX V   0     ive        [^e]
-
-SFX N Y 3
-SFX N   e     ion        e
-SFX N   y     ication    y 
-SFX N   0     en         [^ey] 
-
-SFX X Y 3
-SFX X   e     ions       e
-SFX X   y     ications   y
-SFX X   0     ens        [^ey]
-
-SFX H N 2
-SFX H   y     ieth       y
-SFX H   0     th         [^y] 
-
-SFX Y Y 1
-SFX Y   0     ly         .
-
-SFX G Y 2
-SFX G   e     ing        e
-SFX G   0     ing        [^e] 
-
-SFX J Y 2
-SFX J   e     ings       e
-SFX J   0     ings       [^e]
-
-SFX D Y 4
-SFX D   0     d          e
-SFX D   y     ied        [^aeiou]y
-SFX D   0     ed         [^ey]
-SFX D   0     ed         [aeiou]y
-
-SFX T N 4
-SFX T   0     st         e
-SFX T   y     iest       [^aeiou]y
-SFX T   0     est        [aeiou]y
-SFX T   0     est        [^ey]
-
-SFX R Y 4
-SFX R   0     r          e
-SFX R   y     ier        [^aeiou]y
-SFX R   0     er         [aeiou]y
-SFX R   0     er         [^ey]
-
-SFX Z Y 4
-SFX Z   0     rs         e
-SFX Z   y     iers       [^aeiou]y
-SFX Z   0     ers        [aeiou]y
-SFX Z   0     ers        [^ey]
-
-SFX S Y 4
-SFX S   y     ies        [^aeiou]y
-SFX S   0     s          [aeiou]y
-SFX S   0     es         [sxzh]
-SFX S   0     s          [^sxzhy]
-
-SFX P Y 3
-SFX P   y     iness      [^aeiou]y
-SFX P   0     ness       [aeiou]y
-SFX P   0     ness       [^y]
-
-SFX M Y 1
-SFX M   0     's         .
-
-SFX B Y 3
-SFX B   0     able       [^aeiou]
-SFX B   0     able       ee
-SFX B   e     able       [^aeiou]e
-
-SFX L Y 1
-SFX L   0     ment       .
-
-REP 88
-REP a ei
-REP ei a
-REP a ey
-REP ey a
-REP ai ie
-REP ie ai
-REP are air
-REP are ear
-REP are eir
-REP air are
-REP air ere
-REP ere air
-REP ere ear
-REP ere eir
-REP ear are
-REP ear air
-REP ear ere
-REP eir are
-REP eir ere
-REP ch te
-REP te ch
-REP ch ti
-REP ti ch
-REP ch tu
-REP tu ch
-REP ch s
-REP s ch
-REP ch k
-REP k ch
-REP f ph
-REP ph f
-REP gh f
-REP f gh
-REP i igh
-REP igh i
-REP i uy
-REP uy i
-REP i ee
-REP ee i
-REP j di
-REP di j
-REP j gg
-REP gg j
-REP j ge
-REP ge j
-REP s ti
-REP ti s
-REP s ci
-REP ci s
-REP k cc
-REP cc k
-REP k qu
-REP qu k
-REP kw qu
-REP o eau
-REP eau o
-REP o ew
-REP ew o
-REP oo ew
-REP ew oo
-REP ew ui
-REP ui ew
-REP oo ui
-REP ui oo
-REP ew u
-REP u ew
-REP oo u
-REP u oo
-REP u oe
-REP oe u
-REP u ieu
-REP ieu u
-REP ue ew
-REP ew ue
-REP uff ough
-REP oo ieu
-REP ieu oo
-REP ier ear
-REP ear ier
-REP ear air
-REP air ear
-REP w qu
-REP qu w
-REP z ss
-REP ss z
-REP shun tion
-REP shun sion
-REP shun cion
-McDonalds’sá/w
-McDonald’sszá/g3)	st:McDonald’s	po:noun_prs	is:TRANS
-McDonald’sszal/g4)	st:McDonald’s	po:noun_prs	is:INSTR
-McDonald’ssal/w
deleted file mode 100644
--- a/extensions/spellcheck/tests/chrome/base/base_utf.dic
+++ /dev/null
@@ -1,29 +0,0 @@
-28
-created/U
-create/XKVNGADS
-imply/GNSDX
-natural/PUY
-like/USPBY
-convey/BDGS
-look/GZRDS
-text
-hello
-said
-sawyer
-NASA
-rotten
-day
-tomorrow
-seven
-FAQ/SM
-can’t
-doesn’t
-etc
-won’t
-lip
-text
-horrifying
-speech
-suggest
-uncreate/V
-Hunspell
deleted file mode 100644
--- a/extensions/spellcheck/tests/chrome/map/Makefile.in
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-# http://www.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
-#
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of the GNU General Public License Version 2 or later (the "GPL"),
-# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
-
-DEPTH		= ../../../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-relativesrcdir  = extensions/spellcheck/tests/chrome/map
-
-include $(DEPTH)/config/autoconf.mk
-include $(topsrcdir)/config/rules.mk
-
-_TEST_FILES = 	maputf.dic \
-				maputf.aff \
-				$(NULL)
-
-libs:: $(_TEST_FILES)
-	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
deleted file mode 100644
--- a/extensions/spellcheck/tests/chrome/map/maputf.aff
+++ /dev/null
@@ -1,11 +0,0 @@
-# With MAP suggestion, Hunspell can add missing accents to a word.
-
-SET UTF-8
-
-# switch off ngram suggestion for testing
-MAXNGRAMSUGS 0
-
-MAP 3
-MAP uúü
-MAP öóo
-MAP ß(ss)
deleted file mode 100644
--- a/extensions/spellcheck/tests/chrome/map/maputf.dic
+++ /dev/null
@@ -1,4 +0,0 @@
-3
-Frühstück
-tükörfúró
-groß
deleted file mode 100644
--- a/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul
+++ /dev/null
@@ -1,110 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-
-<window title="Add and remove dictionaries test"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        onload="RunTest();">
-  <title>Add and remove dictionaries test</title>
-
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-
-  <script type="application/javascript">
-  <![CDATA[
-SimpleTest.waitForExplicitFinish();
-
-function getMisspelledWords(editor) {
-  return editor.selectionController.getSelection(Components.interfaces.nsISelectionController.SELECTION_SPELLCHECK).toString();
-}
-
-function getDictionaryList(editor) {
-  var spellchecker = editor.getInlineSpellChecker(true).spellChecker;
-  var o1 = {};
-  spellchecker.GetDictionaryList(o1, {});
-  return o1.value;
-}
-
-function getCurrentDictionary(editor) {
-  var spellchecker = editor.getInlineSpellChecker(true).spellChecker;
-  return spellchecker.GetCurrentDictionary();
-}
-
-function setCurrentDictionary(editor, dictionary) {
-  var spellchecker = editor.getInlineSpellChecker(true).spellChecker;
-  spellchecker.SetCurrentDictionary(dictionary);
-}
-
-function RunTest() {
-  var editor = document.getElementById('textbox').editor;
-
-  var dir = Components.classes["@mozilla.org/file/directory_service;1"].
-            getService(Components.interfaces.nsIProperties).
-            get("CurWorkD", Components.interfaces.nsIFile);
-  dir.append("chrome");
-  dir.append("extensions");
-  dir.append("spellcheck");
-  dir.append("tests");
-  dir.append("chrome");
-
-  var hunspell = Components
-    .classes["@mozilla.org/spellchecker/engine;1"]
-    .getService(Components.interfaces.mozISpellCheckingEngine);
-
-  // install base dictionary
-  var base = dir.clone();
-  base.append("base");
-  ok(base.exists());
-  hunspell.addDirectory(base);
-
-  // install map dictionary
-  var map = dir.clone();
-  map.append("map");
-  ok(map.exists());
-  hunspell.addDirectory(map);
-
-  // test that base and map dictionaries are available
-  var dicts = getDictionaryList(editor);
-  isnot(dicts.indexOf("base_utf"), -1, "base is available");
-  isnot(dicts.indexOf("maputf"), -1, "map is available");
-
-  // select base dictionary
-  setCurrentDictionary(editor, "base_utf");
-
-  SimpleTest.executeSoon(function() {
-    // test that base dictionary is in use
-    is(getMisspelledWords(editor), "Frühstück" + "qwertyu", "base misspellings");
-    is(getCurrentDictionary(editor), "base_utf", "current dictionary");
-
-    // select map dictionary
-    setCurrentDictionary(editor, "maputf");
-
-    SimpleTest.executeSoon(function() {
-      // test that map dictionary is in use
-      is(getMisspelledWords(editor), "created" + "imply" + "tomorrow" + "qwertyu", "map misspellings");
-      is(getCurrentDictionary(editor), "maputf", "current dictionary");
-
-      // uninstall map dictionary
-      hunspell.removeDirectory(map);
-
-      SimpleTest.executeSoon(function() {
-        // test that map dictionary is not in use
-        isnot(getMisspelledWords(editor), "created" + "imply" + "tomorrow" + "qwertyu", "map misspellings");
-        isnot(getCurrentDictionary(editor), "maputf", "current dictionary");
-
-        // test that base dictionary is available and map dictionary is unavailable
-        var dicts = getDictionaryList(editor);
-        isnot(dicts.indexOf("base_utf"), -1, "base is available");
-        is(dicts.indexOf("maputf"), -1, "map is unavailable");
-
-        // uninstall base dictionary
-        hunspell.removeDirectory(base);
-
-        SimpleTest.finish();
-      });
-    });
-  });
-}
-  ]]>
-  </script>
-  <textbox id="textbox" spellcheck="true" value="created imply Frühstück tomorrow qwertyu"/>
-</window>
--- a/js/src/assembler/assembler/SparcAssembler.h
+++ b/js/src/assembler/assembler/SparcAssembler.h
@@ -1111,29 +1111,34 @@ namespace JSC {
             ASSERT(from.m_offset != -1);
             void *where = (void *)((intptr_t)code + from.m_offset);
             relinkJump(where, to);
         }
 
         static void relinkCall(void* from, void* to)
         {
             js::JaegerSpew(js::JSpew_Insns,
-                           ISPFX "##linkCall ((from=%p)) ((to=%p))\n",
+                           ISPFX "##relinkCall ((from=%p)) ((to=%p))\n",
                            from, to);
 
-            int disp = ((int)to - (int)from)/4;
-            *(uint32_t *)((int)from) &= 0x40000000;
-            *(uint32_t *)((int)from) |= disp & 0x3fffffff;
-            ExecutableAllocator::cacheFlush(from, 4);
+            void * where= (void *)((intptr_t)from - 20);
+            patchPointerInternal(where, (int)to);
+            ExecutableAllocator::cacheFlush(where, 8);
         }
 
         static void linkCall(void* code, JmpSrc where, void* to)
         {
             void *from = (void *)((intptr_t)code + where.m_offset);
-            relinkCall(from, to);
+            js::JaegerSpew(js::JSpew_Insns,
+                           ISPFX "##linkCall ((from=%p)) ((to=%p))\n",
+                           from, to);
+            int disp = ((int)to - (int)from)/4;
+            *(uint32_t *)((int)from) &= 0x40000000;
+            *(uint32_t *)((int)from) |= disp & 0x3fffffff;
+            ExecutableAllocator::cacheFlush(from, 4);
         }
 
         static void linkPointer(void* code, JmpDst where, void* value)
         {
             js::JaegerSpew(js::JSpew_Insns,
                            ISPFX "##linkPointer     ((%p + %#x)) points to ((%p))\n",
                            code, where.m_offset, value);
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug684348.js
@@ -0,0 +1,4 @@
+var x = Proxy.create({ fix: function() { return []; } });
+Object.__proto__ = x;
+Object.freeze(x);
+quit();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug684084.js
@@ -0,0 +1,7 @@
+// |jit-test| error: TypeError
+function Integer( value, exception ) {
+  try {  } catch ( e ) {  }
+  new (value = this)( this.value );
+  if ( Math.floor(value) != value || isNaN(value) ) {  }
+}
+new Integer( 3, false );
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug684576.js
@@ -0,0 +1,10 @@
+// |jit-test| error: TypeError
+function f0(p0,p1) {
+    var v3;
+    do {
+        p1 > v3
+        v3=1.7
+    } while (((p0[p1][5]==1)||(p0[p1][5]==2)||(p0[p1][5] == 3)) + 0 > p0);
+    + (v3(f0));
+}
+f0(4105,8307);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/closure-01.js
@@ -0,0 +1,18 @@
+
+/* Non-reentrant call on an inner and outer closure. */
+
+function foo() {
+  var x = 0;
+  function bar() {
+    var y = 0;
+    function baz() {
+      return ++x + ++y;
+    }
+    return baz;
+  }
+  return bar();
+}
+
+var a = foo();
+var b = foo();
+assertEq(a() + a() + b() + b(), 12);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/closure-02.js
@@ -0,0 +1,14 @@
+
+/* Non-reentrant closure used in an invoke session. */
+
+var last = null;
+
+var a = [1,2,3,4,5,6,7,8];
+var b = a.map(function(x) {
+    x++;
+    var res = last ? last() : 0;
+    last = function() { return x; };
+    return res;
+  });
+
+assertEq("" + b, "0,2,3,4,5,6,7,8");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/closure-03.js
@@ -0,0 +1,15 @@
+
+/* Recovering non-reentrant information on singletons after a GC. */
+
+function foo(a) {
+  return function() {
+    gc();
+    var n = 0;
+    for (var i = 0; i < 20; i++)
+      n = a++;
+    assertEq(n, 29);
+  };
+}
+var a = foo(10);
+var b = foo(20);
+a();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/closure-04.js
@@ -0,0 +1,23 @@
+test();
+function test() {
+  var catch1, catch2, catch3, finally1, finally2, finally3;
+  function gen() {
+    yield 1;
+    try {
+      try {
+        try {
+          yield 1;
+        } finally {
+          test();
+        }
+      } catch (e) {
+        finally2 = true;
+      }
+    } catch (e) {    }
+  }
+  iter = gen();
+  iter.next();
+  iter.next();
+  iter.close();
+  gc();
+} 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/closure-05.js
@@ -0,0 +1,17 @@
+var gTestcases = new Array();
+var gTc = gTestcases.length;
+function TestCase(n, d, e, a) {
+  gTestcases[gTc++] = this;
+}
+new TestCase("SECTION", "with MyObject, eval should return square of ");
+test();
+function test() {
+  for (gTc = 0; gTc < gTestcases.length; gTc++) {
+    var MYOBJECT = (function isPrototypeOf(message) {
+        delete input;
+      })();
+    with({}) {
+      gTestcases[gTc].actual = eval("");
+    }
+  }
+} 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/loops/bug684621.js
@@ -0,0 +1,15 @@
+function runRichards() {
+    queue = new Packet;
+    Packet(queue, ID_DEVICE_A, KIND_DEVICE);
+    new Packet;
+}
+var ID_DEVICE_A = 4;
+var KIND_DEVICE = 0;
+Packet = function (queue) {
+    this.link = null
+    if (queue == null) return;
+    var peek, next = queue;
+    while ((peek = next.link) != null)
+    ID_HANDLER_B
+};
+runRichards()
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -337,17 +337,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
     for (unsigned i = LOCAL_LIMIT; i < script->nfixed; i++)
         setLocal(i, LOCAL_USE_BEFORE_DEF);
 
     /*
      * If the script is in debug mode, JS_SetFrameReturnValue can be called at
      * any safe point.
      */
     if (cx->compartment->debugMode())
-        usesRval = true;
+        usesReturnValue_ = true;
 
     isInlineable = true;
     if (script->nClosedArgs || script->nClosedVars || script->nfixed >= LOCAL_LIMIT ||
         (script->hasFunction && script->function()->isHeavyweight()) ||
         script->usesEval || script->usesArguments || cx->compartment->debugMode()) {
         isInlineable = false;
     }
 
@@ -493,51 +493,65 @@ ScriptAnalysis::analyzeBytecode(JSContex
 
           case JSOP_RETURN:
           case JSOP_STOP:
             numReturnSites_++;
             break;
 
           case JSOP_SETRVAL:
           case JSOP_POPV:
-            usesRval = true;
+            usesReturnValue_ = true;
             isInlineable = false;
             break;
 
           case JSOP_NAME:
           case JSOP_CALLNAME:
           case JSOP_BINDNAME:
           case JSOP_SETNAME:
           case JSOP_DELNAME:
           case JSOP_QNAMEPART:
           case JSOP_QNAMECONST:
             checkAliasedName(cx, pc);
-            usesScope = true;
+            usesScopeChain_ = true;
             isInlineable = false;
             break;
 
           case JSOP_DEFFUN:
           case JSOP_DEFVAR:
           case JSOP_DEFCONST:
           case JSOP_SETCONST:
             checkAliasedName(cx, pc);
-            /* FALLTHROUGH */
+            extendsScope_ = true;
+            isInlineable = canTrackVars = false;
+            break;
 
-          case JSOP_ENTERWITH:
+          case JSOP_EVAL:
+            extendsScope_ = true;
             isInlineable = canTrackVars = false;
             break;
 
+          case JSOP_ENTERWITH:
+            addsScopeObjects_ = true;
+            isInlineable = canTrackVars = false;
+            break;
+
+          case JSOP_ENTERBLOCK:
+          case JSOP_LEAVEBLOCK:
+            addsScopeObjects_ = true;
+            isInlineable = false;
+            break;
+
           case JSOP_THIS:
-            usesThis = true;
+            usesThisValue_ = true;
             break;
 
           case JSOP_CALL:
           case JSOP_NEW:
             /* Only consider potentially inlineable calls here. */
-            hasCalls = true;
+            hasFunctionCalls_ = true;
             break;
 
           case JSOP_TABLESWITCH:
           case JSOP_TABLESWITCHX: {
             isInlineable = canTrackVars = false;
             jsbytecode *pc2 = pc;
             unsigned jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
@@ -712,30 +726,27 @@ ScriptAnalysis::analyzeBytecode(JSContex
           case JSOP_ARGINC:
           case JSOP_ARGDEC:
             modifiesArguments_ = true;
             isInlineable = false;
             break;
 
           /* Additional opcodes which can be compiled but which can't be inlined. */
           case JSOP_ARGUMENTS:
-          case JSOP_EVAL:
           case JSOP_THROW:
           case JSOP_EXCEPTION:
           case JSOP_DEFLOCALFUN:
           case JSOP_DEFLOCALFUN_FC:
           case JSOP_LAMBDA:
           case JSOP_LAMBDA_FC:
           case JSOP_GETFCSLOT:
           case JSOP_CALLFCSLOT:
           case JSOP_ARGSUB:
           case JSOP_ARGCNT:
           case JSOP_DEBUGGER:
-          case JSOP_ENTERBLOCK:
-          case JSOP_LEAVEBLOCK:
           case JSOP_FUNCALL:
           case JSOP_FUNAPPLY:
             isInlineable = false;
             break;
 
           default:
             break;
         }
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -881,25 +881,28 @@ class ScriptAnalysis
     /* Which analyses have been performed. */
     bool ranBytecode_;
     bool ranSSA_;
     bool ranLifetimes_;
     bool ranInference_;
 
     /* --------- Bytecode analysis --------- */
 
-    bool usesRval;
-    bool usesScope;
-    bool usesThis;
-    bool hasCalls;
-    bool canTrackVars;
-    bool isInlineable;
+    bool usesReturnValue_:1;
+    bool usesScopeChain_:1;
+    bool usesThisValue_:1;
+    bool hasFunctionCalls_:1;
+    bool modifiesArguments_:1;
+    bool extendsScope_:1;
+    bool addsScopeObjects_:1;
+    bool localsAliasStack_:1;
+    bool isInlineable:1;
+    bool canTrackVars:1;
+
     uint32 numReturnSites_;
-    bool modifiesArguments_;
-    bool localsAliasStack_;
 
     /* Offsets at which each local becomes unconditionally defined, or a value below. */
     uint32 *definedLocals;
 
     static const uint32 LOCAL_USE_BEFORE_DEF = uint32(-1);
     static const uint32 LOCAL_CONDITIONALLY_DEFINED = uint32(-2);
 
     /* --------- Lifetime analysis --------- */
@@ -923,32 +926,41 @@ class ScriptAnalysis
     /* Analyze the effect of invoking 'new' on script. */
     void analyzeTypesNew(JSContext *cx);
 
     bool OOM() { return outOfMemory; }
     bool failed() { return hadFailure; }
     bool inlineable(uint32 argc) { return isInlineable && argc == script->function()->nargs; }
 
     /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */
-    bool usesReturnValue() const { return usesRval; }
+    bool usesReturnValue() const { return usesReturnValue_; }
 
     /* Whether there are NAME bytecodes which can access the frame's scope chain. */
-    bool usesScopeChain() const { return usesScope; }
+    bool usesScopeChain() const { return usesScopeChain_; }
 
-    bool usesThisValue() const { return usesThis; }
-    bool hasFunctionCalls() const { return hasCalls; }
+    bool usesThisValue() const { return usesThisValue_; }
+    bool hasFunctionCalls() const { return hasFunctionCalls_; }
     uint32 numReturnSites() const { return numReturnSites_; }
 
     /*
      * True if all named formal arguments are not modified. If the arguments
      * object cannot escape, the arguments are never modified within the script.
      */
     bool modifiesArguments() { return modifiesArguments_; }
 
     /*
+     * True if the script may extend declarations in its top level scope with
+     * dynamic fun/var declarations or through eval.
+     */
+    bool extendsScope() { return extendsScope_; }
+
+    /* True if the script may add block or with objects to its scope chain. */
+    bool addsScopeObjects() { return addsScopeObjects_; }
+
+    /*
      * True if there are any LOCAL opcodes aliasing values on the stack (above
      * script->nfixed).
      */
     bool localsAliasStack() { return localsAliasStack_; }
 
     /* Accessors for bytecode information. */
 
     Bytecode& getCode(uint32 offset) {
@@ -1163,16 +1175,31 @@ class ScriptAnalysis
     bool trackSlot(uint32 slot) { return !slotEscapes(slot) && canTrackVars; }
 
     const LifetimeVariable & liveness(uint32 slot) {
         JS_ASSERT(script->compartment()->activeAnalysis);
         JS_ASSERT(!slotEscapes(slot));
         return lifetimes[slot];
     }
 
+    /*
+     * If a NAME or similar opcode is definitely accessing a particular slot
+     * of a script this one is nested in, get that script/slot.
+     */
+    struct NameAccess {
+        JSScript *script;
+        types::TypeScriptNesting *nesting;
+        uint32 slot;
+
+        /* Decompose the slot above. */
+        bool arg;
+        uint32 index;
+    };
+    NameAccess resolveNameAccess(JSContext *cx, jsid id, bool addDependency = false);
+
     void printSSA(JSContext *cx);
     void printTypes(JSContext *cx);
 
     void clearAllocations();
 
   private:
     void setOOM(JSContext *cx) {
         if (!outOfMemory)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1491,18 +1491,17 @@ class AutoGCRooter {
         NAMESPACES =   -8, /* js::AutoNamespaceArray */
         XML =          -9, /* js::AutoXMLRooter */
         OBJECT =      -10, /* js::AutoObjectRooter */
         ID =          -11, /* js::AutoIdRooter */
         VALVECTOR =   -12, /* js::AutoValueVector */
         DESCRIPTOR =  -13, /* js::AutoPropertyDescriptorRooter */
         STRING =      -14, /* js::AutoStringRooter */
         IDVECTOR =    -15, /* js::AutoIdVector */
-        OBJVECTOR =   -16, /* js::AutoObjectVector */
-        TYPE =        -17  /* js::types::AutoTypeRooter */
+        OBJVECTOR =   -16  /* js::AutoObjectVector */
     };
 
     private:
     /* No copy or assignment semantics. */
     AutoGCRooter(AutoGCRooter &ida);
     void operator=(AutoGCRooter &ida);
 };
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -580,57 +580,71 @@ JSCompartment::sweep(JSContext *cx, uint
                 ScriptTryDestroyCode(cx, script, true, releaseInterval, counter);
                 ScriptTryDestroyCode(cx, script, false, releaseInterval, counter);
             }
         }
     }
 
 #endif
 
-    if (!activeAnalysis) {
+#ifdef JS_METHODJIT
+    if (types.inferenceEnabled)
+        mjit::ClearAllFrames(this);
+#endif
+
+    if (activeAnalysis) {
         /*
-         * Clear the analysis pool, but don't releas its data yet. While
+         * Analysis information is in use, so don't clear the analysis pool.
+         * jitcode still needs to be released, if this is a shape-regenerating
+         * GC then shape numbers baked into the code may change.
+         */
+        if (types.inferenceEnabled) {
+            for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+                JSScript *script = i.get<JSScript>();
+                mjit::ReleaseScriptCode(cx, script);
+            }
+        }
+    } else {
+        /*
+         * Clear the analysis pool, but don't release its data yet. While
          * sweeping types any live data will be allocated into the pool.
          */
         JSArenaPool oldPool;
         MoveArenaPool(&pool, &oldPool);
 
         /*
          * Sweep analysis information and everything depending on it from the
          * compartment, including all remaining mjit code if inference is
          * enabled in the compartment.
          */
         if (types.inferenceEnabled) {
-#ifdef JS_METHODJIT
-            mjit::ClearAllFrames(this);
-#endif
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 if (script->types) {
                     types::TypeScript::Sweep(cx, script);
 
                     /*
                      * On each 1/8 lifetime, release observed types for all scripts.
                      * This is always safe to do when there are no frames for the
                      * compartment on the stack.
                      */
                     if (discardScripts) {
                         script->types->destroy();
                         script->types = NULL;
+                        script->typesPurged = true;
                     }
                 }
             }
         }
 
         types.sweep(cx);
 
         for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
-            if (script->types)
-                script->types->analysis = NULL;
+            script->clearAnalysis();
         }
 
         /* Reset the analysis pool, releasing all analysis and intermediate type data. */
         JS_FinishArenaPool(&oldPool);
     }
 
     active = false;
 }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -766,16 +766,27 @@ Class js::DeclEnvClass = {
 static JSObject *
 NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
 {
     Bindings &bindings = script->bindings;
     size_t argsVars = bindings.countArgsAndVars();
     size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
     gc::AllocKind kind = gc::GetGCObjectKind(slots);
 
+    /*
+     * Make sure that the arguments and variables in the call object all end up
+     * in a contiguous range of slots. We need this to be able to embed the
+     * args/vars arrays in the TypeScriptNesting for the function, after the
+     * call object's frame has finished.
+     */
+    if (cx->typeInferenceEnabled() && gc::GetGCKindSlots(kind) < slots) {
+        kind = gc::GetGCObjectKind(JSObject::CALL_RESERVED_SLOTS);
+        JS_ASSERT(gc::GetGCKindSlots(kind) == JSObject::CALL_RESERVED_SLOTS);
+    }
+
     JSObject *callobj = js_NewGCObject(cx, kind);
     if (!callobj)
         return NULL;
 
     /* Init immediately to avoid GC seeing a half-init'ed object. */
     callobj->initCall(cx, bindings, &scopeChain);
     callobj->makeVarObj();
 
@@ -824,17 +835,17 @@ CreateFunCallObject(JSContext *cx, Stack
     JSObject *scopeChain = &fp->scopeChain();
     JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(),
                  scopeChain->getPrivate() != fp);
 
     /*
      * For a named function expression Call's parent points to an environment
      * object holding function's name.
      */
-    if (JSAtom *lambdaName = (fp->fun()->flags & JSFUN_LAMBDA) ? fp->fun()->atom : NULL) {
+    if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) {
         scopeChain = NewDeclEnvObject(cx, fp);
         if (!scopeChain)
             return NULL;
 
         if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName),
                                   ObjectValue(fp->callee()), NULL, NULL,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
             return NULL;
@@ -941,16 +952,26 @@ js_PutCallObject(StackFrame *fp)
                 }
 
                 nclosed = script->nClosedVars;
                 for (uint32 i = 0; i < nclosed; i++) {
                     uint32 e = script->getClosedVar(i);
                     callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
                 }
             }
+
+            /*
+             * Update the args and vars for the active call if this is an outer
+             * function in a script nesting.
+             */
+            types::TypeScriptNesting *nesting = script->nesting();
+            if (nesting && script->isOuterFunction) {
+                nesting->argArray = callobj.callObjArgArray();
+                nesting->varArray = callobj.callObjVarArray();
+            }
         }
 
         /* Clear private pointers to fp, which is about to go away. */
         if (js_IsNamedLambda(fun)) {
             JSObject *env = callobj.getParent();
 
             JS_ASSERT(env->isDeclEnv());
             JS_ASSERT(env->getPrivate() == fp);
@@ -1023,17 +1044,21 @@ SetCallArg(JSContext *cx, JSObject *obj,
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
     if (StackFrame *fp = obj->maybeCallObjStackFrame())
         fp->formalArg(i) = *vp;
     else
         obj->setCallObjArg(i, *vp);
 
-    JSScript *script = obj->getCallObjCalleeFunction()->script();
+    JSFunction *fun = obj->getCallObjCalleeFunction();
+    JSScript *script = fun->script();
+    if (!script->ensureHasTypes(cx, fun))
+        return false;
+
     TypeScript::SetArgument(cx, script, i, *vp);
 
     return true;
 }
 
 JSBool
 GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
@@ -1090,17 +1115,21 @@ SetCallVar(JSContext *cx, JSObject *obj,
     }
 #endif
 
     if (StackFrame *fp = obj->maybeCallObjStackFrame())
         fp->varSlot(i) = *vp;
     else
         obj->setCallObjVar(i, *vp);
 
-    JSScript *script = obj->getCallObjCalleeFunction()->script();
+    JSFunction *fun = obj->getCallObjCalleeFunction();
+    JSScript *script = fun->script();
+    if (!script->ensureHasTypes(cx, fun))
+        return false;
+
     TypeScript::SetLocal(cx, script, i, *vp);
 
     return true;
 }
 
 } // namespace js
 
 #if JS_TRACER
@@ -2384,19 +2413,18 @@ js_InitFunctionClass(JSContext *cx, JSOb
 
     JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
     if (!script)
         return NULL;
     script->noScriptRval = true;
     script->code[0] = JSOP_STOP;
     script->code[1] = SRC_NULL;
     fun->u.i.script = script;
-    fun->getType(cx)->functionScript = script;
+    fun->getType(cx)->interpretedFunction = fun;
     script->hasFunction = true;
-    script->where.fun = fun;
     script->setOwnerObject(fun);
     js_CallNewScriptHook(cx, script, fun);
 
     if (obj->isGlobal()) {
         /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
         JSFunction *throwTypeError =
             js_NewFunction(cx, NULL, reinterpret_cast<Native>(ThrowTypeError), 0,
                            0, obj, NULL);
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -229,18 +229,18 @@ MarkTypeObject(JSTracer *trc, types::Typ
      * Mark parts of a type object skipped by ScanTypeObject. ScanTypeObject is
      * only used for marking tracers; for tracers with a callback, if we
      * reenter through JS_TraceChildren then MarkChildren will *not* skip these
      * members, and we don't need to handle them here.
      */
     if (IS_GC_MARKING_TRACER(trc)) {
         if (type->singleton)
             MarkObject(trc, *type->singleton, "type_singleton");
-        if (type->functionScript)
-            MarkScript(trc, type->functionScript, "functionScript");
+        if (type->interpretedFunction)
+            MarkObject(trc, *type->interpretedFunction, "type_function");
     }
 }
 
 #if JS_HAS_XML_SUPPORT
 void
 MarkXML(JSTracer *trc, JSXML *xml, const char *name)
 {
     JS_ASSERT(trc);
@@ -410,17 +410,17 @@ MarkKind(JSTracer *trc, void *thing, JSG
         break;
       case JSTRACE_SCRIPT:
         Mark(trc, static_cast<JSScript *>(thing));
         break;
       case JSTRACE_SHAPE:
         Mark(trc, reinterpret_cast<Shape *>(thing));
         break;
       case JSTRACE_TYPE_OBJECT:
-        Mark(trc, reinterpret_cast<types::TypeObject *>(thing));
+        MarkTypeObject(trc, reinterpret_cast<types::TypeObject *>(thing), "type_stack");
         break;
 #if JS_HAS_XML_SUPPORT
       case JSTRACE_XML:
         Mark(trc, static_cast<JSXML *>(thing));
         break;
 #endif
     }
 }
@@ -846,24 +846,25 @@ MarkChildren(JSTracer *trc, JSScript *sc
 
     if (JSScript::isValidOffset(script->constOffset)) {
         JSConstArray *constarray = script->consts();
         MarkValueRange(trc, constarray->length, constarray->vector, "consts");
     }
 
     if (!script->isCachedEval && script->u.object)
         MarkObject(trc, *script->u.object, "object");
-    if (script->hasFunction)
-        MarkObject(trc, *script->function(), "script_fun");
 
     if (IS_GC_MARKING_TRACER(trc) && script->filename)
         js_MarkScriptFilename(script->filename);
 
     script->bindings.trace(trc);
 
+    if (script->types)
+        script->types->trace(trc);
+
 #ifdef JS_METHODJIT
     if (script->jitNormal)
         script->jitNormal->trace(trc);
     if (script->jitCtor)
         script->jitCtor->trace(trc);
 #endif
 }
 
@@ -908,17 +909,17 @@ ScanTypeObject(GCMarker *gcmarker, types
                 PushMarkStack(gcmarker, type->emptyShapes[i]);
         }
     }
 
     if (type->proto)
         PushMarkStack(gcmarker, type->proto);
 
     if (type->newScript) {
-        PushMarkStack(gcmarker, type->newScript->script);
+        PushMarkStack(gcmarker, type->newScript->fun);
         PushMarkStack(gcmarker, type->newScript->shape);
     }
 
     /*
      * Don't need to trace singleton or functionScript, an object with this
      * type must have already been traced and it will also hold a reference
      * on the script (singleton and functionScript types cannot be the newType
      * of another object). Attempts to mark type objects directly must use
@@ -948,22 +949,22 @@ MarkChildren(JSTracer *trc, types::TypeO
 
     if (type->proto)
         MarkObject(trc, *type->proto, "type_proto");
 
     if (type->singleton)
         MarkObject(trc, *type->singleton, "type_singleton");
 
     if (type->newScript) {
-        MarkScript(trc, type->newScript->script, "type_new_script");
+        MarkObject(trc, *type->newScript->fun, "type_new_function");
         MarkShape(trc, type->newScript->shape, "type_new_shape");
     }
 
-    if (type->functionScript)
-        MarkScript(trc, type->functionScript, "functionScript");
+    if (type->interpretedFunction)
+        MarkObject(trc, *type->interpretedFunction, "type_function");
 }
 
 #ifdef JS_HAS_XML_SUPPORT
 void
 MarkChildren(JSTracer *trc, JSXML *xml)
 {
     js_TraceXML(trc, xml);
 }
--- a/js/src/jsgcmark.h
+++ b/js/src/jsgcmark.h
@@ -45,19 +45,16 @@
 #include "jscompartment.h"
 
 #include "jslock.h"
 #include "jstl.h"
 
 namespace js {
 namespace gc {
 
-template<typename T>
-void Mark(JSTracer *trc, T *thing);
-
 void
 MarkString(JSTracer *trc, JSString *str);
 
 void
 MarkString(JSTracer *trc, JSString *str, const char *name);
 
 void
 MarkObject(JSTracer *trc, JSObject &obj, const char *name);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -711,42 +711,52 @@ TypeSet::addPropagateThis(JSContext *cx,
     add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type));
 }
 
 /* Subset constraint which filters out primitive types. */
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
 public:
     TypeSet *target;
-
-    /* Primitive types other than null and undefined are passed through. */
-    bool onlyNullVoid;
-
-    TypeConstraintFilterPrimitive(TypeSet *target, bool onlyNullVoid)
-        : TypeConstraint("filter"), target(target), onlyNullVoid(onlyNullVoid)
+    TypeSet::FilterKind filter;
+
+    TypeConstraintFilterPrimitive(TypeSet *target, TypeSet::FilterKind filter)
+        : TypeConstraint("filter"), target(target), filter(filter)
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
-        if (onlyNullVoid) {
+        switch (filter) {
+          case TypeSet::FILTER_ALL_PRIMITIVES:
+            if (type.isPrimitive())
+                return;
+            break;
+
+          case TypeSet::FILTER_NULL_VOID:
             if (type.isPrimitive(JSVAL_TYPE_NULL) || type.isPrimitive(JSVAL_TYPE_UNDEFINED))
                 return;
-        } else if (type.isPrimitive()) {
-            return;
+            break;
+
+          case TypeSet::FILTER_VOID:
+            if (type.isPrimitive(JSVAL_TYPE_UNDEFINED))
+                return;
+            break;
+
+          default:
+            JS_NOT_REACHED("Bad filter");
         }
 
         target->addType(cx, type);
     }
 };
 
 void
-TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid)
-{
-    add(cx, ArenaNew<TypeConstraintFilterPrimitive>(cx->compartment->pool,
-                                                    target, onlyNullVoid));
+TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter)
+{
+    add(cx, ArenaNew<TypeConstraintFilterPrimitive>(cx->compartment->pool, target, filter));
 }
 
 /* If id is a normal slotful 'own' property of an object, get its shape. */
 static inline const Shape *
 GetSingletonShape(JSObject *obj, jsid id)
 {
     const Shape *shape = obj->nativeLookup(id);
     if (shape && shape->hasDefaultGetterOrIsMethod() && shape->slot != SHAPE_INVALID_SLOT)
@@ -1098,17 +1108,17 @@ TypeConstraintCall::newType(JSContext *c
     jsbytecode *pc = callsite->pc;
 
     if (type.isUnknown() || type.isAnyObject()) {
         /* Monitor calls on unknown functions. */
         cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         return;
     }
 
-    JSScript *callee = NULL;
+    JSFunction *callee = NULL;
 
     if (type.isSingleObject()) {
         JSObject *obj = type.singleObject();
 
         if (!obj->isFunction()) {
             /* Calls on non-functions are dynamically monitored. */
             return;
         }
@@ -1154,69 +1164,67 @@ TypeConstraintCall::newType(JSContext *c
                                        callsite->argumentTypes[i], JSID_VOID);
                     }
                 }
             }
 
             return;
         }
 
-        callee = obj->getFunctionPrivate()->script();
+        callee = obj->getFunctionPrivate();
     } else if (type.isTypeObject()) {
-        callee = type.typeObject()->functionScript;
+        callee = type.typeObject()->interpretedFunction;
         if (!callee)
             return;
     } else {
         /* Calls on non-objects are dynamically monitored. */
         return;
     }
 
-    unsigned nargs = callee->function()->nargs;
-
-    if (!callee->ensureHasTypes(cx))
+    if (!callee->script()->ensureHasTypes(cx, callee))
         return;
 
-    /* Analyze the function if we have not already done so. */
-    if (!callee->ensureRanInference(cx)) {
-        cx->compartment->types.setPendingNukeTypes(cx);
-        return;
-    }
+    unsigned nargs = callee->nargs;
 
     /* Add bindings for the arguments of the call. */
     for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) {
         TypeSet *argTypes = callsite->argumentTypes[i];
-        TypeSet *types = TypeScript::ArgTypes(callee, i);
+        TypeSet *types = TypeScript::ArgTypes(callee->script(), i);
         argTypes->addSubsetBarrier(cx, script, pc, types);
     }
 
     /* Add void type for any formals in the callee not supplied at the call site. */
     for (unsigned i = callsite->argumentCount; i < nargs; i++) {
-        TypeSet *types = TypeScript::ArgTypes(callee, i);
+        TypeSet *types = TypeScript::ArgTypes(callee->script(), i);
         types->addType(cx, Type::UndefinedType());
     }
 
+    TypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
+    TypeSet *returnTypes = TypeScript::ReturnTypes(callee->script());
+
     if (callsite->isNew) {
         /*
          * If the script does not return a value then the pushed value is the
          * new object (typical case). Note that we don't model construction of
          * the new value, which is done dynamically; we don't keep track of the
          * possible 'new' types for a given prototype type object.
          */
-        TypeScript::ThisTypes(callee)->addSubset(cx, callsite->returnTypes);
-        TypeScript::ReturnTypes(callee)->addFilterPrimitives(cx, callsite->returnTypes, false);
+        thisTypes->addSubset(cx, callsite->returnTypes);
+        returnTypes->addFilterPrimitives(cx, callsite->returnTypes,
+                                         TypeSet::FILTER_ALL_PRIMITIVES);
     } else {
         /*
          * Add a binding for the return value of the call. We don't add a
          * binding for the receiver object, as this is done with PropagateThis
          * constraints added by the original JSOP_CALL* op. The type sets we
          * manipulate here have lost any correlations between particular types
          * in the 'this' and 'callee' sets, which we want to maintain for
          * polymorphic JSOP_CALLPROP invocations.
          */
-        TypeScript::ReturnTypes(callee)->addSubset(cx, callsite->returnTypes);
+        returnTypes->addSubset(cx, callsite->returnTypes);
     }
 }
 
 void
 TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type)
 {
     if (type.isUnknown() || type.isAnyObject()) {
         /*
@@ -1225,37 +1233,37 @@ TypeConstraintPropagateThis::newType(JSC
          * CALLPROP and CALLELEM, for other calls we are past the type barrier
          * already and a TypeConstraintCall will also monitor the call.
          */
         cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         return;
     }
 
     /* Ignore calls to natives, these will be handled by TypeConstraintCall. */
-    JSScript *callee = NULL;
+    JSFunction *callee = NULL;
 
     if (type.isSingleObject()) {
         JSObject *object = type.singleObject();
         if (!object->isFunction() || !object->getFunctionPrivate()->isInterpreted())
             return;
-        callee = object->getFunctionPrivate()->script();
+        callee = object->getFunctionPrivate();
     } else if (type.isTypeObject()) {
         TypeObject *object = type.typeObject();
-        if (!object->isFunction() || !object->functionScript)
+        if (!object->interpretedFunction)
             return;
-        callee = object->functionScript;
+        callee = object->interpretedFunction;
     } else {
         /* Ignore calls to primitives, these will go through a stub. */
         return;
     }
 
-    if (!callee->ensureHasTypes(cx))
+    if (!callee->script()->ensureHasTypes(cx, callee))
         return;
 
-    TypeScript::ThisTypes(callee)->addType(cx, this->type);
+    TypeScript::ThisTypes(callee->script())->addType(cx, this->type);
 }
 
 void
 TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type)
 {
     /*
      * We only model a subset of the arithmetic behavior that is actually
      * possible. The following need to be watched for at runtime:
@@ -1618,17 +1626,17 @@ types::MarkArgumentsCreated(JSContext *c
         return;
 
     AutoEnterTypeInference enter(cx);
 
 #ifdef JS_METHODJIT
     mjit::ExpandInlineFrames(cx->compartment);
 #endif
 
-    if (!script->ensureRanBytecode(cx))
+    if (!script->ensureRanAnalysis(cx))
         return;
 
     ScriptAnalysis *analysis = script->analysis();
 
     for (FrameRegsIter iter(cx); !iter.done(); ++iter) {
         StackFrame *fp = iter.fp();
         if (fp->isScriptFrame() && fp->script() == script) {
             /*
@@ -1707,30 +1715,30 @@ public:
         if (source->isOwnProperty(configurable)) {
             updated = true;
             cx->compartment->types.addPendingRecompile(cx, script);
         }
     }
 };
 
 static void
-CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script);
+CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun);
 
 bool
 TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable)
 {
     /*
      * Everywhere compiled code depends on definite properties associated with
      * a type object's newScript, we need to make sure there are constraints
      * in place which will mark those properties as configured should the
      * definite properties be invalidated.
      */
     if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
         if (object->newScript) {
-            CheckNewScriptProperties(cx, object, object->newScript->script);
+            CheckNewScriptProperties(cx, object, object->newScript->fun);
         } else {
             JS_ASSERT(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED);
             object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
         }
     }
 
     if (isOwnProperty(configurable))
         return true;
@@ -1809,16 +1817,74 @@ TypeSet::getSingleton(JSContext *cx, boo
     if (freeze) {
         add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
                                                cx->compartment->types.compiledScript), false);
     }
 
     return obj;
 }
 
+static inline bool
+TypeHasGlobal(Type type, JSObject *global)
+{
+    if (type.isUnknown() || type.isAnyObject())
+        return false;
+
+    if (type.isSingleObject())
+        return type.singleObject()->getGlobal() == global;
+
+    if (type.isTypeObject())
+        return type.typeObject()->getGlobal() == global;
+
+    JS_ASSERT(type.isPrimitive());
+    return true;
+}
+
+class TypeConstraintFreezeGlobal : public TypeConstraint
+{
+public:
+    JSScript *script;
+    JSObject *global;
+
+    TypeConstraintFreezeGlobal(JSScript *script, JSObject *global)
+        : TypeConstraint("freezeGlobal"), script(script), global(global)
+    {
+        JS_ASSERT(global);
+    }
+
+    void newType(JSContext *cx, TypeSet *source, Type type)
+    {
+        if (!global || TypeHasGlobal(type, global))
+            return;
+
+        global = NULL;
+        cx->compartment->types.addPendingRecompile(cx, script);
+    }
+};
+
+bool
+TypeSet::hasGlobalObject(JSContext *cx, JSObject *global)
+{
+    if (unknownObject())
+        return false;
+
+    unsigned count = getObjectCount();
+    for (unsigned i = 0; i < count; i++) {
+        TypeObjectKey *object = getObject(i);
+        if (object && !TypeHasGlobal(Type::ObjectType(object), global))
+            return false;
+    }
+
+    add(cx, ArenaNew<TypeConstraintFreezeGlobal>(cx->compartment->pool,
+                                                 cx->compartment->types.compiledScript,
+                                                 global), false);
+
+    return true;
+}
+
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
 TypeObject types::emptyTypeObject(NULL, false, true);
 
 void
 TypeCompartment::init(JSContext *cx)
@@ -2275,55 +2341,34 @@ ScriptAnalysis::addSingletonTypeBarrier(
     TypeBarrier *barrier =
         ArenaNew<TypeBarrier>(cx->compartment->pool, target, Type::UndefinedType(),
                               singleton, singletonId);
 
     barrier->next = code.typeBarriers;
     code.typeBarriers = barrier;
 }
 
-static void
-PrintScriptTypeCallback(JSContext *cx, void *data, void *thing,
-                        JSGCTraceKind traceKind, size_t thingSize)
-{
-    JS_ASSERT(!data);
-    JS_ASSERT(traceKind == JSTRACE_SCRIPT);
-    JSScript *script = static_cast<JSScript *>(thing);
-    if (script->hasAnalysis() && script->analysis()->ranInference())
-        script->analysis()->printTypes(cx);
-}
-
-#ifdef DEBUG
-static void
-PrintObjectCallback(JSContext *cx, void *data, void *thing,
-                    JSGCTraceKind traceKind, size_t thingSize)
-{
-    JS_ASSERT(traceKind == JSTRACE_OBJECT);
-    TypeObject *object = (TypeObject *) thing;
-    object->print(cx);
-}
-#endif
-
 void
 TypeCompartment::print(JSContext *cx, bool force)
 {
     JSCompartment *compartment = this->compartment();
 
     if (!force && !InferSpewActive(ISpewResult))
         return;
 
-    {
-        AutoUnlockGC unlock(cx->runtime);
-        IterateCells(cx, compartment, gc::FINALIZE_SCRIPT, cx, PrintScriptTypeCallback);
+    for (gc::CellIter i(cx, compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+        JSScript *script = i.get<JSScript>();
+        if (script->hasAnalysis() && script->analysis()->ranInference())
+            script->analysis()->printTypes(cx);
     }
 
 #ifdef DEBUG
-    {
-        AutoUnlockGC unlock(cx->runtime);
-        IterateCells(cx, compartment, gc::FINALIZE_TYPE_OBJECT, NULL, PrintObjectCallback);
+    for (gc::CellIter i(cx, compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
+        TypeObject *object = i.get<TypeObject>();
+        object->print(cx);
     }
 #endif
 
     printf("Counts: ");
     for (unsigned count = 0; count < TYPE_COUNT_LIMIT; count++) {
         if (count)
             printf("/");
         printf("%u", typeCounts[count]);
@@ -2836,19 +2881,24 @@ TypeObject::setFlags(JSContext *cx, Type
     if ((this->flags & flags) == flags)
         return;
 
     AutoEnterTypeInference enter(cx);
 
     if (singleton) {
         /* Make sure flags are consistent with persistent object state. */
         JS_ASSERT_IF(flags & OBJECT_FLAG_CREATED_ARGUMENTS,
-                     (flags & OBJECT_FLAG_UNINLINEABLE) && functionScript->createdArgs);
-        JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE, functionScript->uninlineable);
-        JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, singleton->flags & JSObject::ITERATED);
+                     (flags & OBJECT_FLAG_UNINLINEABLE) &&
+                     interpretedFunction->script()->createdArgs);
+        JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE,
+                     interpretedFunction->script()->uninlineable);
+        JS_ASSERT_IF(flags & OBJECT_FLAG_REENTRANT_FUNCTION,
+                     interpretedFunction->script()->reentrantOuterFunction);
+        JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
+                     singleton->flags & JSObject::ITERATED);
     }
 
     this->flags |= flags;
 
     InferSpew(ISpewOps, "%s: setFlags %u", TypeObjectString(this), flags);
 
     ObjectStateChange(cx, this, false, false);
 }
@@ -2929,17 +2979,17 @@ TypeObject::clearNewScript(JSContext *cx
      * longer correct state of the object once its initialization is completed.
      * We can't really detect the possibility of this statically, but the new
      * script keeps track of where each property is initialized so we can walk
      * the stack and fix up any such objects.
      */
     for (FrameRegsIter iter(cx); !iter.done(); ++iter) {
         StackFrame *fp = iter.fp();
         if (fp->isScriptFrame() && fp->isConstructing() &&
-            fp->script() == newScript->script && fp->thisValue().isObject() &&
+            fp->fun() == newScript->fun && fp->thisValue().isObject() &&
             !fp->thisValue().toObject().hasLazyType() &&
             fp->thisValue().toObject().type() == this) {
             JSObject *obj = &fp->thisValue().toObject();
             jsbytecode *pc = iter.pc();
 
             /* Whether all identified 'new' properties have been initialized. */
             bool finished = false;
 
@@ -3084,16 +3134,106 @@ GetInitializerType(JSContext *cx, JSScri
 
     JSOp op = JSOp(*pc);
     JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT);
 
     bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array));
     return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object);
 }
 
+/*
+ * Detach nesting state for script from its parent, removing it entirely if it
+ * has no children of its own. This happens when walking type information while
+ * initially resolving NAME accesses, thus will not invalidate any compiler
+ * dependencies.
+ */
+static void
+DetachNestingParent(JSScript *script)
+{
+    TypeScriptNesting *nesting = script->nesting();
+
+    if (!nesting || !nesting->parent)
+        return;
+
+    /* Remove from parent's list of children. */
+    JSScript **pscript = &nesting->parent->nesting()->children;
+    while ((*pscript)->nesting() != nesting)
+        pscript = &(*pscript)->nesting()->next;
+    *pscript = nesting->next;
+
+    nesting->parent = NULL;
+
+    /* If this nesting can have no children of its own, destroy it. */
+    if (!script->isOuterFunction)
+        script->clearNesting();
+}
+
+ScriptAnalysis::NameAccess
+ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency)
+{
+    JS_ASSERT(cx->typeInferenceEnabled());
+
+    NameAccess access;
+    PodZero(&access);
+
+    if (!JSID_IS_ATOM(id))
+        return access;
+    JSAtom *atom = JSID_TO_ATOM(id);
+
+    JSScript *script = this->script;
+    while (script->hasFunction && script->nesting()) {
+        if (!script->ensureRanInference(cx))
+            return access;
+
+        /*
+         * Don't resolve names in scripts which use 'let' or 'with'. New names
+         * bound here can mask variables of the script itself.
+         *
+         * Also, don't resolve names in scripts which are generators. Frame
+         * balancing works differently for generators and we do not maintain
+         * active frame counts for such scripts.
+         */
+        if (script->analysis()->addsScopeObjects() ||
+            js_GetOpcode(cx, script, script->code) == JSOP_GENERATOR) {
+            return access;
+        }
+
+        /* Check if the script definitely binds the identifier. */
+        uintN index;
+        BindingKind kind = script->bindings.lookup(cx, atom, &index);
+        if (kind == ARGUMENT || kind == VARIABLE) {
+            TypeObject *obj = script->function()->getType(cx);
+
+            if (addDependency) {
+                /*
+                 * Record the dependency which compiled code has on the outer
+                 * function being non-reentrant.
+                 */
+                if (TypeSet::HasObjectFlags(cx, obj, OBJECT_FLAG_REENTRANT_FUNCTION))
+                    return access;
+            }
+
+            access.script = script;
+            access.nesting = script->nesting();
+            access.slot = (kind == ARGUMENT) ? ArgSlot(index) : LocalSlot(script, index);
+            access.arg = (kind == ARGUMENT);
+            access.index = index;
+            return access;
+        } else if (kind != NONE) {
+            return access;
+        }
+
+        if (!script->nesting()->parent)
+            return access;
+        script = script->nesting()->parent;
+    }
+
+    return access;
+}
+
 /* Analyze type information for a single bytecode. */
 bool
 ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
                                      TypeInferenceState &state)
 {
     jsbytecode *pc = script->code + offset;
     JSOp op = (JSOp)*pc;
 
@@ -3316,17 +3456,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_GETGNAME:
       case JSOP_CALLGNAME: {
         jsid id;
         if (op == JSOP_GETGLOBAL || op == JSOP_CALLGLOBAL)
             id = GetGlobalId(cx, script, pc);
         else
             id = GetAtomId(cx, script, pc, 0);
 
-        TypeSet *seen = script->analysis()->bytecodeTypes(pc);
+        TypeSet *seen = bytecodeTypes(pc);
         seen->addSubset(cx, &pushed[0]);
 
         /*
          * Normally we rely on lazy standard class initialization to fill in
          * the types of global properties the script can access. In a few cases
          * the method JIT will bypass this, and we need to add the types direclty.
          */
         if (id == ATOM_TO_JSID(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]))
@@ -3346,23 +3486,35 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       case JSOP_NAME:
       case JSOP_CALLNAME: {
+        TypeSet *seen = bytecodeTypes(pc);
+        seen->addSubset(cx, &pushed[0]);
+
         /*
-         * The first value pushed by NAME/CALLNAME must always be added to the
-         * bytecode types, we don't model these opcodes with inference.
+         * Try to resolve this name by walking the function's scope nesting.
+         * If we succeed but the accessed script has had its TypeScript purged
+         * in the past, we still must use a type barrier: the name access can
+         * be on a call object which predated the purge, and whose types might
+         * not be reflected in the reconstructed information.
          */
-        TypeSet *seen = script->analysis()->bytecodeTypes(pc);
-        addTypeBarrier(cx, pc, seen, Type::UnknownType());
-        seen->addSubset(cx, &pushed[0]);
+        jsid id = GetAtomId(cx, script, pc, 0);
+        NameAccess access = resolveNameAccess(cx, id);
+        if (access.script && !access.script->typesPurged) {
+            TypeSet *types = TypeScript::SlotTypes(access.script, access.slot);
+            types->addSubsetBarrier(cx, script, pc, seen);
+        } else {
+            addTypeBarrier(cx, pc, seen, Type::UnknownType());
+        }
+
         if (op == JSOP_CALLNAME) {
             pushed[1].addType(cx, Type::UnknownType());
             pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
         }
         break;
       }
 
       case JSOP_BINDGNAME:
@@ -3372,17 +3524,29 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_SETGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
         PropertyAccess(cx, script, pc, script->global()->getType(cx),
                        true, poppedTypes(pc, 0), id);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
-      case JSOP_SETNAME:
+      case JSOP_SETNAME: {
+        jsid id = GetAtomId(cx, script, pc, 0);
+        NameAccess access = resolveNameAccess(cx, id);
+        if (access.script) {
+            TypeSet *types = TypeScript::SlotTypes(access.script, access.slot);
+            poppedTypes(pc, 0)->addSubset(cx, types);
+        } else {
+            cx->compartment->types.monitorBytecode(cx, script, offset);
+        }
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
+        break;
+      }
+
       case JSOP_SETCONST:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_GETXPROP:
       case JSOP_GETFCSLOT:
       case JSOP_CALLFCSLOT: {
@@ -3491,17 +3655,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         TypeSet *seen = script->analysis()->bytecodeTypes(pc);
 
         poppedTypes(pc, 0)->addGetProperty(cx, script, pc, seen, id);
         if (op == JSOP_CALLPROP)
             poppedTypes(pc, 0)->addCallProperty(cx, script, pc, id);
 
         seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLPROP)
-            poppedTypes(pc, 0)->addFilterPrimitives(cx, &pushed[1], true);
+            poppedTypes(pc, 0)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID);
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       /*
        * We only consider ELEM accesses on integers below. Any element access
        * which is accessing a non-integer property must be monitored.
@@ -3512,17 +3676,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         TypeSet *seen = script->analysis()->bytecodeTypes(pc);
 
         poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
         if (op == JSOP_CALLELEM)
             poppedTypes(pc, 1)->addCallProperty(cx, script, pc, JSID_VOID);
 
         seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLELEM)
-            poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], true);
+            poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID);
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       case JSOP_SETELEM:
       case JSOP_SETHOLE:
         poppedTypes(pc, 1)->addSetElement(cx, script, pc, poppedTypes(pc, 2), poppedTypes(pc, 0));
@@ -3910,16 +4074,56 @@ ScriptAnalysis::analyzeTypes(JSContext *
      * all types in the compartment.
      */
     ranInference_ = true;
 
     /* Make sure the initial type set of all local vars includes void. */
     for (unsigned i = 0; i < script->nfixed; i++)
         TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType());
 
+    TypeScriptNesting *nesting = script->hasFunction ? script->nesting() : NULL;
+    if (nesting && nesting->parent) {
+        /*
+         * Check whether NAME accesses can be resolved in parent scopes, and
+         * detach from the parent if so. Even if outdated activations of this
+         * function are live when the parent is called again, we do not need to
+         * consider this reentrance as no state in the parent will be used.
+         */
+        if (!nesting->parent->ensureRanInference(cx))
+            return;
+
+        bool detached = false;
+
+        /* Don't track for leaf scripts which have no free variables. */
+        if (!usesScopeChain() && !script->isOuterFunction) {
+            DetachNestingParent(script);
+            detached = true;
+        }
+
+        /*
+         * If the names bound by the script are extensible (DEFFUN, EVAL, ...),
+         * don't resolve NAME accesses into the parent.
+         */
+        if (!detached && extendsScope()) {
+            DetachNestingParent(script);
+            detached = true;
+        }
+
+        /*
+         * Don't track for parents which add call objects or are generators,
+         * don't resolve NAME accesses into the parent.
+         */
+        if (!detached &&
+            (nesting->parent->analysis()->addsScopeObjects() ||
+             js_GetOpcode(cx, nesting->parent, nesting->parent->code) == JSOP_GENERATOR)) {
+            DetachNestingParent(script);
+            detached = true;
+        }
+    }
+
     TypeInferenceState state(cx);
 
     unsigned offset = 0;
     while (offset < script->length) {
         Bytecode *code = maybeCode(offset);
 
         jsbytecode *pc = script->code + offset;
         UntrapOpcode untrap(cx, script, pc);
@@ -4118,17 +4322,17 @@ public:
             return;
 
         if (source->baseFlags() || source->getObjectCount() > 1)
             object->clearNewScript(cx);
     }
 };
 
 static bool
-AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JSObject **pbaseobj,
+AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSObject **pbaseobj,
                            Vector<TypeNewScript::Initializer> *initializerList)
 {
     /*
      * When invoking 'new' on the specified script, try to find some properties
      * which will definitely be added to the created object before it has a
      * chance to escape and be accessed elsewhere.
      *
      * Returns true if the entire script was analyzed (pbaseobj has been
@@ -4139,24 +4343,28 @@ AnalyzeNewScriptProperties(JSContext *cx
     if (initializerList->length() > 50) {
         /*
          * Bail out on really long initializer lists (far longer than maximum
          * number of properties we can track), we may be recursing.
          */
         return false;
     }
 
-    if (script->hasClearedGlobal())
-        return false;
-
-    if (!script->ensureRanInference(cx)) {
+    JSScript *script = fun->script();
+    JS_ASSERT(!script->isInnerFunction);
+
+    if (!script->ensureRanAnalysis(cx, fun) || !script->ensureRanInference(cx)) {
         *pbaseobj = NULL;
         cx->compartment->types.setPendingNukeTypes(cx);
         return false;
     }
+
+    if (script->hasClearedGlobal())
+        return false;
+
     ScriptAnalysis *analysis = script->analysis();
 
     /*
      * Offset of the last bytecode which popped 'this' and which we have
      * processed. For simplicity, we scan for places where 'this' is pushed
      * and immediately analyze the place where that pushed value is popped.
      * This runs the risk of doing things out of order, if the script looks
      * something like 'this.f  = (this.g = ...)', so we watch and bail out if
@@ -4324,17 +4532,18 @@ AnalyzeNewScriptProperties(JSContext *cx
             /* Need to definitely be calling Function.call on a specific script. */
             JSObject *funcallObj = funcallTypes->getSingleton(cx, false);
             JSObject *scriptObj = scriptTypes->getSingleton(cx, false);
             if (!funcallObj || !scriptObj || !scriptObj->isFunction() ||
                 !scriptObj->getFunctionPrivate()->isInterpreted()) {
                 return false;
             }
 
-            JSScript *functionScript = scriptObj->getFunctionPrivate()->script();
+            JSFunction *function = scriptObj->getFunctionPrivate();
+            JS_ASSERT(!function->script()->isInnerFunction);
 
             /*
              * Generate constraints to clear definite properties from the type
              * should the Function.call or callee itself change in the future.
              */
             analysis->pushedTypes(calleev.pushedOffset(), 0)->add(cx,
                 ArenaNew<TypeConstraintClearDefiniteSingle>(cx->compartment->pool, type));
             analysis->pushedTypes(calleev.pushedOffset(), 1)->add(cx,
@@ -4342,17 +4551,17 @@ AnalyzeNewScriptProperties(JSContext *cx
 
             TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, uses->offset);
             if (!initializerList->append(pushframe)) {
                 cx->compartment->types.setPendingNukeTypes(cx);
                 *pbaseobj = NULL;
                 return false;
             }
 
-            if (!AnalyzeNewScriptProperties(cx, type, functionScript,
+            if (!AnalyzeNewScriptProperties(cx, type, function,
                                             pbaseobj, initializerList)) {
                 return false;
             }
 
             TypeNewScript::Initializer popframe(TypeNewScript::Initializer::FRAME_POP, 0);
             if (!initializerList->append(popframe)) {
                 cx->compartment->types.setPendingNukeTypes(cx);
                 *pbaseobj = NULL;
@@ -4374,31 +4583,31 @@ AnalyzeNewScriptProperties(JSContext *cx
 }
 
 /*
  * Either make the newScript information for type when it is constructed
  * by the specified script, or regenerate the constraints for an existing
  * newScript on the type after they were cleared by a GC.
  */
 static void
-CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script)
-{
-    if (type->unknownProperties())
+CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
+{
+    if (type->unknownProperties() || fun->script()->isInnerFunction)
         return;
 
     /* Strawman object to add properties to and watch for duplicates. */
     JSObject *baseobj = NewBuiltinClassInstance(cx, &ObjectClass, gc::FINALIZE_OBJECT16);
     if (!baseobj) {
         if (type->newScript)
             type->clearNewScript(cx);
         return;
     }
 
     Vector<TypeNewScript::Initializer> initializerList(cx);
-    AnalyzeNewScriptProperties(cx, type, script, &baseobj, &initializerList);
+    AnalyzeNewScriptProperties(cx, type, fun, &baseobj, &initializerList);
     if (!baseobj || baseobj->slotSpan() == 0 || !!(type->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)) {
         if (type->newScript)
             type->clearNewScript(cx);
         return;
     }
 
     /*
      * If the type already has a new script, we are just regenerating the type
@@ -4435,17 +4644,17 @@ CheckNewScriptProperties(JSContext *cx, 
     size_t numBytes = sizeof(TypeNewScript)
                     + (initializerList.length() * sizeof(TypeNewScript::Initializer));
     type->newScript = (TypeNewScript *) cx->calloc_(numBytes);
     if (!type->newScript) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
-    type->newScript->script = script;
+    type->newScript->fun = fun;
     type->newScript->allocKind = kind;
     type->newScript->shape = baseobj->lastProperty();
 
     type->newScript->initializerList = (TypeNewScript::Initializer *)
         ((char *) type->newScript + sizeof(TypeNewScript));
     PodCopy(type->newScript->initializerList, initializerList.begin(), initializerList.length());
 }
 
@@ -4685,27 +4894,35 @@ TypeMonitorCallSlow(JSContext *cx, JSObj
 
 static inline bool
 IsAboutToBeFinalized(JSContext *cx, TypeObjectKey *key)
 {
     /* Mask out the low bit indicating whether this is a type or JS object. */
     return !reinterpret_cast<const gc::Cell *>((jsuword) key & ~1)->isMarked();
 }
 
+inline bool
+ScriptIsAboutToBeFinalized(JSContext *cx, JSScript *script, JSFunction *fun)
+{
+    return script->isCachedEval ||
+        (script->u.object && IsAboutToBeFinalized(cx, script->u.object)) ||
+        (fun && IsAboutToBeFinalized(cx, fun));
+}
+
 void
 TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     AutoEnterTypeInference enter(cx);
 
     UntrapOpcode untrap(cx, script, pc);
 
     /* Directly update associated type sets for applicable bytecodes. */
     if (js_CodeSpec[*pc].format & JOF_TYPESET) {
-        if (!script->ensureRanBytecode(cx)) {
+        if (!script->ensureRanAnalysis(cx)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
         TypeSet *types = script->analysis()->bytecodeTypes(pc);
         if (!types->hasType(type)) {
             InferSpew(ISpewOps, "externalType: monitorResult #%u:%05u: %s",
                       script->id(), pc - script->code, TypeString(type));
             types->addType(cx, type);
@@ -4803,31 +5020,264 @@ TypeMonitorResult(JSContext *cx, JSScrip
     UntrapOpcode untrap(cx, script, pc);
 
     /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
     if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
         return;
 
     AutoEnterTypeInference enter(cx);
 
-    if (!script->ensureRanBytecode(cx)) {
+    if (!script->ensureRanAnalysis(cx)) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     Type type = GetValueType(cx, rval);
     TypeSet *types = script->analysis()->bytecodeTypes(pc);
     if (types->hasType(type))
         return;
 
     InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s",
               script->id(), pc - script->code, TypeString(type));
     types->addType(cx, type);
 }
 
+bool
+TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope)
+{
+    JS_ASSERT(script->types && !script->types->hasScope());
+
+    JSFunction *fun = script->types->function;
+
+    JS_ASSERT(script->hasFunction == (fun != NULL));
+    JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction);
+    JS_ASSERT_IF(!scope, fun && !script->isInnerFunction);
+
+    /*
+     * The scope object must be the initial one for the script, before any call
+     * object has been created in the heavyweight case.
+     */
+    JS_ASSERT_IF(scope && scope->isCall() && !scope->callIsForEval(),
+                 scope->getCallObjCalleeFunction() != fun);
+
+    if (!script->compileAndGo) {
+        script->types->global = NULL;
+        return true;
+    }
+
+    JS_ASSERT_IF(fun && scope, fun->getGlobal() == scope->getGlobal());
+    script->types->global = fun ? fun->getGlobal() : scope->getGlobal();
+
+    if (!cx->typeInferenceEnabled())
+        return true;
+
+    if (!script->isInnerFunction || fun->isNullClosure()) {
+        /*
+         * Outermost functions need nesting information if there are inner
+         * functions directly nested in them.
+         */
+        if (script->isOuterFunction) {
+            script->types->nesting = cx->new_<TypeScriptNesting>();
+            if (!script->types->nesting)
+                return false;
+        }
+        return true;
+    }
+
+    /*
+     * Walk the scope chain to the next call object, which will be the function
+     * the script is nested inside.
+     */
+    while (!scope->isCall())
+        scope = scope->getParent();
+
+    /* The isInnerFunction test ensures there is no intervening strict eval call object. */
+    JS_ASSERT(!scope->callIsForEval());
+
+    /* Don't track non-heavyweight parents, NAME ops won't reach into them. */
+    JSFunction *parentFun = scope->getCallObjCalleeFunction();
+    if (!parentFun || !parentFun->isHeavyweight())
+        return true;
+    JSScript *parent = parentFun->script();
+    JS_ASSERT(parent->isOuterFunction);
+
+    /*
+     * We only need the nesting in the child if it has NAME accesses going
+     * into the parent. We won't know for sure whether this is the case until
+     * analyzing the script's types, which we don't want to do yet. The nesting
+     * info we make here may get pruned if/when we eventually do such analysis.
+     */
+
+    /*
+     * Scopes are set when scripts first execute, and the parent script must
+     * have executed first. It is still possible for the parent script to not
+     * have a scope, however, as we occasionally purge all TypeScripts from the
+     * compartment and there may be inner function objects parented to an
+     * activation of the outer function sticking around. In such cases, treat
+     * the parent's call object as the most recent one, so that it is not
+     * marked as reentrant.
+     */
+    if (!parent->ensureHasTypes(cx, parentFun))
+        return false;
+    if (!parent->types->hasScope()) {
+        if (!SetScope(cx, parent, scope->getParent()))
+            return false;
+        parent->nesting()->activeCall = scope;
+        parent->nesting()->argArray = scope->callObjArgArray();
+        parent->nesting()->varArray = scope->callObjVarArray();
+    }
+
+    JS_ASSERT(!script->types->nesting);
+
+    /* Construct and link nesting information for the two functions. */
+
+    script->types->nesting = cx->new_<TypeScriptNesting>();
+    if (!script->types->nesting)
+        return false;
+
+    script->nesting()->parent = parent;
+    script->nesting()->next = parent->nesting()->children;
+    parent->nesting()->children = script;
+
+    return true;
+}
+
+TypeScriptNesting::~TypeScriptNesting()
+{
+    /*
+     * Unlink from any parent/child. Nesting info on a script does not keep
+     * either the parent or children live during GC.
+     */
+
+    if (parent) {
+        JSScript **pscript = &parent->nesting()->children;
+        while ((*pscript)->nesting() != this)
+            pscript = &(*pscript)->nesting()->next;
+        *pscript = next;
+    }
+
+    while (children) {
+        TypeScriptNesting *child = children->nesting();
+        children = child->next;
+        child->parent = NULL;
+        child->next = NULL;
+    }
+}
+
+bool
+ClearActiveNesting(JSScript *start)
+{
+    /*
+     * Clear active call information for script and any outer functions
+     * inner to it. Return false if an inner function has frames on the stack.
+     */
+
+    /* Traverse children, then parent, avoiding recursion. */
+    JSScript *script = start;
+    bool traverseChildren = true;
+    while (true) {
+        TypeScriptNesting *nesting = script->nesting();
+        if (nesting->children && traverseChildren) {
+            script = nesting->children;
+            continue;
+        }
+        if (nesting->activeFrames)
+            return false;
+        if (script->isOuterFunction) {
+            nesting->activeCall = NULL;
+            nesting->argArray = NULL;
+            nesting->varArray = NULL;
+        }
+        if (script == start)
+            break;
+        if (nesting->next) {
+            script = nesting->next;
+            traverseChildren = true;
+        } else {
+            script = nesting->parent;
+            traverseChildren = false;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * For the specified scope and script with an outer function, check if the
+ * scope represents a reentrant activation on an inner function of the parent
+ * or any of its transitive parents.
+ */
+static void
+CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
+{
+  restart:
+    JSScript *parent = script->nesting()->parent;
+    JS_ASSERT(parent);
+
+    while (!scope->isCall() || scope->getCallObjCalleeFunction()->script() != parent)
+        scope = scope->getParent();
+
+    if (scope != parent->nesting()->activeCall) {
+        parent->reentrantOuterFunction = true;
+        MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION);
+
+        /*
+         * Continue checking parents to see if this is reentrant for them too.
+         * We don't need to check this in for non-reentrant calls on the outer
+         * function: when we entered any outer function to the immediate parent
+         * we cleared the active call for its transitive children, so a
+         * non-reentrant call on a child is also a non-reentrant call on the
+         * parent.
+         */
+        if (parent->nesting()->parent) {
+            scope = scope->getParent();
+            script = parent;
+            goto restart;
+        }
+    }
+}
+
+void
+NestingPrologue(JSContext *cx, StackFrame *fp)
+{
+    JSScript *script = fp->fun()->script();
+    TypeScriptNesting *nesting = script->nesting();
+
+    if (nesting->parent)
+        CheckNestingParent(cx, &fp->scopeChain(), script);
+
+    if (script->isOuterFunction) {
+        /*
+         * Check the stack has no frames for this activation, any of its inner
+         * functions or any of their transitive inner functions.
+         */
+        if (!ClearActiveNesting(script)) {
+            script->reentrantOuterFunction = true;
+            MarkTypeObjectFlags(cx, fp->fun(), OBJECT_FLAG_REENTRANT_FUNCTION);
+        }
+
+        nesting->activeCall = &fp->callObj();
+        nesting->argArray = fp->formalArgs();
+        nesting->varArray = fp->slots();
+    }
+
+    /* Maintain stack frame count for the function. */
+    nesting->activeFrames++;
+}
+
+void
+NestingEpilogue(StackFrame *fp)
+{
+    JSScript *script = fp->fun()->script();
+    TypeScriptNesting *nesting = script->nesting();
+
+    JS_ASSERT(nesting->activeFrames != 0);
+    nesting->activeFrames--;
+}
+
 } } /* namespace js::types */
 
 /////////////////////////////////////////////////////////////////////
 // TypeScript
 /////////////////////////////////////////////////////////////////////
 
 /*
  * Returns true if we don't expect to compute the correct types for some value
@@ -4901,34 +5351,42 @@ IgnorePushed(const jsbytecode *pc, unsig
         return JSOp(pc[JSOP_GETLOCAL_LENGTH]) == JSOP_POP;
 
       default:
         return false;
     }
 }
 
 bool
-JSScript::makeTypes(JSContext *cx)
+JSScript::makeTypes(JSContext *cx, JSFunction *fun)
 {
     JS_ASSERT(!types);
+    JS_ASSERT(hasFunction == (fun != NULL));
 
     if (!cx->typeInferenceEnabled()) {
         types = (TypeScript *) cx->calloc_(sizeof(TypeScript));
-        return types != NULL;
+        if (!types)
+            return false;
+        new(types) TypeScript(fun);
+        return true;
     }
 
     AutoEnterTypeInference enter(cx);
 
-    unsigned count = TypeScript::NumTypeSets(this);
+    /* Open code for NumTypeSets since the types are not filled in yet. */
+    unsigned count = 2 + (fun ? fun->nargs : 0) + nfixed + nTypeSets;
+
     types = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(TypeSet) * count));
     if (!types) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return false;
     }
 
+    new(types) TypeScript(fun);
+
 #ifdef DEBUG
     TypeSet *typeArray = types->typeArray();
     for (unsigned i = 0; i < nTypeSets; i++)
         InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
                   InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
                   i, id());
     TypeSet *returnTypes = TypeScript::ReturnTypes(this);
     InferSpew(ISpewOps, "typeSet: %sT%p%s return #%u",
@@ -4977,32 +5435,33 @@ JSScript::makeAnalysis(JSContext *cx)
 
     return true;
 }
 
 bool
 JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton)
 {
     hasFunction = true;
-    where.fun = fun;
+    if (fun->isHeavyweight())
+        isHeavyweightFunction = true;
 
     if (!cx->typeInferenceEnabled())
         return true;
 
     if (singleton) {
         if (!fun->setSingletonType(cx))
             return false;
     } else {
         TypeObject *type = cx->compartment->types.newTypeObject(cx, this,
                                                                 JSProto_Function, fun->getProto());
         if (!type)
             return false;
 
         fun->setType(type);
-        type->functionScript = this;
+        type->interpretedFunction = fun;
     }
 
     return true;
 }
 
 #ifdef DEBUG
 
 /* static */ void
@@ -5125,21 +5584,24 @@ JSObject::makeLazyType(JSContext *cx)
         return;
     }
 
     /* Fill in the type according to the state of this object. */
 
     type->singleton = this;
 
     if (isFunction() && getFunctionPrivate() && getFunctionPrivate()->isInterpreted()) {
-        type->functionScript = getFunctionPrivate()->script();
-        if (type->functionScript->uninlineable)
+        type->interpretedFunction = getFunctionPrivate();
+        JSScript *script = type->interpretedFunction->script();
+        if (script->createdArgs)
+            type->flags |= OBJECT_FLAG_CREATED_ARGUMENTS;
+        if (script->uninlineable)
             type->flags |= OBJECT_FLAG_UNINLINEABLE;
-        if (type->functionScript->createdArgs)
-            type->flags |= OBJECT_FLAG_CREATED_ARGUMENTS;
+        if (script->reentrantOuterFunction)
+            type->flags |= OBJECT_FLAG_REENTRANT_FUNCTION;
     }
 
     if (flags & ITERATED)
         type->flags |= OBJECT_FLAG_ITERATED;
 
 #if JS_HAS_XML_SUPPORT
     /*
      * XML objects do not have equality hooks but are treated special by EQ/NE
@@ -5163,17 +5625,17 @@ JSObject::makeLazyType(JSContext *cx)
                 |  OBJECT_FLAG_NON_PACKED_ARRAY
                 |  OBJECT_FLAG_NON_TYPED_ARRAY;
 
     type_ = type;
     flags &= ~LAZY_TYPE;
 }
 
 void
-JSObject::makeNewType(JSContext *cx, JSScript *newScript, bool unknown)
+JSObject::makeNewType(JSContext *cx, JSFunction *fun, bool unknown)
 {
     JS_ASSERT(!newType);
 
     TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
                                                             JSProto_Object, this, unknown);
     if (!type)
         return;
 
@@ -5188,18 +5650,18 @@ JSObject::makeNewType(JSContext *cx, JSS
     /*
      * Set the special equality flag for types whose prototype also has the
      * flag set. This is a hack, :XXX: need a real correspondence between
      * types and the possible js::Class of objects with that type.
      */
     if (hasSpecialEquality())
         type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
 
-    if (newScript)
-        CheckNewScriptProperties(cx, type, newScript);
+    if (fun)
+        CheckNewScriptProperties(cx, type, fun);
 
 #if JS_HAS_XML_SUPPORT
     /* Special case for XML object equality, see makeLazyType(). */
     if (isXML() && !type->unknownProperties())
         type->flags |= OBJECT_FLAG_UNKNOWN_MASK;
 #endif
 
     if (clasp->ext.equality)
@@ -5515,41 +5977,44 @@ TypeCompartment::~TypeCompartment()
 TypeScript::Sweep(JSContext *cx, JSScript *script)
 {
     JSCompartment *compartment = script->compartment();
     JS_ASSERT(compartment->types.inferenceEnabled);
 
     unsigned num = NumTypeSets(script);
     TypeSet *typeArray = script->types->typeArray();
 
-    if (IsAboutToBeFinalized(cx, script)) {
-        /* Release all memory associated with the persistent type sets. */
-        for (unsigned i = 0; i < num; i++)
-            typeArray[i].clearObjects();
-    } else {
-        /* Remove constraints and references to dead objects from the persistent type sets. */
-        for (unsigned i = 0; i < num; i++)
-            typeArray[i].sweep(cx, compartment);
-    }
+    /* Remove constraints and references to dead objects from the persistent type sets. */
+    for (unsigned i = 0; i < num; i++)
+        typeArray[i].sweep(cx, compartment);
 
     TypeResult **presult = &script->types->dynamicList;
     while (*presult) {
         TypeResult *result = *presult;
         Type type = result->type;
 
         if (!type.isUnknown() && !type.isAnyObject() && type.isObject() &&
             IsAboutToBeFinalized(cx, type.objectKey())) {
             *presult = result->next;
             cx->delete_(result);
         } else {
             presult = &result->next;
         }
     }
 
     /*
+     * If the script has nesting state with a most recent activation, we do not
+     * need either to mark the call object or clear it if not live. Even with
+     * a dead pointer in the nesting, we can't get a spurious match while
+     * testing for reentrancy: if previous activations are still live, they
+     * cannot alias the most recent one, and future activations will overwrite
+     * activeCall on creation.
+     */
+
+    /*
      * Method JIT code depends on the type inference data which is about to
      * be purged, so purge the jitcode as well.
      */
 #ifdef JS_METHODJIT
     mjit::ReleaseScriptCode(cx, script);
 #endif
 }
 
@@ -5557,16 +6022,19 @@ void
 TypeScript::destroy()
 {
     while (dynamicList) {
         TypeResult *next = dynamicList->next;
         Foreground::delete_(dynamicList);
         dynamicList = next;
     }
 
+    if (nesting)
+        Foreground::delete_(nesting);
+
     Foreground::free_(this);
 }
 
 inline size_t
 TypeSet::dynamicSize()
 {
     /* Get the amount of memory allocated from the analysis pool for this set. */
     uint32 count = baseObjectCount();
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -50,16 +50,17 @@
 #include "jsvalue.h"
 #include "jshashtable.h"
 
 namespace js {
     class CallArgs;
     namespace analyze {
         class ScriptAnalysis;
     }
+    struct GlobalObject;
 }
 
 namespace js {
 namespace types {
 
 /* Forward declarations. */
 class TypeSet;
 struct TypeCallsite;
@@ -306,47 +307,50 @@ enum {
 
     /* Mask/shift for the number of properties in propertySet */
     OBJECT_FLAG_PROPERTY_COUNT_MASK   = 0xfff0,
     OBJECT_FLAG_PROPERTY_COUNT_SHIFT  = 4,
     OBJECT_FLAG_PROPERTY_COUNT_LIMIT  =
         OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT,
 
     /*
-     * Whether any objects this represents are not dense arrays. This also
-     * includes dense arrays whose length property does not fit in an int32.
+     * Some objects are not dense arrays, or are dense arrays whose length
+     * property does not fit in an int32.
      */
-    OBJECT_FLAG_NON_DENSE_ARRAY       = 0x010000,
+    OBJECT_FLAG_NON_DENSE_ARRAY       = 0x0010000,
 
     /* Whether any objects this represents are not packed arrays. */
-    OBJECT_FLAG_NON_PACKED_ARRAY      = 0x020000,
+    OBJECT_FLAG_NON_PACKED_ARRAY      = 0x0020000,
 
     /* Whether any objects this represents are not typed arrays. */
-    OBJECT_FLAG_NON_TYPED_ARRAY       = 0x040000,
+    OBJECT_FLAG_NON_TYPED_ARRAY       = 0x0040000,
 
     /* Whether any represented script has had arguments objects created. */
-    OBJECT_FLAG_CREATED_ARGUMENTS     = 0x080000,
+    OBJECT_FLAG_CREATED_ARGUMENTS     = 0x0080000,
 
     /* Whether any represented script is considered uninlineable. */
-    OBJECT_FLAG_UNINLINEABLE          = 0x100000,
+    OBJECT_FLAG_UNINLINEABLE          = 0x0100000,
 
     /* Whether any objects have an equality hook. */
-    OBJECT_FLAG_SPECIAL_EQUALITY      = 0x200000,
+    OBJECT_FLAG_SPECIAL_EQUALITY      = 0x0200000,
 
     /* Whether any objects have been iterated over. */
-    OBJECT_FLAG_ITERATED              = 0x400000,
+    OBJECT_FLAG_ITERATED              = 0x0400000,
+
+    /* Outer function which has been marked reentrant. */
+    OBJECT_FLAG_REENTRANT_FUNCTION    = 0x0800000,
 
     /* Flags which indicate dynamic properties of represented objects. */
-    OBJECT_FLAG_DYNAMIC_MASK          = 0x7f0000,
+    OBJECT_FLAG_DYNAMIC_MASK          = 0x0ff0000,
 
     /*
      * Whether all properties of this object are considered unknown.
      * If set, all flags in DYNAMIC_MASK will also be set.
      */
-    OBJECT_FLAG_UNKNOWN_PROPERTIES    = 0x800000,
+    OBJECT_FLAG_UNKNOWN_PROPERTIES    = 0x1000000,
 
     /* Mask for objects created with unknown properties. */
     OBJECT_FLAG_UNKNOWN_MASK =
         OBJECT_FLAG_DYNAMIC_MASK
       | OBJECT_FLAG_UNKNOWN_PROPERTIES
       | OBJECT_FLAG_SETS_MARKED_UNKNOWN
 };
 typedef uint32 TypeObjectFlags;
@@ -424,31 +428,37 @@ class TypeSet
     void setDefinite(unsigned slot) {
         JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT));
         flags |= TYPE_FLAG_DEFINITE_PROPERTY | (slot << TYPE_FLAG_DEFINITE_SHIFT);
     }
 
     bool hasPropagatedProperty() { return !!(flags & TYPE_FLAG_PROPAGATED_PROPERTY); }
     void setPropagatedProperty() { flags |= TYPE_FLAG_PROPAGATED_PROPERTY; }
 
+    enum FilterKind {
+        FILTER_ALL_PRIMITIVES,
+        FILTER_NULL_VOID,
+        FILTER_VOID
+    };
+
     /* Add specific kinds of constraints to this set. */
     inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
     void addSubset(JSContext *cx, TypeSet *target);
     void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id);
     void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id);
     void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id);
     void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
                        TypeSet *objectTypes, TypeSet *valueTypes);
     void addCall(JSContext *cx, TypeCallsite *site);
     void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL);
     void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
     void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type);
-    void addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid);
+    void addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter);
     void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
     void addLazyArguments(JSContext *cx, TypeSet *target);
 
     /*
      * Make an type set with the specified debugging name, not embedded in
      * another structure.
      */
     static TypeSet *make(JSContext *cx, const char *name);
@@ -495,16 +505,19 @@ class TypeSet
      * Get the typed array type of all objects in this set. Returns
      * TypedArray::TYPE_MAX if the set contains different array types.
      */
     int getTypedArrayType(JSContext *cx);
 
     /* Get the single value which can appear in this type set, otherwise NULL. */
     JSObject *getSingleton(JSContext *cx, bool freeze = true);
 
+    /* Whether all objects in this set are parented to a particular global. */
+    bool hasGlobalObject(JSContext *cx, JSObject *global);
+
     inline void clearObjects();
 
   private:
     uint32 baseObjectCount() const {
         return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
     }
     inline void setBaseObjectCount(uint32 count);
 };
@@ -639,17 +652,17 @@ struct Property
  * information which could change as the script executes (e.g. a scripted
  * setter is added to a prototype object), and we need to ensure both that the
  * appropriate type constraints are in place when necessary, and that we can
  * remove the definite property information and repair the JS stack if the
  * constraints are violated.
  */
 struct TypeNewScript
 {
-    JSScript *script;
+    JSFunction *fun;
 
     /* Allocation kind to use for newly constructed objects. */
     gc::AllocKind allocKind;
 
     /*
      * Shape to use for newly constructed objects. Reflects all definite
      * properties the object will have.
      */
@@ -771,18 +784,18 @@ struct TypeObject : gc::Cell
      *    or deletion.
      *
      * We establish these by using write barriers on calls to setProperty and
      * defineProperty which are on native properties, and by using the inference
      * analysis to determine the side effects of code which is JIT-compiled.
      */
     Property **propertySet;
 
-    /* If this is an interpreted function, the corresponding script. */
-    JSScript *functionScript;
+    /* If this is an interpreted function, the function object. */
+    JSFunction *interpretedFunction;
 
     inline TypeObject(JSObject *proto, bool isFunction, bool unknown);
 
     bool isFunction() { return !!(flags & OBJECT_FLAG_FUNCTION); }
 
     bool hasAnyFlags(TypeObjectFlags flags) {
         JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
         return !!(this->flags & flags);
@@ -820,16 +833,22 @@ struct TypeObject : gc::Cell
     inline TypeSet *maybeGetProperty(JSContext *cx, jsid id);
 
     inline unsigned getPropertyCount();
     inline Property *getProperty(unsigned i);
 
     /* Set flags on this object which are implied by the specified key. */
     inline void setFlagsFromKey(JSContext *cx, JSProtoKey kind);
 
+    /*
+     * Get the global object which all objects of this type are parented to,
+     * or NULL if there is none known.
+     */
+    inline JSObject *getGlobal();
+
     /* Helpers */
 
     bool addProperty(JSContext *cx, jsid id, Property **pprop);
     bool addDefiniteProperties(JSContext *cx, JSObject *obj);
     bool matchDefiniteProperties(JSObject *obj);
     void addPrototype(JSContext *cx, TypeObject *proto);
     void addPropertyType(JSContext *cx, jsid id, Type type);
     void addPropertyType(JSContext *cx, jsid id, const Value &value);
@@ -897,29 +916,141 @@ struct TypeCallsite
 
     /* Type set receiving the return value of this call. */
     TypeSet *returnTypes;
 
     inline TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
                         bool isNew, unsigned argumentCount);
 };
 
+/*
+ * Information attached to outer and inner function scripts nested in one
+ * another for tracking the reentrance state for outer functions. This state is
+ * used to generate fast accesses to the args and vars of the outer function.
+ *
+ * A function is non-reentrant if, at any point in time, only the most recent
+ * activation (i.e. call object) is live. An activation is live if either the
+ * activation is on the stack, or a transitive inner function parented to the
+ * activation is on the stack.
+ *
+ * Because inner functions can be (and, quite often, are) stored in object
+ * properties and it is difficult to build a fast and robust escape analysis
+ * to cope with such flow, we detect reentrance dynamically. For the outer
+ * function, we keep track of the call object for the most recent activation,
+ * and the number of frames for the function and its inner functions which are
+ * on the stack.
+ *
+ * If the outer function is called while frames associated with a previous
+ * activation are on the stack, the outer function is reentrant. If an inner
+ * function is called whose scope does not match the most recent activation,
+ * the outer function is reentrant.
+ *
+ * The situation gets trickier when there are several levels of nesting.
+ *
+ * function foo() {
+ *   var a;
+ *   function bar() {
+ *     var b;
+ *     function baz() { return a + b; }
+ *   }
+ * }
+ *
+ * At calls to 'baz', we don't want to do the scope check for the activations
+ * of both 'foo' and 'bar', but rather 'bar' only. For this to work, a call to
+ * 'baz' which is a reentrant call on 'foo' must also be a reentrant call on
+ * 'bar'. When 'foo' is called, we clear the most recent call object for 'bar'.
+ */
+struct TypeScriptNesting
+{
+    /*
+     * If this is an inner function, the outer function. If non-NULL, this will
+     * be the immediate nested parent of the script (even if that parent has
+     * been marked reentrant). May be NULL even if the script has a nested
+     * parent, if NAME accesses cannot be tracked into the parent (either the
+     * script extends its scope with eval() etc., or the parent can make new
+     * scope chain objects with 'let' or 'with').
+     */
+    JSScript *parent;
+
+    /* If this is an outer function, list of inner functions. */
+    JSScript *children;
+
+    /* Link for children list of parent. */
+    JSScript *next;
+
+    /* If this is an outer function, the most recent activation. */
+    JSObject *activeCall;
+
+    /*
+     * If this is an outer function, pointers to the most recent activation's
+     * arguments and variables arrays. These could be referring either to stack
+     * values in activeCall's frame (if it has not finished yet) or to the
+     * internal slots of activeCall (if the frame has finished). Pointers to
+     * these fields can be embedded directly in JIT code (though remember to
+     * use 'addDependency == true' when calling resolveNameAccess).
+     */
+    Value *argArray;
+    Value *varArray;
+
+    /* Number of frames for this function on the stack. */
+    uint32 activeFrames;
+
+    TypeScriptNesting() { PodZero(this); }
+    ~TypeScriptNesting();
+};
+
+/* Construct nesting information for script wrt its parent. */
+bool CheckScriptNesting(JSContext *cx, JSScript *script);
+
+/* Track nesting state when calling or finishing an outer/inner function. */
+void NestingPrologue(JSContext *cx, StackFrame *fp);
+void NestingEpilogue(StackFrame *fp);
+
 /* Persistent type information for a script, retained across GCs. */
-struct TypeScript
+class TypeScript
 {
+    friend struct ::JSScript;
+
     /* Analysis information for the script, cleared on each GC. */
     analyze::ScriptAnalysis *analysis;
 
+    /* Function for the script, if it has one. */
+    JSFunction *function;
+
+    /*
+     * Information about the scope in which a script executes. This information
+     * is not set until the script has executed at least once and SetScope
+     * called, before that 'global' will be poisoned per GLOBAL_MISSING_SCOPE.
+     */
+    static const size_t GLOBAL_MISSING_SCOPE = 0x1;
+
+    /* Global object for the script, if compileAndGo. */
+    js::GlobalObject *global;
+
+    /* Nesting state for outer or inner function scripts. */
+    TypeScriptNesting *nesting;
+
+  public:
+
+    /* Dynamic types generated at points within this script. */
+    TypeResult *dynamicList;
+
+    TypeScript(JSFunction *fun) {
+        this->function = fun;
+        this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE;
+    }
+
+    bool hasScope() { return size_t(global) != GLOBAL_MISSING_SCOPE; }
+
     /* Array of type type sets for variables and JOF_TYPESET ops. */
     TypeSet *typeArray() { return (TypeSet *) (jsuword(this) + sizeof(TypeScript)); }
 
     static inline unsigned NumTypeSets(JSScript *script);
 
-    /* Dynamic types generated at points within this script. */
-    TypeResult *dynamicList;
+    static bool SetScope(JSContext *cx, JSScript *script, JSObject *scope);
 
     static inline TypeSet *ReturnTypes(JSScript *script);
     static inline TypeSet *ThisTypes(JSScript *script);
     static inline TypeSet *ArgTypes(JSScript *script, unsigned i);
     static inline TypeSet *LocalTypes(JSScript *script, unsigned i);
 
     /* Follows slot layout in jsanalyze.h, can get this/arg/local type sets. */
     static inline TypeSet *SlotTypes(JSScript *script, unsigned slot);
@@ -961,16 +1092,17 @@ struct TypeScript
     static inline void SetThis(JSContext *cx, JSScript *script, Type type);
     static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value);
     static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type);
     static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value);
 
     static void Sweep(JSContext *cx, JSScript *script);
+    inline void trace(JSTracer *trc);
     void destroy();
 };
 
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,TypeObject*,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
 struct ObjectTableKey;
 struct ObjectTableEntry;
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -37,16 +37,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* Inline members for javascript type inference. */
 
 #include "jsarray.h"
 #include "jsanalyze.h"
 #include "jscompartment.h"
+#include "jsgcmark.h"
 #include "jsinfer.h"
 #include "jsprf.h"
 #include "vm/GlobalObject.h"
 
 #include "vm/Stack-inl.h"
 
 #ifndef jsinferinlines_h___
 #define jsinferinlines_h___
@@ -313,20 +314,26 @@ MarkIteratorUnknown(JSContext *cx)
  * from within the interpreter.
  */
 inline void
 TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
 {
     extern void TypeMonitorCallSlow(JSContext *cx, JSObject *callee,
                                     const CallArgs &args, bool constructing);
 
-    if (cx->typeInferenceEnabled()) {
-        JSObject *callee = &args.callee();
-        if (callee->isFunction() && callee->getFunctionPrivate()->isInterpreted())
-            TypeMonitorCallSlow(cx, callee, args, constructing);
+    JSObject *callee = &args.callee();
+    if (callee->isFunction()) {
+        JSFunction *fun = callee->getFunctionPrivate();
+        if (fun->isInterpreted()) {
+            JSScript *script = fun->script();
+            if (!script->ensureRanAnalysis(cx, fun, callee->getParent()))
+                return;
+            if (cx->typeInferenceEnabled())
+                TypeMonitorCallSlow(cx, callee, args, constructing);
+        }
     }
 }
 
 inline bool
 TrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
 {
     if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties())
         return false;
@@ -503,16 +510,17 @@ TypeScript::StandardType(JSContext *cx, 
     JSObject *proto;
     if (!js_GetClassPrototype(cx, script->global(), key, &proto, NULL))
         return NULL;
     return proto->getNewType(cx);
 }
 
 struct AllocationSiteKey {
     JSScript *script;
+
     uint32 offset : 24;
     JSProtoKey kind : 8;
 
     static const uint32 OFFSET_LIMIT = (1 << 23);
 
     AllocationSiteKey() { PodZero(this); }
 
     typedef AllocationSiteKey Lookup;
@@ -596,46 +604,49 @@ TypeScript::MonitorAssign(JSContext *cx,
             return;
         MarkTypeObjectUnknownProperties(cx, obj->type());
     }
 }
 
 /* static */ inline void
 TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
 {
-    if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx))
+    if (!cx->typeInferenceEnabled())
         return;
+    JS_ASSERT(script->types);
 
     /* Analyze the script regardless if -a was used. */
     bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS);
 
     if (!ThisTypes(script)->hasType(type) || analyze) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setThis #%u: %s",
                   script->id(), TypeString(type));
         ThisTypes(script)->addType(cx, type);
 
-        if (analyze)
+        if (analyze && script->types->hasScope())
             script->ensureRanInference(cx);
     }
 }
 
 /* static */ inline void
 TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
 {
     if (cx->typeInferenceEnabled())
         SetThis(cx, script, GetValueType(cx, value));
 }
 
 /* static */ inline void
 TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type)
 {
-    if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx))
+    if (!cx->typeInferenceEnabled())
         return;
+    JS_ASSERT(script->types);
+
     if (!LocalTypes(script, local)->hasType(type)) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setLocal #%u %u: %s",
                   script->id(), local, TypeString(type));
         LocalTypes(script, local)->addType(cx, type);
     }
 }
@@ -647,18 +658,20 @@ TypeScript::SetLocal(JSContext *cx, JSSc
         Type type = GetValueType(cx, value);
         SetLocal(cx, script, local, type);
     }
 }
 
 /* static */ inline void
 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
 {
-    if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx))
+    if (!cx->typeInferenceEnabled())
         return;
+    JS_ASSERT(script->types);
+
     if (!ArgTypes(script, arg)->hasType(type)) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
                   script->id(), arg, TypeString(type));
         ArgTypes(script, arg)->addType(cx, type);
     }
 }
@@ -667,16 +680,27 @@ TypeScript::SetArgument(JSContext *cx, J
 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
 {
     if (cx->typeInferenceEnabled()) {
         Type type = GetValueType(cx, value);
         SetArgument(cx, script, arg, type);
     }
 }
 
+void
+TypeScript::trace(JSTracer *trc)
+{
+    if (function)
+        gc::MarkObject(trc, *function, "script_fun");
+    if (hasScope() && global)
+        gc::MarkObject(trc, *global, "script_global");
+
+    /* Note: nesting does not keep anything alive. */
+}
+
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
 inline JSCompartment *
 TypeCompartment::compartment()
 {
     return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
@@ -1080,16 +1104,17 @@ TypeCallsite::TypeCallsite(JSContext *cx
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
 {
     PodZero(this);
 
     this->proto = proto;
+
     if (function)
         flags |= OBJECT_FLAG_FUNCTION;
     if (unknown)
         flags |= OBJECT_FLAG_UNKNOWN_MASK;
 
     InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
 }
 
@@ -1211,41 +1236,51 @@ TypeObject::setFlagsFromKey(JSContext *c
               | OBJECT_FLAG_NON_PACKED_ARRAY;
         break;
     }
 
     if (!hasAllFlags(flags))
         setFlags(cx, flags);
 }
 
+inline JSObject *
+TypeObject::getGlobal()
+{
+    if (singleton)
+        return singleton->getGlobal();
+    if (interpretedFunction && interpretedFunction->script()->compileAndGo)
+        return interpretedFunction->getGlobal();
+    return NULL;
+}
+
 } } /* namespace js::types */
 
 inline bool
-JSScript::ensureHasTypes(JSContext *cx)
+JSScript::ensureHasTypes(JSContext *cx, JSFunction *fun)
 {
-    return types || makeTypes(cx);
+    return types || makeTypes(cx, fun);
 }
 
 inline bool
-JSScript::ensureRanBytecode(JSContext *cx)
+JSScript::ensureRanAnalysis(JSContext *cx, JSFunction *fun, JSObject *scope)
 {
-    if (!ensureHasTypes(cx))
+    if (!ensureHasTypes(cx, fun))
         return false;
-    if (!hasAnalysis()) {
-        if (!makeAnalysis(cx))
-            return false;
-    }
+    if (!types->hasScope() && !js::types::TypeScript::SetScope(cx, this, scope))
+        return false;
+    if (!hasAnalysis() && !makeAnalysis(cx))
+        return false;
     JS_ASSERT(analysis()->ranBytecode());
     return true;
 }
 
 inline bool
 JSScript::ensureRanInference(JSContext *cx)
 {
-    if (!ensureRanBytecode(cx))
+    if (!ensureRanAnalysis(cx))
         return false;
     if (!analysis()->ranInference()) {
         js::types::AutoEnterTypeInference enter(cx);
         analysis()->analyzeTypes(cx);
     }
     return !analysis()->OOM();
 }
 
@@ -1258,16 +1293,23 @@ JSScript::hasAnalysis()
 inline js::analyze::ScriptAnalysis *
 JSScript::analysis()
 {
     JS_ASSERT(hasAnalysis());
     return types->analysis;
 }
 
 inline void
+JSScript::clearAnalysis()
+{
+    if (types)
+        types->analysis = NULL;
+}
+
+inline void
 js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32 offset, uint32 which,
                                            js::types::Type type)
 {
     js::types::TypeSet *pushed = pushedTypes(offset, which);
     pushed->addType(cx, type);
 }
 
 #endif // jsinferinlines_h___
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -665,17 +665,17 @@ js::InvokeKernel(JSContext *cx, const Ca
 
     /* Get pointer to new frame/slots, prepare arguments. */
     InvokeFrameGuard ifg;
     if (!cx->stack.pushInvokeFrame(cx, args, initial, &ifg))
         return false;
 
     /* Now that the new frame is rooted, maybe create a call object. */
     StackFrame *fp = ifg.fp();
-    if (fun->isHeavyweight() && !CreateFunCallObject(cx, fp))
+    if (!fp->functionPrologue(cx))
         return false;
 
     /* Run function until JSOP_STOP, JSOP_RETURN or error. */
     JSBool ok;
     {
         AutoPreserveEnumerators preserve(cx);
         ok = RunScript(cx, fun->script(), fp);
     }
@@ -712,17 +712,19 @@ InvokeSessionGuard::start(JSContext *cx,
             break;
         JSObject &callee = calleev.toObject();
         if (callee.getClass() != &FunctionClass)
             break;
         JSFunction *fun = callee.getFunctionPrivate();
         if (fun->isNative())
             break;
         script_ = fun->script();
-        if (fun->isHeavyweight())
+        if (!script_->ensureRanAnalysis(cx, fun, callee.getParent()))
+            return false;
+        if (FunctionNeedsPrologue(cx, fun) || script_->isEmpty())
             break;
 
         /*
          * The frame will remain pushed even when the callee isn't active which
          * will affect the observable current global, so avoid any change.
          */
         if (callee.getGlobal() != GetGlobalForScopeChain(cx))
             break;
@@ -904,23 +906,29 @@ js::ExecuteKernel(JSContext *cx, JSScrip
 
 #if JS_HAS_SHARP_VARS
     if (script->hasSharps && !InitSharpSlots(cx, fp))
         return false;
 #endif
 
     Probes::startExecution(cx, script);
 
+    if (!script->ensureRanAnalysis(cx, NULL, &scopeChain))
+        return false;
+
     TypeScript::SetThis(cx, script, fp->thisValue());
 
     AutoPreserveEnumerators preserve(cx);
     JSBool ok = RunScript(cx, script, fp);
     if (result && ok)
         *result = fp->returnValue();
 
+    if (fp->isStrictEvalFrame())
+        js_PutCallObject(fp);
+
     Probes::stopExecution(cx, script);
 
     return !!ok;
 }
 
 bool
 js::Execute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rval)
 {
@@ -4171,25 +4179,26 @@ BEGIN_CASE(JSOP_FUNAPPLY)
     InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
 
     JSScript *newScript = fun->script();
     if (!cx->stack.pushInlineFrame(cx, regs, args, *callee, fun, newScript, initial))
         goto error;
 
     RESTORE_INTERP_VARS();
 
-    /* Only create call object after frame is rooted. */
-    if (fun->isHeavyweight() && !CreateFunCallObject(cx, regs.fp()))
+    if (!regs.fp()->functionPrologue(cx))
         goto error;
 
     RESET_USE_METHODJIT();
     TRACE_0(EnterFrame);
 
+    bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
+
 #ifdef JS_METHODJIT
-    {
+    if (!newType) {
         /* Try to ensure methods are method JIT'd.  */
         mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL)
                                        ? mjit::CompileRequest_Interpreter
                                        : mjit::CompileRequest_JIT;
         mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, construct, request);
         if (status == mjit::Compile_Error)
             goto error;
         if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) {
@@ -4197,17 +4206,16 @@ BEGIN_CASE(JSOP_FUNAPPLY)
             CHECK_PARTIAL_METHODJIT(status);
             interpReturnOK = (status == mjit::Jaeger_Returned);
             CHECK_INTERRUPT_HANDLER();
             goto jit_return;
         }
     }
 #endif
 
-    bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
     if (!ScriptPrologue(cx, regs.fp(), newType))
         goto error;
 
     CHECK_INTERRUPT_HANDLER();
 
     /* Load first op and dispatch it (safe since JSOP_STOP). */
     op = (JSOp) *regs.pc;
     DO_OP();
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -144,36 +144,55 @@ InvokeSessionGuard::invoke(JSContext *cx
         JS_ASSERT(status == mjit::Compile_Okay);
         jit = script_->getJIT(false);
     }
     void *code;
     if (!(code = jit->invokeEntry))
         return Invoke(cx, args_);
 #endif
 
-    /* Clear any garbage left from the last Invoke. */
     StackFrame *fp = ifg_.fp();
+
+    /*
+     * Clear any activation objects on the frame. Normally the frame should not
+     * have any, but since we leave it on the stack between calls to invoke()
+     * the debugger can start operating on it. See markFunctionEpilogueDone()
+     * calls below. :XXX: this is pretty gross, and slows us down. Can the
+     * debugger be prevented from observing this frame?
+     */
+    fp->functionEpilogue(/* activationOnly = */ true);
+    fp->markFunctionEpilogueDone(/* activationOnly = */ true);
+
     fp->resetCallFrame(script_);
 
     JSBool ok;
     {
         AutoPreserveEnumerators preserve(cx);
         args_.setActive();  /* From js::Invoke(InvokeArgsGuard) overload. */
         Probes::enterJSFun(cx, fp->fun(), script_);
 #ifdef JS_METHODJIT
         ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_, /* partial = */ false);
         cx->regs().pc = stop_;
 #else
         cx->regs().pc = script_->code;
         ok = Interpret(cx, cx->fp());
+
+        /* Interpret does not perform the entry frame's epilogue, unlike EnterMethodJIT. */
+        cx->fp()->functionEpilogue();
 #endif
         Probes::exitJSFun(cx, fp->fun(), script_);
         args_.setInactive();
     }
 
+    /*
+     * Clear activation object flags, for the functionEpilogue() call in the
+     * next invoke().
+     */
+    fp->markFunctionEpilogueDone(/* activationOnly = */ true);
+
     /* Don't clobber callee with rval; rval gets read from fp->rval. */
     return ok;
 }
 
 namespace detail {
 
 template<typename T> class PrimitiveBehavior { };
 
@@ -327,16 +346,30 @@ ValuePropertyBearer(JSContext *cx, const
 
     JSObject *pobj;
     if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
         return NULL;
     return pobj;
 }
 
 inline bool
+FunctionNeedsPrologue(JSContext *cx, JSFunction *fun)
+{
+    /* Heavyweight functions need call objects created. */
+    if (fun->isHeavyweight())
+        return true;
+
+    /* Outer and inner functions need to preserve nesting invariants. */
+    if (cx->typeInferenceEnabled() && fun->script()->nesting())
+        return true;
+
+    return false;
+}
+
+inline bool
 ScriptPrologue(JSContext *cx, StackFrame *fp, bool newType)
 {
     JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(), fp->hasCallObj());
 
     if (fp->isConstructing()) {
         JSObject *obj = js_CreateThisForFunction(cx, &fp->callee(), newType);
         if (!obj)
             return false;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3013,31 +3013,30 @@ CreateThisForFunctionWithType(JSContext 
 
     gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
     return NewObjectWithType(cx, type, parent, kind);
 }
 
 JSObject *
 js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto)
 {
-    JSScript *calleeScript = callee->getFunctionPrivate()->script();
     JSObject *res;
 
     if (proto) {
-        types::TypeObject *type = proto->getNewType(cx, calleeScript);
+        types::TypeObject *type = proto->getNewType(cx, callee->getFunctionPrivate());
         if (!type)
             return NULL;
         res = CreateThisForFunctionWithType(cx, type, callee->getParent());
     } else {
         gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
         res = NewNonFunction<WithProto::Class>(cx, &ObjectClass, proto, callee->getParent(), kind);
     }
 
     if (res && cx->typeInferenceEnabled())
-        TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(res));
+        TypeScript::SetThis(cx, callee->getFunctionPrivate()->script(), types::Type::ObjectType(res));
 
     return res;
 }
 
 JSObject *
 js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType)
 {
     Value protov;
@@ -3735,16 +3734,20 @@ JSObject::TradeGuts(JSContext *cx, JSObj
 
     /*
      * Callers should not try to swap dense arrays or ArrayBuffer objects,
      * these use a different slot representation from other objects.
      */
     JS_ASSERT(!a->isDenseArray() && !b->isDenseArray());
     JS_ASSERT(!a->isArrayBuffer() && !b->isArrayBuffer());
 
+    /* New types for a JSObject need to be stable when trading guts. */
+    TypeObject *newTypeA = a->newType;
+    TypeObject *newTypeB = b->newType;
+
     /* Trade the guts of the objects. */
     const size_t size = a->structSize();
     if (size == b->structSize()) {
         /*
          * If the objects are the same size, then we make no assumptions about
          * whether they have dynamically allocated slots and instead just copy
          * them over wholesale.
          */
@@ -3805,16 +3808,19 @@ JSObject::TradeGuts(JSContext *cx, JSObj
         b->capacity = Max(bfixed, acap);
         b->copySlotRange(0, reserved.avals.begin(), acap);
         b->clearSlotRange(acap, b->capacity - acap);
 
         /* Make sure the destructor for reserved doesn't free the slots. */
         reserved.newaslots = NULL;
         reserved.newbslots = NULL;
     }
+
+    a->newType = newTypeA;
+    b->newType = newTypeB;
 }
 
 /*
  * Use this method with extreme caution. It trades the guts of two objects and updates
  * scope ownership. This operation is not thread-safe, just as fast array to slow array
  * transitions are inherently not thread-safe. Don't perform a swap operation on objects
  * shared across threads or, or bad things will happen. You have been warned.
  */
@@ -4413,16 +4419,24 @@ JSObject::allocSlots(JSContext *cx, size
 
     return true;
 }
 
 bool
 JSObject::growSlots(JSContext *cx, size_t newcap)
 {
     /*
+     * Slots are only allocated for call objects when new properties are
+     * added to them, which can only happen while the call is still on the
+     * stack (and an eval, DEFFUN, etc. happens). We thus do not need to
+     * worry about updating any active outer function args/vars.
+     */
+    JS_ASSERT_IF(isCall(), maybeCallObjStackFrame() != NULL);
+
+    /*
      * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
      * grow, double its capacity, to add N elements in amortized O(N) time.
      *
      * Above this limit, grow by 12.5% each time. Speed is still amortized
      * O(N), with a higher constant factor, and we waste less space.
      */
     static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
     static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
@@ -4478,16 +4492,25 @@ JSObject::growSlots(JSContext *cx, size_
     Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
 
     return true;
 }
 
 void
 JSObject::shrinkSlots(JSContext *cx, size_t newcap)
 {
+    /*
+     * Refuse to shrink slots for call objects. This only happens in a very
+     * obscure situation (deleting names introduced by a direct 'eval') and
+     * allowing the slots pointer to change may require updating pointers in
+     * the function's active args/vars information.
+     */
+    if (isCall())
+        return;
+
     uint32 oldcap = numSlots();
     JS_ASSERT(newcap <= oldcap);
     JS_ASSERT(newcap >= slotSpan());
 
     size_t oldSize = slotsAndStructSize();
 
     if (oldcap <= SLOT_CAPACITY_MIN || !hasSlotsArray()) {
         /*
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -728,16 +728,17 @@ struct JSObject : js::gc::Cell {
     /* Whether this object has any dynamic slots at all. */
     inline bool hasSlotsArray() const;
 
     /* Get the number of dynamic slots required for a given capacity. */
     inline size_t numDynamicSlots(size_t capacity) const;
 
   private:
     inline js::Value* fixedSlots() const;
+    inline bool hasContiguousSlots(size_t start, size_t count) const;
 
   public:
     /* Minimum size for dynamically allocated slots. */
     static const uint32 SLOT_CAPACITY_MIN = 8;
 
     bool allocSlots(JSContext *cx, size_t nslots);
     bool growSlots(JSContext *cx, size_t nslots);
     void shrinkSlots(JSContext *cx, size_t nslots);
@@ -788,22 +789,32 @@ struct JSObject : js::gc::Cell {
     inline bool ensureClassReservedSlots(JSContext *cx);
 
     inline uint32 slotSpan() const;
 
     inline bool containsSlot(uint32 slot) const;
 
     void rollbackProperties(JSContext *cx, uint32 slotSpan);
 
-    js::Value& getSlotRef(uintN slot) {
-        JS_ASSERT(slot < capacity);
+    js::Value *getSlotAddress(uintN slot) {
+        /*
+         * This can be used to get the address of the end of the slots for the
+         * object, which may be necessary when fetching zero-length arrays of
+         * slots (e.g. for callObjVarArray).
+         */
+        JS_ASSERT(slot <= capacity);
         size_t fixed = numFixedSlots();
         if (slot < fixed)
-            return fixedSlots()[slot];
-        return slots[slot - fixed];
+            return fixedSlots() + slot;
+        return slots + (slot - fixed);
+    }
+
+    js::Value &getSlotRef(uintN slot) {
+        JS_ASSERT(slot < capacity);
+        return *getSlotAddress(slot);
     }
 
     inline js::Value &nativeGetSlotRef(uintN slot);
 
     const js::Value &getSlot(uintN slot) const {
         JS_ASSERT(slot < capacity);
         size_t fixed = numFixedSlots();
         if (slot < fixed)
@@ -883,20 +894,20 @@ struct JSObject : js::gc::Cell {
         return type_;
     }
 
     static inline size_t offsetOfType() { return offsetof(JSObject, type_); }
 
     inline void clearType();
     inline void setType(js::types::TypeObject *newType);
 
-    inline js::types::TypeObject *getNewType(JSContext *cx, JSScript *script = NULL,
+    inline js::types::TypeObject *getNewType(JSContext *cx, JSFunction *fun = NULL,
                                              bool markUnknown = false);
   private:
-    void makeNewType(JSContext *cx, JSScript *script, bool markUnknown);
+    void makeNewType(JSContext *cx, JSFunction *fun, bool markUnknown);
   public:
 
     /* Set a new prototype for an object with a singleton type. */
     bool splicePrototype(JSContext *cx, JSObject *proto);
 
     /*
      * For bootstrapping, whether to splice a prototype for Function.prototype
      * or the global object.
@@ -1094,16 +1105,24 @@ struct JSObject : js::gc::Cell {
     inline const js::Value &callObjArg(uintN i) const;
     inline void setCallObjArg(uintN i, const js::Value &v);
 
     /* Returns the variable at the given index. */
     inline const js::Value &callObjVar(uintN i) const;
     inline void setCallObjVar(uintN i, const js::Value &v);
 
     /*
+     * Get the actual arrays of arguments and variables. Only call if type
+     * inference is enabled, where we ensure that call object variables are in
+     * contiguous slots (see NewCallObject).
+     */
+    inline js::Value *callObjArgArray();
+    inline js::Value *callObjVarArray();
+
+    /*
      * Date-specific getters and setters.
      */
 
     static const uint32 JSSLOT_DATE_UTC_TIME = 0;
 
     /*
      * Cached slots holding local properties of the date.
      * These are undefined until the first actual lookup occurs
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -138,17 +138,17 @@ JSObject::getProperty(JSContext *cx, JSO
 {
     js::PropertyIdOp op = getOps()->getProperty;
     if (op) {
         if (!op(cx, this, receiver, id, vp))
             return false;
     } else {
         if (!js_GetProperty(cx, this, receiver, id, vp))
             return false;
-        JS_ASSERT_IF(!hasSingletonType(),
+        JS_ASSERT_IF(!hasSingletonType() && nativeContains(js_CheckForStringIndex(id)),
                      js::types::TypeHasProperty(cx, type(), id, *vp));
     }
     return true;
 }
 
 inline JSBool
 JSObject::getProperty(JSContext *cx, jsid id, js::Value *vp)
 {
@@ -399,16 +399,27 @@ JSObject::setPrimitiveThis(const js::Val
 inline bool
 JSObject::hasSlotsArray() const
 {
     JS_ASSERT_IF(!slots, !isDenseArray());
     JS_ASSERT_IF(slots == fixedSlots(), isDenseArray() || isArrayBuffer());
     return slots && slots != fixedSlots();
 }
 
+inline bool
+JSObject::hasContiguousSlots(size_t start, size_t count) const
+{
+    /*
+     * Check that the range [start, start+count) is either all inline or all
+     * out of line.
+     */
+    JS_ASSERT(start + count <= numSlots());
+    return (start + count <= numFixedSlots()) || (start >= numFixedSlots());
+}
+
 inline size_t
 JSObject::structSize() const
 {
     return (isFunction() && !getPrivate())
            ? sizeof(JSFunction)
            : (sizeof(JSObject) + sizeof(js::Value) * numFixedSlots());
 }
 
@@ -595,16 +606,24 @@ JSObject::callObjArg(uintN i) const
 inline void
 JSObject::setCallObjArg(uintN i, const js::Value &v)
 {
     JS_ASSERT(isCall());
     JS_ASSERT(i < getCallObjCalleeFunction()->nargs);
     setSlot(JSObject::CALL_RESERVED_SLOTS + i, v);
 }
 
+inline js::Value *
+JSObject::callObjArgArray()
+{
+    js::DebugOnly<JSFunction*> fun = getCallObjCalleeFunction();
+    JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS, fun->nargs));
+    return getSlotAddress(JSObject::CALL_RESERVED_SLOTS);
+}
+
 inline const js::Value &
 JSObject::callObjVar(uintN i) const
 {
     JSFunction *fun = getCallObjCalleeFunction();
     JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
     JS_ASSERT(i < fun->script()->bindings.countVars());
     return getSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i);
 }
@@ -613,16 +632,39 @@ inline void
 JSObject::setCallObjVar(uintN i, const js::Value &v)
 {
     JSFunction *fun = getCallObjCalleeFunction();
     JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
     JS_ASSERT(i < fun->script()->bindings.countVars());
     setSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i, v);
 }
 
+inline js::Value *
+JSObject::callObjVarArray()
+{
+    JSFunction *fun = getCallObjCalleeFunction();
+    JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS + fun->nargs,
+                                 fun->script()->bindings.countVars()));
+    return getSlotAddress(JSObject::CALL_RESERVED_SLOTS + fun->nargs);
+}
+
+namespace js {
+
+/*
+ * Any name atom for a function which will be added as a DeclEnv object to the
+ * scope chain above call objects for fun.
+ */
+static inline JSAtom *
+CallObjectLambdaName(JSFunction *fun)
+{
+    return (fun->flags & JSFUN_LAMBDA) ? fun->atom : NULL;
+}
+
+} /* namespace js */
+
 inline const js::Value &
 JSObject::getDateUTCTime() const
 {
     JS_ASSERT(isDate());
     return getFixedSlot(JSSLOT_DATE_UTC_TIME);
 }
 
 inline void 
@@ -847,38 +889,38 @@ inline js::types::TypeObject *
 JSObject::getType(JSContext *cx)
 {
     if (hasLazyType())
         makeLazyType(cx);
     return type_;
 }
 
 inline js::types::TypeObject *
-JSObject::getNewType(JSContext *cx, JSScript *script, bool markUnknown)
+JSObject::getNewType(JSContext *cx, JSFunction *fun, bool markUnknown)
 {
     if (isDenseArray() && !makeDenseArraySlow(cx))
         return NULL;
     if (newType) {
         /*
          * If set, the newType's newScript indicates the script used to create
          * all objects in existence which have this type. If there are objects
          * in existence which are not created by calling 'new' on newScript,
          * we must clear the new script information from the type and will not
          * be able to assume any definite properties for instances of the type.
          * This case is rare, but can happen if, for example, two scripted
          * functions have the same value for their 'prototype' property, or if
          * Object.create is called with a prototype object that is also the
          * 'prototype' property of some scripted function.
          */
-        if (newType->newScript && newType->newScript->script != script)
+        if (newType->newScript && newType->newScript->fun != fun)
             newType->clearNewScript(cx);
         if (markUnknown && cx->typeInferenceEnabled() && !newType->unknownProperties())
             newType->markUnknown(cx);
     } else {
-        makeNewType(cx, script, markUnknown);
+        makeNewType(cx, fun, markUnknown);
     }
     return newType;
 }
 
 inline void
 JSObject::clearType()
 {
     JS_ASSERT(!hasSingletonType());
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -354,19 +354,45 @@ js_DumpScript(JSContext *cx, JSScript *s
     Sprinter sprinter;
     INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
     JSBool ok = js_Disassemble(cx, script, true, &sprinter);
     fprintf(stdout, "%s", sprinter.base);
     JS_ARENA_RELEASE(&cx->tempPool, mark);
     return ok;
 }
 
+static char *
+QuoteString(Sprinter *sp, JSString *str, uint32 quote);
+
 static bool
 ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes)
 {
+    if (JSVAL_IS_STRING(v)) {
+        Sprinter sprinter;
+        void *mark = JS_ARENA_MARK(&cx->tempPool);
+        INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
+        char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"');
+        if (!nbytes)
+            return false;
+        nbytes = JS_sprintf_append(NULL, "%s", nbytes);
+        JS_ARENA_RELEASE(&cx->tempPool, mark);
+        if (!nbytes)
+            return false;
+        bytes->initBytes(nbytes);
+        return true;
+    }
+
+    if (cx->runtime->gcRunning || JS_THREAD_DATA(cx)->noGCOrAllocationCheck) {
+        char *source = JS_sprintf_append(NULL, "<value>");
+        if (!source)
+            return false;
+        bytes->initBytes(source);
+        return true;
+    }
+
     if (!JSVAL_IS_PRIMITIVE(v)) {
         JSObject *obj = JSVAL_TO_OBJECT(v);
         Class *clasp = obj->getClass();
 
         if (clasp == &BlockClass) {
             char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj));
             if (!source)
                 return false;
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1127,19 +1127,16 @@ Compiler::compileScript(JSContext *cx, J
     parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
     script = NULL;
     goto out;
 }
 
 bool
 Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
 {
-    if (!globalScope.defs.length())
-        return true;
-
     JSObject *globalObj = globalScope.globalObj;
 
     /* Define and update global properties. */
     for (size_t i = 0; i < globalScope.defs.length(); i++) {
         GlobalScope::GlobalDef &def = globalScope.defs[i];
 
         /* Names that could be resolved ahead of time can be skipped. */
         if (!def.atom)
@@ -1182,41 +1179,52 @@ Compiler::defineGlobals(JSContext *cx, G
 
     /*
      * Recursively walk through all scripts we just compiled. For each script,
      * go through all global uses. Each global use indexes into globalScope->defs.
      * Use this information to repoint each use to the correct slot in the global
      * object.
      */
     while (worklist.length()) {
-        JSScript *inner = worklist.back();
+        JSScript *outer = worklist.back();
         worklist.popBack();
 
-        if (JSScript::isValidOffset(inner->objectsOffset)) {
-            JSObjectArray *arr = inner->objects();
-            for (size_t i = 0; i < arr->length; i++) {
+        if (JSScript::isValidOffset(outer->objectsOffset)) {
+            JSObjectArray *arr = outer->objects();
+
+            /*
+             * If this is an eval script, don't treat the saved caller function
+             * stored in the first object slot as an inner function.
+             */
+            size_t start = outer->savedCallerFun ? 1 : 0;
+
+            for (size_t i = start; i < arr->length; i++) {
                 JSObject *obj = arr->vector[i];
                 if (!obj->isFunction())
                     continue;
                 JSFunction *fun = obj->getFunctionPrivate();
                 JS_ASSERT(fun->isInterpreted());
                 JSScript *inner = fun->script();
+                if (outer->isHeavyweightFunction) {
+                    outer->isOuterFunction = true;
+                    inner->isInnerFunction = true;
+                }
                 if (!JSScript::isValidOffset(inner->globalsOffset) &&
                     !JSScript::isValidOffset(inner->objectsOffset)) {
                     continue;
                 }
                 if (!worklist.append(inner))
                     return false;
             }
         }
 
-        if (!JSScript::isValidOffset(inner->globalsOffset))
+        if (!JSScript::isValidOffset(outer->globalsOffset))
             continue;
 
-        GlobalSlotArray *globalUses = inner->globals();
+        GlobalSlotArray *globalUses = outer->globals();
         uint32 nGlobalUses = globalUses->length;
         for (uint32 i = 0; i < nGlobalUses; i++) {
             uint32 index = globalUses->vector[i].slot;
             JS_ASSERT(index < globalScope.defs.length());
             globalUses->vector[i].slot = globalScope.defs[index].knownSlot;
         }
     }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1207,25 +1207,16 @@ JSScript::NewScriptFromCG(JSContext *cx,
     if (cg->hasUpvarIndices()) {
         JS_ASSERT(cg->upvarIndices->count() <= cg->upvarMap.length());
         memcpy(script->upvars()->vector, cg->upvarMap.begin(),
                cg->upvarIndices->count() * sizeof(cg->upvarMap[0]));
         cg->upvarIndices->clear();
         cg->upvarMap.clear();
     }
 
-    /* Set global for compileAndGo scripts. */
-    if (script->compileAndGo) {
-        GlobalScope *globalScope = cg->compiler()->globalScope;
-        if (globalScope->globalObj && globalScope->globalObj->isGlobal())
-            script->where.global = globalScope->globalObj->asGlobal();
-        else if (cx->globalObject->isGlobal())
-            script->where.global = cx->globalObject->asGlobal();
-    }
-
     if (cg->globalUses.length()) {
         memcpy(script->globals()->vector, &cg->globalUses[0],
                cg->globalUses.length() * sizeof(GlobalSlotArray::Entry));
     }
 
     if (script->nClosedArgs)
         memcpy(script->closedSlots, &cg->closedArgs[0], script->nClosedArgs * sizeof(uint32));
     if (script->nClosedVars) {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -506,22 +506,28 @@ struct JSScript : public js::gc::Cell {
     bool            usesArguments:1;  /* script uses arguments */
     bool            warnedAboutTwoArgumentEval:1; /* have warned about use of
                                                      obsolete eval(s, o) in
                                                      this script */
     bool            warnedAboutUndefinedProp:1; /* have warned about uses of
                                                    undefined properties in this
                                                    script */
     bool            hasSingletons:1;  /* script has singleton objects */
-    bool            hasFunction:1;    /* function is active in 'where' union */
+    bool            hasFunction:1;       /* script has an associated function */
+    bool            isHeavyweightFunction:1; /* function is heavyweight */
+    bool            isOuterFunction:1;       /* function is heavyweight, with inner functions */
+    bool            isInnerFunction:1;       /* function is directly nested in a heavyweight
+                                              * outer function */
     bool            isActiveEval:1;   /* script came from eval(), and is still active */
     bool            isCachedEval:1;   /* script came from eval(), and is in eval cache */
     bool            usedLazyArgs:1;   /* script has used lazy arguments at some point */
     bool            createdArgs:1;    /* script has had arguments objects created */
     bool            uninlineable:1;   /* script is considered uninlineable by analysis */
+    bool            reentrantOuterFunction:1; /* outer function marked reentrant */
+    bool            typesPurged:1;    /* TypeScript has been purged at some point */
 #ifdef JS_METHODJIT
     bool            debugMode:1;      /* script was compiled in debug mode */
     bool            failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
 #endif
     bool            callDestroyHook:1;/* need to call destroy hook */
 
     uint32          natoms;     /* length of atoms array */
     uint16          nslots;     /* vars plus maximum stack depth */
@@ -530,18 +536,20 @@ struct JSScript : public js::gc::Cell {
     uint16          nClosedArgs; /* number of args which are closed over. */
     uint16          nClosedVars; /* number of vars which are closed over. */
 
     /*
      * To ensure sizeof(JSScript) % gc::Cell::CellSize  == 0 on we must pad
      * the script with 4 bytes. We use them to store tiny scripts like empty
      * scripts.
      */
+#if JS_BITS_PER_WORD == 64
 #define JS_SCRIPT_INLINE_DATA_LIMIT 4
     uint8           inlineData[JS_SCRIPT_INLINE_DATA_LIMIT];
+#endif
 
     const char      *filename;  /* source filename or null */
     JSAtom          **atoms;    /* maps immediate index to literal struct */
   private:
     size_t          useCount;  /* Number of times the script has been called
                                  * or has had backedges taken. Reset if the
                                  * script's JIT code is forcibly discarded. */
   public:
@@ -567,75 +575,75 @@ struct JSScript : public js::gc::Cell {
         JSScript    *evalHashLink;
     } u;
 
     uint32          *closedSlots; /* vector of closed slots; args first, then vars. */
 
     /* array of execution counters for every JSOp in the script, by runmode */
     JSPCCounters    pcCounters;
 
-    union {
-        /* Function this script is the body for, if there is one. */
-        JSFunction *fun;
-
-        /* Global object for this script, if compileAndGo. */
-        js::GlobalObject *global;
-    } where;
-
-    inline JSFunction *function() const {
-        JS_ASSERT(hasFunction);
-        return where.fun;
-    }
-
 #ifdef JS_CRASH_DIAGNOSTICS
     JSObject        *ownerObject;
 
     /* All diagnostic fields must be multiples of Cell::CellSize. */
     uint32          cookie2[sizeof(JSObject *) == 4 ? 1 : 2];
 #endif
 
     void setOwnerObject(JSObject *owner);
 
-    /*
-     * Associates this script with a specific function, constructing a new type
-     * object for the function.
-     */
-    bool typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton = false);
-
-    inline bool hasGlobal() const;
-    inline js::GlobalObject *global() const;
-
-    inline bool hasClearedGlobal() const;
-
 #ifdef DEBUG
     /*
      * Unique identifier within the compartment for this script, used for
      * printing analysis information.
      */
     uint32 id_;
     uint32 idpad;
     unsigned id() { return id_; }
 #else
     unsigned id() { return 0; }
 #endif
 
     /* Persistent type information retained across GCs. */
     js::types::TypeScript *types;
 
-    /* Ensure the script has types, bytecode and/or type inference results. */
-    inline bool ensureHasTypes(JSContext *cx);
-    inline bool ensureRanBytecode(JSContext *cx);
+    /* Ensure the script has a TypeScript. */
+    inline bool ensureHasTypes(JSContext *cx, JSFunction *fun = NULL);
+
+    /*
+     * Ensure the script has scope and bytecode analysis information.
+     * Performed when the script first runs, or first runs after a TypeScript
+     * GC purge. If fun/scope are NULL then the script must already have types
+     * with scope information.
+     */
+    inline bool ensureRanAnalysis(JSContext *cx, JSFunction *fun = NULL, JSObject *scope = NULL);
+
+    /* Ensure the script has type inference analysis information. */
     inline bool ensureRanInference(JSContext *cx);
 
-    /* Filled in by one of the above. */
     inline bool hasAnalysis();
+    inline void clearAnalysis();
     inline js::analyze::ScriptAnalysis *analysis();
 
+    /*
+     * Associates this script with a specific function, constructing a new type
+     * object for the function if necessary.
+     */
+    bool typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton = false);
+
+    inline bool hasGlobal() const;
+    inline bool hasClearedGlobal() const;
+
+    inline JSFunction *function() const;
+    inline js::GlobalObject *global() const;
+    inline js::types::TypeScriptNesting *nesting() const;
+
+    inline void clearNesting();
+
   private:
-    bool makeTypes(JSContext *cx);
+    bool makeTypes(JSContext *cx, JSFunction *fun);
     bool makeAnalysis(JSContext *cx);
   public:
 
 #ifdef JS_METHODJIT
     // Fast-cached pointers to make calls faster. These are also used to
     // quickly test whether there is JIT code; a NULL value means no
     // compilation has been attempted. A JS_UNJITTABLE_SCRIPT value means
     // compilation failed. Any value is the arity-check entry point.
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -172,31 +172,53 @@ JSScript::isEmpty() const
 inline bool
 JSScript::hasGlobal() const
 {
     /*
      * Make sure that we don't try to query information about global objects
      * which have had their scopes cleared. compileAndGo code should not run
      * anymore against such globals.
      */
-    if (!compileAndGo)
-        return false;
-    js::GlobalObject *obj = hasFunction ? function()->getGlobal() : where.global;
+    JS_ASSERT(types && types->hasScope());
+    js::GlobalObject *obj = types->global;
     return obj && !obj->isCleared();
 }
 
 inline js::GlobalObject *
 JSScript::global() const
 {
     JS_ASSERT(hasGlobal());
-    return hasFunction ? function()->getGlobal() : where.global;
+    return types->global;
 }
 
 inline bool
 JSScript::hasClearedGlobal() const
 {
-    if (!compileAndGo)
-        return false;
-    js::GlobalObject *obj = hasFunction ? function()->getGlobal() : where.global;
+    JS_ASSERT(types && types->hasScope());
+    js::GlobalObject *obj = types->global;
     return obj && obj->isCleared();
 }
 
+inline JSFunction *
+JSScript::function() const
+{
+    JS_ASSERT(hasFunction && types);
+    return types->function;
+}
+
+inline js::types::TypeScriptNesting *
+JSScript::nesting() const
+{
+    JS_ASSERT(hasFunction && types && types->hasScope());
+    return types->nesting;
+}
+
+inline void
+JSScript::clearNesting()
+{
+    js::types::TypeScriptNesting *nesting = this->nesting();
+    if (nesting) {
+        js::Foreground::delete_(nesting);
+        types->nesting = NULL;
+    }
+}
+
 #endif /* jsscriptinlines_h___ */
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -128,16 +128,17 @@ mjit::Compiler::Compiler(JSContext *cx, 
 #if defined JS_TRACER
     addTraceHints(cx->traceJitEnabled),
 #else
     addTraceHints(false),
 #endif
     inlining_(false),
     hasGlobalReallocation(false),
     oomInVector(false),
+    gcNumber(cx->runtime->gcNumber),
     applyTricks(NoApplyTricks),
     pcLengths(NULL)
 {
     /* :FIXME: bug 637856 disabling traceJit if inference is enabled */
     if (cx->typeInferenceEnabled())
         addTraceHints = false;
 
     /* Once a script starts getting really hot we will inline calls in it. */
@@ -183,17 +184,17 @@ mjit::Compiler::compile()
 CompileStatus
 mjit::Compiler::checkAnalysis(JSScript *script)
 {
     if (script->hasClearedGlobal()) {
         JaegerSpew(JSpew_Abort, "script has a cleared global\n");
         return Compile_Abort;
     }
 
-    if (!script->ensureRanBytecode(cx))
+    if (!script->ensureRanAnalysis(cx))
         return Compile_Error;
     if (cx->typeInferenceEnabled() && !script->ensureRanInference(cx))
         return Compile_Error;
 
     ScriptAnalysis *analysis = script->analysis();
     if (analysis->failed()) {
         JaegerSpew(JSpew_Abort, "couldn't analyze bytecode; probably switchX or OOM\n");
         return Compile_Abort;
@@ -333,16 +334,21 @@ mjit::Compiler::scanInlineCalls(uint32 i
                 break;
 
             /* Watch for excessively deep nesting of inlined frames. */
             if (nextDepth + script->nslots >= stackLimit) {
                 okay = false;
                 break;
             }
 
+            if (!script->types || !script->types->hasScope()) {
+                okay = false;
+                break;
+            }
+
             CompileStatus status = checkAnalysis(script);
             if (status != Compile_Okay)
                 return status;
 
             if (!script->analysis()->inlineable(argc)) {
                 okay = false;
                 break;
             }
@@ -737,37 +743,70 @@ mjit::Compiler::generatePrologue()
          */
         for (uint32 i = 0; i < script->nfixed; i++) {
             if (analysis->localHasUseBeforeDef(i) || addTraceHints) {
                 Address local(JSFrameReg, sizeof(StackFrame) + i * sizeof(Value));
                 masm.storeValue(UndefinedValue(), local);
             }
         }
 
-        /* Create the call object. */
+        types::TypeScriptNesting *nesting = script->nesting();
+
+        /*
+         * Run the function prologue if necessary. This is always done in a
+         * stub for heavyweight functions (including nesting outer functions).
+         */
+        JS_ASSERT_IF(nesting && nesting->children, script->function()->isHeavyweight());
         if (script->function()->isHeavyweight()) {
             prepareStubCall(Uses(0));
-            INLINE_STUBCALL(stubs::CreateFunCallObject, REJOIN_CREATE_CALL_OBJECT);
-        }
-
-        j.linkTo(masm.label(), &masm);
-
-        if (analysis->usesScopeChain() && !script->function()->isHeavyweight()) {
+            INLINE_STUBCALL(stubs::FunctionFramePrologue, REJOIN_FUNCTION_PROLOGUE);
+        } else {
             /*
-             * Load the scope chain into the frame if necessary.  The scope chain
-             * is always set for global and eval frames, and will have been set by
+             * Load the scope chain into the frame if it will be needed by NAME
+             * opcodes or by the nesting prologue below. The scope chain is
+             * always set for global and eval frames, and will have been set by
              * CreateFunCallObject for heavyweight function frames.
              */
-            RegisterID t0 = Registers::ReturnReg;
-            Jump hasScope = masm.branchTest32(Assembler::NonZero,
-                                              FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN));
-            masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script->function())), t0);
-            masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0);
-            masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain()));
-            hasScope.linkTo(masm.label(), &masm);
+            if (analysis->usesScopeChain() || nesting) {
+                RegisterID t0 = Registers::ReturnReg;
+                Jump hasScope = masm.branchTest32(Assembler::NonZero,
+                                                  FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN));
+                masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script->function())), t0);
+                masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0);
+                masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain()));
+                hasScope.linkTo(masm.label(), &masm);
+            }
+
+            if (nesting) {
+                /*
+                 * Inline the common case for the nesting prologue: the
+                 * function is a non-heavyweight inner function with no
+                 * children of its own. We ensure during inference that the
+                 * outer function does not add scope objects for 'let' or
+                 * 'with', so that the frame's scope chain will be
+                 * the parent's call object, and if it differs from the
+                 * parent's current activation then the parent is reentrant.
+                 */
+                JSScript *parent = nesting->parent;
+                JS_ASSERT(parent);
+                JS_ASSERT_IF(parent->hasAnalysis() && parent->analysis()->ranBytecode(),
+                             !parent->analysis()->addsScopeObjects());
+
+                RegisterID t0 = Registers::ReturnReg;
+                masm.move(ImmPtr(&parent->nesting()->activeCall), t0);
+                masm.loadPtr(Address(t0), t0);
+
+                Address scopeChain(JSFrameReg, StackFrame::offsetOfScopeChain());
+                Jump mismatch = masm.branchPtr(Assembler::NotEqual, t0, scopeChain);
+                masm.add32(Imm32(1), AbsoluteAddress(&nesting->activeFrames));
+
+                stubcc.linkExitDirect(mismatch, stubcc.masm.label());
+                OOL_STUBCALL(stubs::FunctionFramePrologue, REJOIN_FUNCTION_PROLOGUE);
+                stubcc.crossJump(stubcc.masm.jump(), masm.label());
+            }
         }
 
         if (outerScript->usesArguments && !script->function()->isHeavyweight()) {
             /*
              * Make sure that fp->args.nactual is always coherent. This may be
              * inspected directly by JIT code, and is not guaranteed to be
              * correct if the UNDERFLOW and OVERFLOW flags are not set.
              */
@@ -775,16 +814,18 @@ mjit::Compiler::generatePrologue()
                                              Imm32(StackFrame::OVERRIDE_ARGS |
                                                    StackFrame::UNDERFLOW_ARGS |
                                                    StackFrame::OVERFLOW_ARGS |
                                                    StackFrame::HAS_ARGS_OBJ));
             masm.storePtr(ImmPtr((void *)(size_t) script->function()->nargs),
                           Address(JSFrameReg, StackFrame::offsetOfArgs()));
             hasArgs.linkTo(masm.label(), &masm);
         }
+
+        j.linkTo(masm.label(), &masm);
     }
 
     if (cx->typeInferenceEnabled()) {
 #ifdef DEBUG
         if (script->hasFunction) {
             prepareStubCall(Uses(0));
             INLINE_STUBCALL(stubs::AssertArgumentTypes, REJOIN_NONE);
         }
@@ -841,16 +882,23 @@ mjit::Compiler::finishThisUp(JITScript *
 
     /*
      * Watch for reallocation of the global slots while we were in the middle
      * of compiling due to, e.g. standard class initialization.
      */
     if (globalSlots && globalObj->getRawSlots() != globalSlots)
         return Compile_Retry;
 
+    /*
+     * Watch for GCs which occurred during compilation. These may have
+     * renumbered shapes baked into the jitcode.
+     */
+    if (cx->runtime->gcNumber != gcNumber)
+        return Compile_Retry;
+
     for (size_t i = 0; i < branchPatches.length(); i++) {
         Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex);
         branchPatches[i].jump.linkTo(label, &masm);
     }
 
 #ifdef JS_CPU_ARM
     masm.forceFlushConstantPool();
     stubcc.masm.forceFlushConstantPool();
@@ -2696,19 +2744,18 @@ mjit::Compiler::generateMethod()
           }
           END_CASE(JSOP_DEBUGGER)
 
           BEGIN_CASE(JSOP_UNBRAND)
             jsop_unbrand();
           END_CASE(JSOP_UNBRAND)
 
           BEGIN_CASE(JSOP_UNBRANDTHIS)
-            jsop_this();
-            jsop_unbrand();
-            frame.pop();
+            prepareStubCall(Uses(1));
+            INLINE_STUBCALL(stubs::UnbrandThis, REJOIN_FALLTHROUGH);
           END_CASE(JSOP_UNBRANDTHIS)
 
           BEGIN_CASE(JSOP_GETGLOBAL)
           BEGIN_CASE(JSOP_CALLGLOBAL)
             jsop_getglobal(GET_SLOTNO(PC));
             if (op == JSOP_CALLGLOBAL)
                 frame.push(UndefinedValue());
           END_CASE(JSOP_GETGLOBAL)
@@ -3107,32 +3154,41 @@ mjit::Compiler::emitReturn(FrameEntry *f
      * members. For JSOP_RETURN, the interpreter only calls popInlineFrame if
      * fp != entryFrame since the VM protocol is that Invoke/Execute are
      * responsible for pushing/popping the initial frame. The mjit does not
      * perform this branch (by instead using a trampoline at the return address
      * to handle exiting mjit code) and thus always puts activation objects,
      * even on the entry frame. To avoid double-putting, EnterMethodJIT clears
      * out the entry frame's activation objects.
      */
-    if (script->hasFunction && script->function()->isHeavyweight()) {
-        /* There will always be a call object. */
-        prepareStubCall(Uses(fe ? 1 : 0));
-        INLINE_STUBCALL(stubs::PutActivationObjects, REJOIN_NONE);
-    } else {
-        /* if (hasCallObj() || hasArgsObj()) */
-        Jump putObjs = masm.branchTest32(Assembler::NonZero,
-                                         Address(JSFrameReg, StackFrame::offsetOfFlags()),
-                                         Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ));
-        stubcc.linkExit(putObjs, Uses(frame.frameSlots()));
-
-        stubcc.leave();
-        OOL_STUBCALL(stubs::PutActivationObjects, REJOIN_NONE);
-
-        emitReturnValue(&stubcc.masm, fe);
-        emitFinalReturn(stubcc.masm);
+    if (script->hasFunction) {
+        types::TypeScriptNesting *nesting = script->nesting();
+        if (script->function()->isHeavyweight() || (nesting && nesting->children)) {
+            prepareStubCall(Uses(fe ? 1 : 0));
+            INLINE_STUBCALL(stubs::FunctionFrameEpilogue, REJOIN_NONE);
+        } else {
+            /* if (hasCallObj() || hasArgsObj()) */
+            Jump putObjs = masm.branchTest32(Assembler::NonZero,
+                                             Address(JSFrameReg, StackFrame::offsetOfFlags()),
+                                             Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ));
+            stubcc.linkExit(putObjs, Uses(frame.frameSlots()));
+
+            stubcc.leave();
+            OOL_STUBCALL(stubs::FunctionFrameEpilogue, REJOIN_NONE);
+
+            emitReturnValue(&stubcc.masm, fe);
+            emitFinalReturn(stubcc.masm);
+
+            /*
+             * Do frame count balancing inline for inner functions in a nesting
+             * with no children of their own.
+             */
+            if (nesting)
+                masm.sub32(Imm32(1), AbsoluteAddress(&nesting->activeFrames));
+        }
     }
 
     emitReturnValue(&masm, fe);
     emitFinalReturn(masm);
 
     /*
      * After we've placed the call object, all tracked state can be
      * thrown away. This will happen anyway because the next live opcode (if
@@ -5035,16 +5091,32 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
 
     /* If the incoming type will never PIC, take slow path. */
     if (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT) {
         jsop_setprop_slow(atom, usePropCache);
         return true;
     }
 
     /*
+     * If this is a SETNAME to a variable of a non-reentrant outer function,
+     * set the variable's slot directly for the active call object.
+     */
+    if (cx->typeInferenceEnabled() && js_CodeSpec[*PC].format & JOF_NAME) {
+        ScriptAnalysis::NameAccess access =
+            analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true);
+        if (access.nesting) {
+            Address address = frame.loadNameAddress(access);
+            frame.storeTo(rhs, address, popGuaranteed);
+            frame.shimmy(1);
+            frame.freeReg(address.base);
+            return true;
+        }
+    }
+
+    /*
      * Set the property directly if we are accessing a known object which
      * always has the property in a particular inline slot.
      */
     jsid id = ATOM_TO_JSID(atom);
     types::TypeSet *types = frame.extra(lhs).types;
     if (JSOp(*PC) == JSOP_SETPROP && id == types::MakeTypeId(cx, id) &&
         types && !types->unknownObject() &&
         types->getObjectCount() == 1 &&
@@ -5196,16 +5268,36 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
 
     pics.append(pic);
     return true;
 }
 
 void
 mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type, bool isCall)
 {
+    /*
+     * If this is a NAME for a variable of a non-reentrant outer function, get
+     * the variable's slot directly for the active call object. We always need
+     * to check for undefined, however.
+     */
+    if (cx->typeInferenceEnabled()) {
+        ScriptAnalysis::NameAccess access =
+            analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true);
+        if (access.nesting) {
+            Address address = frame.loadNameAddress(access);
+            JSValueType type = knownPushedType(0);
+            BarrierState barrier = pushAddressMaybeBarrier(address, type, true,
+                                                           /* testUndefined = */ true);
+            finishBarrier(barrier, REJOIN_GETTER, 0);
+            if (isCall)
+                jsop_callgname_epilogue();
+            return;
+        }
+    }
+
     PICGenInfo pic(isCall ? ic::PICInfo::CALLNAME : ic::PICInfo::NAME, JSOp(*PC), true);
 
     RESERVE_IC_SPACE(masm);
 
     pic.shapeReg = frame.allocReg();
     pic.objReg = frame.allocReg();
     pic.typeReg = Registers::ReturnReg;
     pic.atom = atom;
@@ -5253,16 +5345,34 @@ mjit::Compiler::jsop_name(JSAtom *atom, 
     pics.append(pic);
 
     finishBarrier(barrier, rejoin, isCall ? 1 : 0);
 }
 
 bool
 mjit::Compiler::jsop_xname(JSAtom *atom)
 {
+    /*
+     * If this is a GETXPROP for a variable of a non-reentrant outer function,
+     * treat in the same way as a NAME.
+     */
+    if (cx->typeInferenceEnabled()) {
+        ScriptAnalysis::NameAccess access =
+            analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true);
+        if (access.nesting) {
+            frame.pop();
+            Address address = frame.loadNameAddress(access);
+            JSValueType type = knownPushedType(0);
+            BarrierState barrier = pushAddressMaybeBarrier(address, type, true,
+                                                           /* testUndefined = */ true);
+            finishBarrier(barrier, REJOIN_GETTER, 0);
+            return true;
+        }
+    }
+
     PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC), true);
 
     FrameEntry *fe = frame.peek(-1);
     if (fe->isNotType(JSVAL_TYPE_OBJECT)) {
         return jsop_getprop(atom, knownPushedType(0));
     }
 
     if (!fe->isTypeKnown()) {
@@ -5312,16 +5422,33 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
 
     finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
     return true;
 }
 
 void
 mjit::Compiler::jsop_bindname(JSAtom *atom, bool usePropCache)
 {
+    /*
+     * If this is a BINDNAME for a variable of a non-reentrant outer function,
+     * the object is definitely the outer function's active call object.
+     */
+    if (cx->typeInferenceEnabled()) {
+        ScriptAnalysis::NameAccess access =
+            analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true);
+        if (access.nesting) {
+            RegisterID reg = frame.allocReg();
+            JSObject **pobj = &access.nesting->activeCall;
+            masm.move(ImmPtr(pobj), reg);
+            masm.loadPtr(Address(reg), reg);
+            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
+            return;
+        }
+    }
+
     PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC), usePropCache);
 
     // This code does not check the frame flags to see if scopeChain has been
     // set. Rather, it relies on the up-front analysis statically determining
     // whether BINDNAME can be used, which reifies the scope chain at the
     // prologue.
     JS_ASSERT(analysis->usesScopeChain());
 
@@ -5453,16 +5580,26 @@ mjit::Compiler::jsop_this()
             if (type != JSVAL_TYPE_OBJECT) {
                 Jump notObj = frame.testObject(Assembler::NotEqual, thisFe);
                 stubcc.linkExit(notObj, Uses(1));
                 stubcc.leave();
                 OOL_STUBCALL(stubs::This, REJOIN_FALLTHROUGH);
                 stubcc.rejoin(Changes(1));
             }
 
+            /*
+             * Watch out for an obscure case where we don't know we are pushing
+             * an object: the script has not yet had a 'this' value assigned,
+             * so no pushed 'this' type has been inferred. Don't mark the type
+             * as known in this case, preserving the invariant that compiler
+             * types reflect inferred types.
+             */
+            if (cx->typeInferenceEnabled() && knownPushedType(0) != JSVAL_TYPE_OBJECT)
+                return;
+
             // Now we know that |this| is an object.
             frame.pop();
             frame.learnThisIsObject(type != JSVAL_TYPE_OBJECT);
             frame.pushThis();
         }
 
         JS_ASSERT(thisFe->isType(JSVAL_TYPE_OBJECT));
     }
@@ -5903,27 +6040,41 @@ mjit::Compiler::jsop_callgname_epilogue(
     if (fval->isNotType(JSVAL_TYPE_OBJECT)) {
         frame.push(UndefinedValue());
         return;
     }
 
     /* Paths for known object callee. */
     if (fval->isConstant()) {
         JSObject *obj = &fval->getValue().toObject();
-        if (obj->getParent() == globalObj) {
+        if (obj->getGlobal() == globalObj) {
             frame.push(UndefinedValue());
         } else {
             prepareStubCall(Uses(1));
             INLINE_STUBCALL(stubs::PushImplicitThisForGlobal, REJOIN_NONE);
             frame.pushSynced(JSVAL_TYPE_UNKNOWN);
         }
         return;
     }
 
     /*
+     * Fast path for functions whose global is statically known to be the
+     * current global. This is primarily for calls on inner functions within
+     * nestings, whose direct parent is a call object rather than the global
+     * and which will make a stub call in the path below.
+     */
+    if (cx->typeInferenceEnabled()) {
+        types::TypeSet *types = analysis->pushedTypes(PC, 0);
+        if (types->hasGlobalObject(cx, globalObj)) {
+            frame.push(UndefinedValue());
+            return;
+        }
+    }
+
+    /*
      * Optimized version. This inlines the common case, calling a
      * (non-proxied) function that has the same global as the current
      * script. To make the code simpler, we:
      *      1. test the stronger property that the callee's parent is
      *         equal to the global of the current script, and
      *      2. bake in the global of the current script, which is why
      *         this optimized path requires compile-and-go.
      */
@@ -6937,17 +7088,17 @@ mjit::Compiler::fixDoubleTypes(jsbytecod
                      * set for the existing value must be empty, so this
                      * code is doomed and we can just mark the value as
                      * a double.
                      */
                     frame.ensureDouble(fe);
                 } else {
                     JS_ASSERT(vt.type == JSVAL_TYPE_DOUBLE);
                 }
-            } else if (fe->isType(JSVAL_TYPE_DOUBLE)) {
+            } else if (vt.type == JSVAL_TYPE_DOUBLE) {
                 fixedDoubleToAnyEntries.append(newv->slot);
                 frame.syncAndForgetFe(fe);
                 frame.forgetLoopReg(fe);
             }
             newv++;
         }
     }
 }
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -463,16 +463,17 @@ class Compiler : public BaseCompiler
     Label argsCheckFallthrough;
     Jump argsCheckJump;
 #endif
     bool debugMode_;
     bool addTraceHints;
     bool inlining_;
     bool hasGlobalReallocation;
     bool oomInVector;       // True if we have OOM'd appending to a vector. 
+    uint32 gcNumber;
     enum { NoApplyTricks, LazyArgsObj } applyTricks;
     PCLengthEntry *pcLengths;
 
     Compiler *thisFromCtor() { return this; }
 
     friend class CompilerAllocPolicy;
   public:
     Compiler(JSContext *cx, JSScript *outerScript, bool isConstructing);
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -873,16 +873,29 @@ FrameState::syncAndForgetFe(FrameEntry *
     }
 
     syncFe(fe);
     forgetAllRegs(fe);
     fe->type.setMemory();
     fe->data.setMemory();
 }
 
+inline JSC::MacroAssembler::Address
+FrameState::loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access)
+{
+    JS_ASSERT(access.script && access.nesting);
+
+    RegisterID reg = allocReg();
+    Value **pbase = access.arg ? &access.nesting->argArray : &access.nesting->varArray;
+    masm.move(ImmPtr(pbase), reg);
+    masm.loadPtr(Address(reg), reg);
+
+    return Address(reg, access.index * sizeof(Value));
+}
+
 inline void
 FrameState::forgetLoopReg(FrameEntry *fe)
 {
     /*
      * Don't use a loop register for fe in the active loop, as its underlying
      * representation may have changed since the start of the loop.
      */
     if (loop)
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -1944,19 +1944,16 @@ FrameState::pushCopyOf(FrameEntry *backi
         if (fe->trackerIndex() < backing->trackerIndex())
             swapInTracker(fe, backing);
     }
 }
 
 FrameEntry *
 FrameState::walkTrackerForUncopy(FrameEntry *original)
 {
-    /* Temporary entries are immutable and should never be uncopied. */
-    JS_ASSERT(!isTemporary(original));
-
     uint32 firstCopy = InvalidIndex;
     FrameEntry *bestFe = NULL;
     uint32 ncopies = 0;
     for (uint32 i = original->trackerIndex() + 1; i < tracker.nentries; i++) {
         FrameEntry *fe = tracker[i];
         if (deadEntry(fe))
             continue;
         if (fe->isCopy() && fe->copyOf() == original) {
@@ -1973,17 +1970,17 @@ FrameState::walkTrackerForUncopy(FrameEn
     if (!ncopies) {
         JS_ASSERT(firstCopy == InvalidIndex);
         JS_ASSERT(!bestFe);
         return NULL;
     }
 
     JS_ASSERT(firstCopy != InvalidIndex);
     JS_ASSERT(bestFe);
-    JS_ASSERT(bestFe > original);
+    JS_ASSERT_IF(!isTemporary(original), bestFe > original);
 
     /* Mark all extra copies as copies of the new backing index. */
     bestFe->setCopyOf(NULL);
     if (ncopies > 1) {
         for (uint32 i = firstCopy; i < tracker.nentries; i++) {
             FrameEntry *other = tracker[i];
             if (deadEntry(other) || other == bestFe)
                 continue;
@@ -2868,16 +2865,18 @@ FrameState::allocTemporary()
 void
 FrameState::clearTemporaries()
 {
     JS_ASSERT(!a->parent);
 
     for (FrameEntry *fe = temporaries; fe < temporariesTop; fe++) {
         if (!fe->isTracked())
             continue;
+        if (fe->isCopied())
+            uncopy(fe);
         forgetAllRegs(fe);
         fe->resetSynced();
     }
 
     temporariesTop = temporaries;
 }
 
 Vector<TemporaryCopy> *
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -948,16 +948,22 @@ class FrameState
     inline FrameEntry *getTemporary(uint32 which);
 
     /* Return NULL or a new vector with all current copies of temporaries. */
     Vector<TemporaryCopy> *getTemporaryCopies();
 
     inline void syncAndForgetFe(FrameEntry *fe, bool markSynced = false);
     inline void forgetLoopReg(FrameEntry *fe);
 
+    /*
+     * Get an address for the specified name access in another script.
+     * The compiler owns the result's base register.
+     */
+    inline Address loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access);
+
   private:
     inline AnyRegisterID allocAndLoadReg(FrameEntry *fe, bool fp, RematInfo::RematType type);
     inline void forgetReg(AnyRegisterID reg);
     AnyRegisterID evictSomeReg(uint32 mask);
     void evictReg(AnyRegisterID reg);
     inline FrameEntry *rawPush();
     inline void addToTracker(FrameEntry *fe);
 
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -356,17 +356,17 @@ UncachedInlineCall(VMFrame &f, InitialFr
     /* Get pointer to new frame/slots, prepare arguments. */
     if (!cx->stack.pushInlineFrame(cx, regs, args, callee, newfun, newscript, initial, &f.stackLimit))
         return false;
 
     /* Finish the handoff to the new frame regs. */
     PreserveRegsGuard regsGuard(cx, regs);
 
     /* Scope with a call object parented by callee's parent. */
-    if (newfun->isHeavyweight() && !js::CreateFunCallObject(cx, regs.fp()))
+    if (!regs.fp()->functionPrologue(cx))
         return false;
 
     /*
      * If newscript was successfully compiled, run it. Skip for calls which
      * will be constructing a new type object for 'this'.
      */
     if (!newType) {
         if (JITScript *jit = newscript->getJIT(regs.fp()->isConstructing())) {
@@ -492,23 +492,16 @@ stubs::UncachedCallHelper(VMFrame &f, ui
 
     if (!InvokeKernel(f.cx, args))
         THROW();
 
     types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     return;
 }
 
-void JS_FASTCALL
-stubs::PutActivationObjects(VMFrame &f)
-{
-    JS_ASSERT(f.fp()->hasCallObj() || f.fp()->hasArgsObj());
-    f.fp()->putActivationObjects();
-}
-
 static void
 RemoveOrphanedNative(JSContext *cx, StackFrame *fp)
 {
     /*
      * Remove fp from the list of frames holding a reference on the orphaned
      * native pools. If all the references have been removed, release all the
      * pools. We don't release pools piecemeal as a pool can be referenced by
      * multiple frames.
@@ -629,17 +622,17 @@ js_InternalThrow(VMFrame &f)
          * Fall back to EnterMethodJIT and finish the frame in the interpreter.
          * With type inference enabled, we may wipe out all JIT code on the
          * stack without patching ncode values to jump to the interpreter, and
          * thus can only enter JIT code via EnterMethodJIT (which overwrites
          * its entry frame's ncode). See ClearAllFrames.
          */
         cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished);
 
-        if (!script->ensureRanBytecode(cx)) {
+        if (!script->ensureRanAnalysis(cx)) {
             js_ReportOutOfMemory(cx);
             return NULL;
         }
 
         analyze::AutoEnterAnalysis enter(cx);
 
         cx->regs().pc = pc;
         cx->regs().sp = fp->base() + script->analysis()->getCode(pc).stackDepth;
@@ -666,24 +659,16 @@ js_InternalThrow(VMFrame &f)
 
         return NULL;
     }
 
     return script->nativeCodeForPC(fp->isConstructing(), pc);
 }
 
 void JS_FASTCALL
-stubs::CreateFunCallObject(VMFrame &f)
-{
-    JS_ASSERT(f.fp()->fun()->isHeavyweight());
-    if (!js::CreateFunCallObject(f.cx, f.fp()))
-        THROW();
-}
-
-void JS_FASTCALL
 stubs::CreateThis(VMFrame &f, JSObject *proto)
 {
     JSContext *cx = f.cx;
     StackFrame *fp = f.fp();
     JSObject *callee = &fp->callee();
     JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
     if (!obj)
         THROW();
@@ -1246,17 +1231,17 @@ js_InternalInterpret(void *returnData, v
     JSScript *script = fp->script();
 
     jsbytecode *pc = f.regs.pc;
     analyze::UntrapOpcode untrap(cx, script, pc);
 
     JSOp op = JSOp(*pc);
     const JSCodeSpec *cs = &js_CodeSpec[op];
 
-    if (!script->ensureRanBytecode(cx)) {
+    if (!script->ensureRanAnalysis(cx)) {
         js_ReportOutOfMemory(cx);
         return js_InternalThrow(f);
     }
 
     analyze::AutoEnterAnalysis enter(cx);
     analyze::ScriptAnalysis *analysis = script->analysis();
 
     /*
@@ -1390,43 +1375,38 @@ js_InternalInterpret(void *returnData, v
             return js_InternalThrow(f);
         fp->formalArgs()[-1].setObject(*obj);
 
         if (script->debugMode || Probes::callTrackingActive(cx))
             js::ScriptDebugPrologue(cx, fp);
         break;
       }
 
-      case REJOIN_CHECK_ARGUMENTS: {
+      case REJOIN_CHECK_ARGUMENTS:
         /*
          * Do all the work needed in arity check JIT prologues after the
          * arguments check occurs (FixupArity has been called if needed, but
          * the stack check and late prologue have not been performed.
          */
         if (!CheckStackQuota(f))
             return js_InternalThrow(f);
 
         SetValueRangeToUndefined(fp->slots(), script->nfixed);
 
-        if (fp->fun()->isHeavyweight()) {
-            if (!js::CreateFunCallObject(cx, fp))
-                return js_InternalThrow(f);
-        }
+        if (!fp->functionPrologue(cx))
+            return js_InternalThrow(f);
+        /* FALLTHROUGH */
 
-        /* FALLTHROUGH */
-      }
-
-      case REJOIN_CREATE_CALL_OBJECT: {
+      case REJOIN_FUNCTION_PROLOGUE:
         fp->scopeChain();
 
         /* Construct the 'this' object for the frame if necessary. */
         if (!ScriptPrologueOrGeneratorResume(cx, fp, types::UseNewTypeAtEntry(cx, fp)))
             return js_InternalThrow(f);
         break;
-      }
 
       case REJOIN_CALL_PROLOGUE:
       case REJOIN_CALL_PROLOGUE_LOWERED_CALL:
       case REJOIN_CALL_PROLOGUE_LOWERED_APPLY:
         if (returnReg) {
             uint32 argc = 0;
             if (rejoin == REJOIN_CALL_PROLOGUE)
                 argc = GET_ARGC(pc);
@@ -1488,16 +1468,17 @@ js_InternalInterpret(void *returnData, v
           case JSOP_GETPROP:
           case JSOP_GETXPROP:
           case JSOP_LENGTH:
             /* Non-fused opcode, state is already correct for the next op. */
             f.regs.pc = nextpc;
             break;
 
           case JSOP_CALLGNAME:
+          case JSOP_CALLNAME:
             if (!ComputeImplicitThis(cx, &fp->scopeChain(), nextsp[-2], &nextsp[-1]))
                 return js_InternalThrow(f);
             f.regs.pc = nextpc;
             break;
 
           case JSOP_CALLGLOBAL:
           case JSOP_CALLFCSLOT:
             /* |this| is always undefined for CALLGLOBAL/CALLFCSLOT. */
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -571,21 +571,26 @@ LoopState::hoistArrayLengthCheck(Invaria
     int32 indexConstant;
     if (!getEntryValue(index, &indexSlot, &indexConstant)) {
         JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
         return false;
     }
 
     if (indexSlot == UNASSIGNED) {
         /* Hoist checks on x[n] accesses for constant n. */
+        if (indexConstant < 0) {
+            JaegerSpew(JSpew_Analysis, "Constant index is negative\n");
+            return false;
+        }
         return addHoistedCheck(arrayKind, objSlot, UNASSIGNED, UNASSIGNED, indexConstant);
     }
 
     if (loopInvariantEntry(indexSlot)) {
         /* Hoist checks on x[y] accesses when y is loop invariant. */
+        addNegativeCheck(indexSlot, indexConstant);
         return addHoistedCheck(arrayKind, objSlot, indexSlot, UNASSIGNED, indexConstant);
     }
 
     /*
      * If the LHS can decrease in the loop, it could become negative and
      * underflow the array. We currently only hoist bounds checks for loops
      * which walk arrays going forward.
      */
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -920,17 +920,18 @@ mjit::EnterMethodJIT(JSContext *cx, Stac
     JS_ASSERT(fp == cx->fp());
 
     if (ok) {
         /* The trampoline wrote the return value but did not set the HAS_RVAL flag. */
         fp->markReturnValue();
     }
 
     /* See comment in mjit::Compiler::emitReturn. */
-    fp->markActivationObjectsAsPut();
+    if (fp->isFunctionFrame())
+        fp->markFunctionEpilogueDone();
 
     return ok ? Jaeger_Returned : Jaeger_Throwing;
 }
 
 static inline JaegerStatus
 CheckStackAndEnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, bool partial)
 {
     JS_CHECK_RECURSION(cx, return Jaeger_Throwing);
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -280,22 +280,25 @@ enum RejoinState {
     /*
      * During the prologue of constructing scripts, after the function's
      * .prototype property has been fetched.
      */
     REJOIN_THIS_PROTOTYPE,
 
     /*
      * Type check on arguments failed during prologue, need stack check and
-     * call object creation before script can execute.
+     * the rest of the JIT prologue before the script can execute.
      */
     REJOIN_CHECK_ARGUMENTS,
 
-    /* A GC while making a call object occurred, discarding the script's jitcode. */
-    REJOIN_CREATE_CALL_OBJECT,
+    /*
+     * The script's jitcode was discarded after marking an outer function as
+     * reentrant or due to a GC while creating a call object.
+     */
+    REJOIN_FUNCTION_PROLOGUE,
 
     /*
      * State after calling a stub which returns a JIT code pointer for a call
      * or NULL for an already-completed call.
      */
     REJOIN_CALL_PROLOGUE,
     REJOIN_CALL_PROLOGUE_LOWERED_CALL,
     REJOIN_CALL_PROLOGUE_LOWERED_APPLY,
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -686,19 +686,20 @@ class SetPropCompiler : public PICStubCo
                  * shape guards we have performed do not by themselves
                  * guarantee that future call objects hit will be for the same
                  * script. We also depend on the fact that the scope chains hit
                  * at the same bytecode are all isomorphic: the same scripts,
                  * in the same order (though the properties on their call
                  * objects may differ due to eval(), DEFFUN, etc.).
                  */
                 RecompilationMonitor monitor(cx);
-                JSScript *script = obj->getCallObjCalleeFunction()->script();
+                JSFunction *fun = obj->getCallObjCalleeFunction();
+                JSScript *script = fun->script();
                 uint16 slot = uint16(shape->shortid);
-                if (!script->ensureHasTypes(cx))
+                if (!script->ensureHasTypes(cx, fun))
                     return error();
                 {
                     types::AutoEnterTypeInference enter(cx);
                     if (shape->setterOp() == SetCallArg)
                         pic.rhsTypes->addSubset(cx, types::TypeScript::ArgTypes(script, slot));
                     else
                         pic.rhsTypes->addSubset(cx, types::TypeScript::LocalTypes(script, slot));
                 }
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -2190,16 +2190,29 @@ stubs::Unbrand(VMFrame &f)
     if (!thisv.isObject())
         return;
     JSObject *obj = &thisv.toObject();
     if (obj->isNative())
         obj->unbrand(f.cx);
 }
 
 void JS_FASTCALL
+stubs::UnbrandThis(VMFrame &f)
+{
+    if (!ComputeThis(f.cx, f.fp()))
+        THROW();
+    Value &thisv = f.fp()->thisValue();
+    if (!thisv.isObject())
+        return;
+    JSObject *obj = &thisv.toObject();
+    if (obj->isNative())
+        obj->unbrand(f.cx);
+}
+
+void JS_FASTCALL
 stubs::Pos(VMFrame &f)
 {
     if (!ToNumber(f.cx, &f.regs.sp[-1]))
         THROW();
     if (!f.regs.sp[-1].isInt32())
         TypeScript::MonitorOverflow(f.cx, f.script(), f.pc());
 }
 
@@ -2515,16 +2528,37 @@ stubs::Exception(VMFrame &f)
     // handling.
     if (JS_THREAD_DATA(f.cx)->interruptFlags && !js_HandleExecutionInterrupt(f.cx))
         THROW();
 
     f.regs.sp[0] = f.cx->getPendingException();
     f.cx->clearPendingException();
 }
 
+void JS_FASTCALL
+stubs::FunctionFramePrologue(VMFrame &f)
+{
+    if (!f.fp()->functionPrologue(f.cx))
+        THROW();
+}
+
+void JS_FASTCALL
+stubs::FunctionFrameEpilogue(VMFrame &f)
+{
+    f.fp()->functionEpilogue();
+}
+
+void JS_FASTCALL
+stubs::AnyFrameEpilogue(VMFrame &f)
+{
+    if (f.fp()->isNonEvalFunctionFrame())
+        f.fp()->functionEpilogue();
+    stubs::ScriptDebugEpilogue(f);
+}
+
 template <bool Clamped>
 int32 JS_FASTCALL
 stubs::ConvertToTypedInt(JSContext *cx, Value *vp)
 {
     JS_ASSERT(!vp->isInt32());
 
     if (vp->isDouble()) {
         if (Clamped)
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -108,18 +108,16 @@ struct UncachedCallResult {
  * These functions either execute the function, return a native code
  * pointer that can be used to call the function, or throw.
  */
 void UncachedCallHelper(VMFrame &f, uint32 argc, bool lowered, UncachedCallResult *ucr);
 void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
 
 void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
 void JS_FASTCALL Throw(VMFrame &f);
-void JS_FASTCALL PutActivationObjects(VMFrame &f);
-void JS_FASTCALL CreateFunCallObject(VMFrame &f);
 #if JS_MONOIC
 void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic);
 #else
 void * JS_FASTCALL InvokeTracer(VMFrame &f);
 #endif
 
 void * JS_FASTCALL LookupSwitch(VMFrame &f, jsbytecode *pc);
 void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc);
@@ -197,16 +195,17 @@ JSBool JS_FASTCALL IterMore(VMFrame &f);
 void JS_FASTCALL EndIter(VMFrame &f);
 
 JSBool JS_FASTCALL ValueToBoolean(VMFrame &f);
 JSString * JS_FASTCALL TypeOf(VMFrame &f);
 JSBool JS_FASTCALL InstanceOf(VMFrame &f);
 void JS_FASTCALL FastInstanceOf(VMFrame &f);
 void JS_FASTCALL ArgCnt(VMFrame &f);
 void JS_FASTCALL Unbrand(VMFrame &f);
+void JS_FASTCALL UnbrandThis(VMFrame &f);
 
 /*
  * Helper for triggering recompilation should a name read miss a type barrier,
  * produce undefined or -0.
  */
 void JS_FASTCALL TypeBarrierHelper(VMFrame &f, uint32 which);
 void JS_FASTCALL TypeBarrierReturn(VMFrame &f, Value *vp);
 void JS_FASTCALL NegZeroHelper(VMFrame &f);
@@ -222,16 +221,21 @@ void JS_FASTCALL MissedBoundsCheckEntry(
 void JS_FASTCALL MissedBoundsCheckHead(VMFrame &f);
 void * JS_FASTCALL InvariantFailure(VMFrame &f, void *repatchCode);
 
 template <bool strict> int32 JS_FASTCALL ConvertToTypedInt(JSContext *cx, Value *vp);
 void JS_FASTCALL ConvertToTypedFloat(JSContext *cx, Value *vp);
 
 void JS_FASTCALL Exception(VMFrame &f);
 
+void JS_FASTCALL FunctionFramePrologue(VMFrame &f);
+void JS_FASTCALL FunctionFrameEpilogue(VMFrame &f);
+
+void JS_FASTCALL AnyFrameEpilogue(VMFrame &f);
+
 JSObject * JS_FASTCALL
 NewDenseUnallocatedArray(VMFrame &f, uint32 length);
 
 } /* namespace stubs */
 
 /* 
  * If COND is true, return A; otherwise, return B. This allows us to choose between
  * function template instantiations without running afoul of C++'s overload resolution
--- a/js/src/methodjit/TrampolineCompiler.cpp
+++ b/js/src/methodjit/TrampolineCompiler.cpp
@@ -105,33 +105,29 @@ TrampolineCompiler::compileTrampoline(Tr
     *where = JS_DATA_TO_FUNC_PTR(Trampolines::TrampolinePtr, result + masm.distanceOf(entry));
 
     return true;
 }
 
 /*
  * This is shamelessly copied from emitReturn, but with several changes:
  * - There was always at least one inline call.
- * - We don't know if there is a call object, so we always check.
+ * - We don't know if there are activation objects or a script with nesting
+ *   state whose active frames need adjustment, so we always stub the epilogue.
  * - We don't know where we came from, so we don't know frame depth or PC.
  * - There is no stub buffer.
  */
 bool
 TrampolineCompiler::generateForceReturn(Assembler &masm)
 {
     /* The JSStackFrame register may have been clobbered while returning, reload it. */
     masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg);
 
-    masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::ScriptDebugEpilogue), NULL, NULL, 0);
-
-    /* if (hasArgsObj() || hasCallObj()) stubs::PutActivationObjects() */
-    Jump noActObjs = masm.branchTest32(Assembler::Zero, FrameFlagsAddress(),
-                                       Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ));
-    masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::PutActivationObjects), NULL, NULL, 0);
-    noActObjs.linkTo(masm.label(), &masm);
+    /* Perform the frame epilogue. */
+    masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::AnyFrameEpilogue), NULL, NULL, 0);
 
     /* Store any known return value */
     masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data);
     Jump rvalClear = masm.branchTest32(Assembler::Zero,
                                        FrameFlagsAddress(), Imm32(StackFrame::HAS_RVAL));
     Address rvalAddress(JSFrameReg, StackFrame::offsetOfReturnValue());
     masm.loadValueAsComponents(rvalAddress, JSReturnReg_Type, JSReturnReg_Data);
     rvalClear.linkTo(masm.label(), &masm);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -220,17 +220,20 @@ BreakpointSite::clearTrap(JSContext *cx,
         *handlerp = trapHandler;
     if (closurep)
         *closurep = trapClosure;
 
     trapHandler = NULL;
     trapClosure.setUndefined();
     if (enabledCount == 0) {
         *pc = realOpcode;
-        recompile(cx, true);  /* ignore failure */
+        if (!cx->runtime->gcRunning) {
+            /* If the GC is running then the script is being destroyed. */
+            recompile(cx, true);  /* ignore failure */
+        }
         destroyIfEmpty(cx->runtime, e);
     }
 }
 
 void
 BreakpointSite::destroyIfEmpty(JSRuntime *rt, BreakpointSiteMap::Enum *e)
 {
     if (JS_CLIST_IS_EMPTY(&breakpoints) && !trapHandler) {
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -41,16 +41,17 @@
 #ifndef Stack_inl_h__
 #define Stack_inl_h__
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "Stack.h"
 
+#include "jsscriptinlines.h"
 #include "ArgumentsObject-inl.h"
 #include "methodjit/MethodJIT.h"
 
 namespace js {
 
 inline void
 StackFrame::initPrev(JSContext *cx)
 {
@@ -125,21 +126,16 @@ StackFrame::initCallFrame(JSContext *cx,
     SetValueRangeToUndefined(slots(), script->nfixed);
 }
 
 inline void
 StackFrame::resetCallFrame(JSScript *script)
 {
     JS_ASSERT(script == this->script());
 
-    /* Undo changes to frame made during execution; see also initCallFrame */
-
-    putActivationObjects();
-    markActivationObjectsAsPut();
-
     if (flags_ & UNDERFLOW_ARGS)
         SetValueRangeToUndefined(formalArgs() + numActualArgs(), formalArgsEnd());
 
     JS_ASSERT(!(flags_ & ~(FUNCTION |
                            OVERFLOW_ARGS |
                            UNDERFLOW_ARGS |
                            OVERRIDE_ARGS |
                            HAS_PREVPC |
@@ -360,30 +356,68 @@ StackFrame::callObj() const
     JSObject *pobj = &scopeChain();
     while (JS_UNLIKELY(!pobj->isCall())) {
         JS_ASSERT(IsCacheableNonGlobalScope(pobj) || pobj->isWith());
         pobj = pobj->getParent();
     }
     return *pobj;
 }
 
+inline bool
+StackFrame::maintainNestingState() const
+{
+    /*
+     * Whether to invoke the nesting epilogue/prologue to maintain active
+     * frame counts and check for reentrant outer functions.
+     */
+    return isNonEvalFunctionFrame() && !isGeneratorFrame() && script()->nesting();
+}
+
+inline bool
+StackFrame::functionPrologue(JSContext *cx)
+{
+    JS_ASSERT(isNonEvalFunctionFrame());
+
+    JSFunction *fun = this->fun();
+
+    if (fun->isHeavyweight()) {
+        if (!CreateFunCallObject(cx, this))
+            return false;
+    } else {
+        /* Force instantiation of the scope chain, for JIT frames. */
+        scopeChain();
+    }
+
+    if (script()->nesting()) {
+        JS_ASSERT(maintainNestingState());
+        types::NestingPrologue(cx, this);
+    }
+
+    return true;
+}
+
 inline void
-StackFrame::putActivationObjects()
+StackFrame::functionEpilogue(bool objectsOnly)
 {
+    JS_ASSERT(isNonEvalFunctionFrame());
+
     if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
         /* NB: there is an ordering dependency here. */
         if (hasCallObj())
             js_PutCallObject(this);
         else if (hasArgsObj())
             js_PutArgsObject(this);
     }
+
+    if (!objectsOnly && maintainNestingState())
+        types::NestingEpilogue(this);
 }
 
 inline void
-StackFrame::markActivationObjectsAsPut()
+StackFrame::markFunctionEpilogueDone(bool activationOnly)
 {
     if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
         if (hasArgsObj() && !argsObj().getPrivate()) {
             args.nactual = args.obj->initialLength();
             flags_ &= ~HAS_ARGS_OBJ;
         }
         if (hasCallObj() && !callObj().getPrivate()) {
             /*
@@ -394,16 +428,24 @@ StackFrame::markActivationObjectsAsPut()
              * scope chain.
              */
             scopeChain_ = isFunctionFrame()
                           ? callee().getParent()
                           : scopeChain_->getParent();
             flags_ &= ~HAS_CALL_OBJ;
         }
     }
+
+    /*
+     * For outer/inner function frames, undo the active frame balancing so that
+     * when we redo it in the epilogue we get the right final value. The other
+     * nesting epilogue changes (update active args/vars) are idempotent.
+     */
+    if (!activationOnly && maintainNestingState())
+        script()->nesting()->activeFrames++;
 }
 
 /*****************************************************************************/
 
 #ifdef JS_TRACER
 JS_ALWAYS_INLINE bool
 StackSpace::ensureEnoughSpaceToEnterTrace(JSContext *cx)
 {
@@ -542,17 +584,17 @@ ContextStack::getFixupFrame(JSContext *c
 
 JS_ALWAYS_INLINE void
 ContextStack::popInlineFrame(FrameRegs &regs)
 {
     JS_ASSERT(onTop());
     JS_ASSERT(&regs == &seg_->regs());
 
     StackFrame *fp = regs.fp();
-    fp->putActivationObjects();
+    fp->functionEpilogue();
 
     Value *newsp = fp->actualArgs() - 1;
     JS_ASSERT(newsp >= fp->prev()->base());
 
     newsp[-1] = fp->returnValue();
     regs.popFrame(newsp);
 }
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -773,17 +773,18 @@ ContextStack::pushDummyFrame(JSContext *
 void
 ContextStack::popFrame(const FrameGuard &fg)
 {
     JS_ASSERT(fg.pushed());
     JS_ASSERT(onTop());
     JS_ASSERT(space().firstUnused() == fg.regs_.sp);
     JS_ASSERT(&fg.regs_ == &seg_->regs());
 
-    fg.regs_.fp()->putActivationObjects();
+    if (fg.regs_.fp()->isNonEvalFunctionFrame())
+        fg.regs_.fp()->functionEpilogue();
 
     seg_->popRegs(fg.prevRegs_);
     if (fg.pushedSeg_)
         popSegment();
 
     /*
      * NB: this code can call out and observe the stack (e.g., through GC), so
      * it should only be called from a consistent stack state.
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -851,21 +851,38 @@ class StackFrame
         return ret;
     }
 
     inline JSObject &callObj() const;
     inline void setScopeChainNoCallObj(JSObject &obj);
     inline void setScopeChainWithOwnCallObj(JSObject &obj);
 
     /*
-     * NB: putActivationObjects does not mark activation objects as having been
-     * put (since the frame is about to be popped).
+     * Prologue for function frames: make a call object for heavyweight
+     * functions, and maintain type nesting invariants.
      */
-    inline void putActivationObjects();
-    inline void markActivationObjectsAsPut();
+    inline bool functionPrologue(JSContext *cx);
+
+    /*
+     * Epilogue for function frames: put any args or call object for the frame
+     * which may still be live, and maintain type nesting invariants. Only the
+     * args/call objects are put if activationOnly is set. Note: this does not
+     * mark the epilogue as having been completed, since the frame is about to
+     * be popped. Use markFunctionEpilogueDone for this.
+     */
+    inline void functionEpilogue(bool activationOnly = false);
+
+    /*
+     * Mark any work needed in the function's epilogue as done. Only the args
+     * and call objects are reset if activationOnly is set. If activationOnly
+     * is *NOT* set, this call must be followed by a later functionEpilogue.
+     */
+    inline void markFunctionEpilogueDone(bool activationOnly = false);
+
+    inline bool maintainNestingState() const;
 
     /*
      * Variables object
      *
      * Given that a (non-dummy) StackFrame corresponds roughly to a ES5
      * Execution Context (ES5 10.3), StackFrame::varObj corresponds to the
      * VariableEnvironment component of a Exection Context. Intuitively, the
      * variables object is where new bindings (variables and functions) are
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -1361,17 +1361,20 @@ CellCallback(JSContext *cx, void *vdata,
             if(shape->hasTable())
                 curr->propertyTables +=
                     shape->getTable()->sizeOf(moz_malloc_usable_size);
             break;
         }
         case JSTRACE_SCRIPT:
         {
             JSScript *script = static_cast<JSScript *>(thing);
-            if (script->data != script->inlineData) {
+#if JS_SCRIPT_INLINE_DATA_LIMIT
+            if (script->data != script->inlineData)
+#endif
+            {
                 size_t usable = moz_malloc_usable_size(script->data);
                 curr->scriptData += usable ? usable : script->dataSize();
             }
 #ifdef JS_METHODJIT
             curr->mjitData += script->jitDataSize(moz_malloc_usable_size);
 #endif
             break;
         }