Merge last green PGO from inbound to central
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 19 Jan 2012 11:34:17 +0100
changeset 86066 f76b576a9e28264cda0df1e782ac48d8c04f0ff8
parent 86016 58e933465c3624555701e202085c87d024a3bf41 (current diff)
parent 86065 cfd3838b4dc2420462e09d8bbf187e43b2a3af0e (diff)
child 86075 78f821cb89749b065b738a6520ee22b97a83e01c
child 86108 14ec9677313bfcfed0fb7a0bf41dd93d1d4aa7d7
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone12.0a1
Merge last green PGO from inbound to central
js/src/methodjit/Compiler.cpp
js/src/methodjit/FastOps.cpp
mobile/android/chrome/content/browser.js
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -196,16 +196,21 @@ var shell = {
     switch (evt.type) {
       case 'keypress':
         switch (evt.keyCode) {
           case evt.DOM_VK_HOME:
             this.sendEvent(this.home.contentWindow, 'home');
             break;
           case evt.DOM_VK_SLEEP:
             this.toggleScreen();
+
+            let details = {
+              'enabled': screen.mozEnabled
+            };
+            this.sendEvent(this.home.contentWindow, 'sleep', details);
             break;
           case evt.DOM_VK_ESCAPE:
             if (evt.defaultPrevented)
               return;
             this.doCommand('cmd_close');
             break;
         }
         break;
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -597,8 +597,10 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 #ifdef XP_OS2
 @BINPATH@/MozSounds.cmd
 #endif
 
 [b2g]
 @BINPATH@/chrome/icons/
 @BINPATH@/chrome/chrome@JAREXT@
 @BINPATH@/chrome/chrome.manifest
+@BINPATH@/components/B2GComponents.manifest
+@BINPATH@/components/B2GComponents.xpt
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -227,16 +227,22 @@ var FullZoom = {
     // Avoid the cps roundtrip and apply the default/global pref.
     if (aURI.spec == "about:blank") {
       this._applyPrefToSetting(undefined, aBrowser);
       return;
     }
 
     let browser = aBrowser || gBrowser.selectedBrowser;
 
+    // Image documents should always start at 1, and are not affected by prefs.
+    if (!aIsTabSwitch && browser.contentDocument instanceof ImageDocument) {
+      ZoomManager.setZoomForBrowser(browser, 1);
+      return;
+    }
+
     if (Services.contentPrefs.hasCachedPref(aURI, this.name)) {
       let zoomValue = Services.contentPrefs.getPref(aURI, this.name);
       this._applyPrefToSetting(zoomValue, browser);
     } else {
       var self = this;
       Services.contentPrefs.getPref(aURI, this.name, function (aResult) {
         // Check that we're still where we expect to be in case this took a while.
         // Null check currentURI, since the window may have been destroyed before
@@ -298,39 +304,40 @@ var FullZoom = {
    * one.
    **/
   _applyPrefToSetting: function FullZoom__applyPrefToSetting(aValue, aBrowser) {
     if ((!this.siteSpecific) || gInPrintPreviewMode)
       return;
 
     var browser = aBrowser || (gBrowser && gBrowser.selectedBrowser);
     try {
-      if (browser.contentDocument instanceof Ci.nsIImageDocument)
-        ZoomManager.setZoomForBrowser(browser, 1);
-      else if (typeof aValue != "undefined")
+      if (browser.contentDocument instanceof ImageDocument)
+        return;
+
+      if (typeof aValue != "undefined")
         ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
       else if (typeof this.globalValue != "undefined")
         ZoomManager.setZoomForBrowser(browser, this.globalValue);
       else
         ZoomManager.setZoomForBrowser(browser, 1);
     }
     catch(ex) {}
   },
 
   _applySettingToPref: function FullZoom__applySettingToPref() {
     if (!this.siteSpecific || gInPrintPreviewMode ||
-        content.document instanceof Ci.nsIImageDocument)
+        content.document instanceof ImageDocument)
       return;
 
     var zoomLevel = ZoomManager.zoom;
     Services.contentPrefs.setPref(gBrowser.currentURI, this.name, zoomLevel);
   },
 
   _removePref: function FullZoom__removePref() {
-    if (!(content.document instanceof Ci.nsIImageDocument))
+    if (!(content.document instanceof ImageDocument))
       Services.contentPrefs.removePref(gBrowser.currentURI, this.name);
   },
 
 
   //**************************************************************************//
   // Utilities
 
   _ensureValid: function FullZoom__ensureValid(aValue) {
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -140,16 +140,17 @@ endif
                  browser_bug561623.js \
                  browser_bug561636.js \
                  browser_bug562649.js \
                  browser_bug563588.js \
                  browser_bug565575.js \
                  browser_bug567306.js \
                  browser_zbug569342.js \
                  browser_bug575561.js \
+                 browser_bug575830.js \
                  browser_bug577121.js \
                  browser_bug578534.js \
                  browser_bug579872.js \
                  browser_bug580638.js \
                  browser_bug580956.js \
                  browser_bug581242.js \
                  browser_bug581253.js \
                  browser_bug581947.js \
--- a/browser/base/content/test/browser_bug386835.js
+++ b/browser/base/content/test/browser_bug386835.js
@@ -49,22 +49,20 @@ function thirdPageLoaded() {
     load(gTab1, gTestImage, imageLoaded);
   });
 
   gBrowser.selectedTab = gTab2;
 }
 
 function imageLoaded() {
   zoomTest(gTab1, 1, "Zoom should be 1 when image was loaded in the background");
-  afterZoom(function() {
-    zoomTest(gTab1, 1, "Zoom should still be 1 when tab with image is selected");
+  gBrowser.selectedTab = gTab1;
+  zoomTest(gTab1, 1, "Zoom should still be 1 when tab with image is selected");
 
-    executeSoon(imageZoomSwitch);
-  });
-  gBrowser.selectedTab = gTab1;
+  executeSoon(imageZoomSwitch);
 }
 
 function imageZoomSwitch() {
   navigate(BACK, function () {
     navigate(FORWARD, function () {
       zoomTest(gTab1, 1, "Tab 1 should not be zoomed when an image loads");
 
       afterZoom(function() {
@@ -131,17 +129,15 @@ function navigate(direction, cb) {
 
   if (direction == BACK)
     gBrowser.goBack();
   else if (direction == FORWARD)
     gBrowser.goForward();
 }
 
 function afterZoom(cb) {
-  let oldAPTS = FullZoom._applyPrefToSetting;
-  FullZoom._applyPrefToSetting = function(value, browser) {
-    if (!value)
-      value = undefined;
-    oldAPTS.call(FullZoom, value, browser);
-    FullZoom._applyPrefToSetting = oldAPTS;
+  let oldSZFB = ZoomManager.setZoomForBrowser;
+  ZoomManager.setZoomForBrowser = function(browser, value) {
+    oldSZFB.call(ZoomManager, browser, value);
+    ZoomManager.setZoomForBrowser = oldSZFB;
     executeSoon(cb);
   };
 }
--- a/browser/base/content/test/browser_bug416661.js
+++ b/browser/base/content/test/browser_bug416661.js
@@ -45,19 +45,17 @@ function test() {
 function afterZoomAndLoad(cb) {
   let didLoad = didZoom = false;
   tabElm.linkedBrowser.addEventListener("load", function() {
     tabElm.linkedBrowser.removeEventListener("load", arguments.callee, true);
     didLoad = true;
     if (didZoom)
       executeSoon(cb);
   }, true);
-  let oldAPTS = FullZoom._applyPrefToSetting;
-  FullZoom._applyPrefToSetting = function(value, browser) {
-    if (!value)
-      value = undefined;
-    oldAPTS.call(FullZoom, value, browser);
-    FullZoom._applyPrefToSetting = oldAPTS;
+  let oldSZFB = ZoomManager.setZoomForBrowser;
+  ZoomManager.setZoomForBrowser = function(browser, value) {
+    oldSZFB.call(ZoomManager, browser, value);
+    ZoomManager.setZoomForBrowser = oldSZFB;
     didZoom = true;
     if (didLoad)
       executeSoon(cb);
   };
 }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug575830.js
@@ -0,0 +1,37 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+function test() {
+  let tab1, tab2;
+  const TEST_IMAGE = "http://example.org/browser/browser/base/content/test/moz.png";
+
+  waitForExplicitFinish();
+  registerCleanupFunction(function cleanup() {
+    gBrowser.removeTab(tab1);
+    gBrowser.removeTab(tab2);
+  });
+
+  tab1 = gBrowser.addTab(TEST_IMAGE);
+  tab2 = gBrowser.addTab();
+  gBrowser.selectedTab = tab1;
+
+  tab1.linkedBrowser.addEventListener("load", function onload() {
+    tab1.linkedBrowser.removeEventListener("load", onload, true);
+    is(ZoomManager.zoom, 1, "initial zoom level for first should be 1");
+
+    FullZoom.enlarge();
+    let zoom = ZoomManager.zoom;
+    isnot(zoom, 1, "zoom level should have changed");
+
+    gBrowser.selectedTab = tab2;
+    is(ZoomManager.zoom, 1, "initial zoom level for second tab should be 1");
+
+    gBrowser.selectedTab = tab1;
+    is(ZoomManager.zoom, zoom, "zoom level for first tab should not have changed");
+
+    finish();
+  }, true);
+}
+
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -1266,17 +1266,17 @@ InplaceEditor.prototype = {
   /**
    * Size the editor to fit its current contents.
    */
   _updateSize: function InplaceEditor_updateSize()
   {
     // Replace spaces with non-breaking spaces.  Otherwise setting
     // the span's textContent will collapse spaces and the measurement
     // will be wrong.
-    this._measurement.textContent = this.input.value.replace(' ', '\u00a0', 'g');
+    this._measurement.textContent = this.input.value.replace(/ /g, '\u00a0');
 
     // We add a bit of padding to the end.  Should be enough to fit
     // any letter that could be typed, otherwise we'll scroll before
     // we get a chance to resize.  Yuck.
     let width = this._measurement.offsetWidth + 10;
 
     this.input.style.width = width + "px";
   },
--- a/build/macosx/mozconfig.leopard
+++ b/build/macosx/mozconfig.leopard
@@ -24,9 +24,9 @@ CXX="$CXX -arch $TARGET_CPU"
 NATIVE_CPU=`$topsrcdir/build/autoconf/config.guess | cut -f1 -d-`
 
 if test "$NATIVE_CPU" != "$TARGET_CPU" ; then
   CROSS_COMPILE=1
 fi
 
 # Note, the version (10) is used by libffi's configure.
 ac_add_options --target=i386-apple-darwin10
-ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.5.sdk
+ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.6.sdk
--- a/build/macosx/universal/mozconfig.common
+++ b/build/macosx/universal/mozconfig.common
@@ -37,18 +37,17 @@
 mk_add_options MOZ_UNIFY_BDATE=1
 
 mk_add_options MOZ_POSTFLIGHT_ALL+=build/macosx/universal/flight.mk
 
 # Note, the version (10) is used by libffi's configure.
 ac_add_app_options i386 --target=i386-apple-darwin10
 ac_add_app_options x86_64 --target=x86_64-apple-darwin10
 
-ac_add_app_options i386 --with-macos-sdk=/Developer/SDKs/MacOSX10.5.sdk
-ac_add_app_options x86_64 --with-macos-sdk=/Developer/SDKs/MacOSX10.6.sdk
+ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.6.sdk
 
 . $topsrcdir/build/macosx/common
 
 # $MOZ_BUILD_APP is only defined when sourced by configure.  That's not a
 # problem, because the variables it affects only need to be set for
 # configure.
 if test -n "$MOZ_BUILD_APP" ; then
 if test "$MOZ_BUILD_APP" = "i386" -o "$MOZ_BUILD_APP" = "x86_64"; then
new file mode 100755
--- /dev/null
+++ b/build/unix/build-toolchain/build-gcc.py
@@ -0,0 +1,167 @@
+#!/usr/bin/python
+
+import urllib
+import os
+import os.path
+import shutil
+import tarfile
+import subprocess
+
+def download_uri(uri):
+    fname = uri.split('/')[-1]
+    if (os.path.exists(fname)):
+        return fname
+    urllib.urlretrieve(uri, fname)
+    return fname
+
+def extract(tar, path):
+    t = tarfile.open(tar)
+    t.extractall(path)
+
+def check_run(args):
+    r = subprocess.call(args)
+    assert r == 0
+
+def run_in(path, args):
+    d = os.getcwd()
+    os.chdir(path)
+    check_run(args)
+    os.chdir(d)
+
+def patch(patch, plevel, srcdir):
+    patch = os.path.realpath(patch)
+    check_run(['patch', '-d', srcdir, '-p%s' % plevel, '-i', patch, '--fuzz=0',
+               '-s'])
+
+def build_package(package_source_dir, package_build_dir, configure_args):
+    os.mkdir(package_build_dir)
+    run_in(package_build_dir,
+           ["%s/configure" % package_source_dir] + configure_args)
+    run_in(package_build_dir, ["make", "-j8"])
+    run_in(package_build_dir, ["make", "install"])
+
+def build_binutils(base_dir, binutils_inst_dir):
+    binutils_build_dir = base_dir + '/binutils_build'
+    build_package(binutils_source_dir, binutils_build_dir,
+                  ["--prefix=%s" % binutils_inst_dir])
+
+# FIXME: factor this with build_binutils
+def build_tar(base_dir, tar_inst_dir):
+    tar_build_dir = base_dir + '/tar_build'
+    build_package(tar_source_dir, tar_build_dir,
+                  ["--prefix=%s" % tar_inst_dir])
+
+def build_one_stage(env, stage_dir):
+    old_env = os.environ.copy()
+    os.environ.update(env)
+    os.mkdir(stage_dir)
+
+    lib_inst_dir = stage_dir + '/libinst'
+
+    gmp_build_dir = stage_dir + '/gmp'
+    build_package(gmp_source_dir, gmp_build_dir,
+                  ["--prefix=%s" % lib_inst_dir, "--disable-shared"])
+    mpfr_build_dir = stage_dir + '/mpfr'
+    build_package(mpfr_source_dir, mpfr_build_dir,
+                  ["--prefix=%s" % lib_inst_dir, "--disable-shared",
+                   "--with-gmp=%s" % lib_inst_dir])
+    mpc_build_dir = stage_dir + '/mpc'
+    build_package(mpc_source_dir, mpc_build_dir,
+                  ["--prefix=%s" % lib_inst_dir, "--disable-shared",
+                   "--with-gmp=%s" % lib_inst_dir,
+                   "--with-mpfr=%s" % lib_inst_dir])
+
+    gcc_build_dir = stage_dir + '/gcc'
+    gcc_inst_dir = stage_dir + '/inst'
+    build_package(gcc_source_dir, gcc_build_dir,
+                  ["--prefix=%s" % gcc_inst_dir,
+                   "--enable-__cxa_atexit",
+                   "--with-gmp=%s" % lib_inst_dir,
+                   "--with-mpfr=%s" % lib_inst_dir,
+                   "--with-mpc=%s" % lib_inst_dir,
+                   "--enable-languages=c,c++",
+                   "--disable-bootstrap"])
+    os.environ.clear()
+    os.environ.update(old_env)
+
+def build_tar_package(tar, name, base, directory):
+    name = os.path.realpath(name)
+    run_in(base, [tar, "-cf", name, "--mtime=2012-01-01", "--owner=root",
+                  directory])
+
+##############################################
+
+source_dir = os.path.realpath('src')
+
+def build_source_dir(prefix, version):
+    return source_dir + '/' + prefix + version
+
+binutils_version = "2.21.1"
+tar_version = "1.26"
+gcc_version = "4.5.2"
+mpfr_version = "2.4.2"
+gmp_version = "5.0.1"
+mpc_version = "0.8.1"
+
+binutils_source_uri = "http://ftp.gnu.org/gnu/binutils/binutils-%sa.tar.bz2" % \
+    binutils_version
+tar_source_uri = "http://ftp.gnu.org/gnu/tar/tar-%s.tar.bz2" % \
+    tar_version
+gcc_source_uri = "http://ftp.gnu.org/gnu/gcc/gcc-%s/gcc-%s.tar.bz2" % \
+    (gcc_version, gcc_version)
+mpfr_source_uri = "http://www.mpfr.org/mpfr-%s/mpfr-%s.tar.bz2" % \
+    (mpfr_version, mpfr_version)
+gmp_source_uri = "http://ftp.gnu.org/gnu/gmp/gmp-%s.tar.bz2" % gmp_version
+mpc_source_uri = "http://www.multiprecision.org/mpc/download/mpc-%s.tar.gz" % \
+    mpc_version
+
+binutils_source_tar = download_uri(binutils_source_uri)
+tar_source_tar = download_uri(tar_source_uri)
+mpc_source_tar = download_uri(mpc_source_uri)
+mpfr_source_tar = download_uri(mpfr_source_uri)
+gmp_source_tar = download_uri(gmp_source_uri)
+gcc_source_tar = download_uri(gcc_source_uri)
+
+build_dir = os.path.realpath('build')
+
+binutils_source_dir  = build_source_dir('binutils-', binutils_version)
+tar_source_dir  = build_source_dir('tar-', tar_version)
+mpc_source_dir  = build_source_dir('mpc-', mpc_version)
+mpfr_source_dir = build_source_dir('mpfr-', mpfr_version)
+gmp_source_dir  = build_source_dir('gmp-', gmp_version)
+gcc_source_dir  = build_source_dir('gcc-', gcc_version)
+
+if not os.path.exists(source_dir):
+    os.mkdir(source_dir)
+    extract(binutils_source_tar, source_dir)
+    extract(tar_source_tar, source_dir)
+    extract(mpc_source_tar, source_dir)
+    extract(mpfr_source_tar, source_dir)
+    extract(gmp_source_tar, source_dir)
+    extract(gcc_source_tar, source_dir)
+    patch('plugin_finish_decl.diff', 0, gcc_source_dir)
+    patch('pr49911.diff', 1, gcc_source_dir)
+    patch('r159628-r163231-r171807.patch', 1, gcc_source_dir)
+
+if os.path.exists(build_dir):
+    shutil.rmtree(build_dir)
+os.mkdir(build_dir)
+
+tools_inst_dir = build_dir + '/tools_inst'
+build_binutils(build_dir, tools_inst_dir)
+build_tar(build_dir, tools_inst_dir)
+
+os.environ["AR"] = os.path.realpath('det-ar.sh')
+os.environ["MOZ_AR"] = tools_inst_dir + '/bin/ar'
+os.environ["RANLIB"] = "true"
+
+stage1_dir = build_dir + '/stage1'
+build_one_stage({"CC": "gcc", "CXX" : "g++"}, stage1_dir)
+
+stage1_gcc_inst_dir = stage1_dir + '/inst'
+stage2_dir = build_dir + '/stage2'
+build_one_stage({"CC"  : stage1_gcc_inst_dir + "/bin/gcc",
+                 "CXX" : stage1_gcc_inst_dir + "/bin/g++"}, stage2_dir)
+
+build_tar_package(tools_inst_dir + "/bin/tar",
+                  "toolchain.tar", stage2_dir, "inst")
new file mode 100755
--- /dev/null
+++ b/build/unix/build-toolchain/det-ar.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+shift
+echo $MOZ_AR "crD" "$@"
+exec $MOZ_AR "crD" "$@"
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -143,16 +143,17 @@ MOZ_UPDATER	= @MOZ_UPDATER@
 MOZ_UPDATE_CHANNEL	= @MOZ_UPDATE_CHANNEL@
 MOZ_UPDATE_PACKAGING	= @MOZ_UPDATE_PACKAGING@
 MOZ_DISABLE_PARENTAL_CONTROLS = @MOZ_DISABLE_PARENTAL_CONTROLS@
 NS_ENABLE_TSF = @NS_ENABLE_TSF@
 MOZ_SPELLCHECK = @MOZ_SPELLCHECK@
 MOZ_ANDROID_HISTORY = @MOZ_ANDROID_HISTORY@
 MOZ_WEBSMS_BACKEND = @MOZ_WEBSMS_BACKEND@
 MOZ_JAVA_COMPOSITOR = @MOZ_JAVA_COMPOSITOR@
+MOZ_TOUCH = @MOZ_TOUCH@
 MOZ_PROFILELOCKING = @MOZ_PROFILELOCKING@
 MOZ_FEEDS = @MOZ_FEEDS@
 MOZ_TOOLKIT_SEARCH = @MOZ_TOOLKIT_SEARCH@
 MOZ_PLACES = @MOZ_PLACES@
 MOZ_SAFE_BROWSING = @MOZ_SAFE_BROWSING@
 MOZ_URL_CLASSIFIER = @MOZ_URL_CLASSIFIER@
 MOZ_ZIPWRITER = @MOZ_ZIPWRITER@
 MOZ_OGG = @MOZ_OGG@
--- a/configure.in
+++ b/configure.in
@@ -561,16 +561,21 @@ if test -n "$CROSS_COMPILE" -a "$target"
     AC_CHECK_PROGS(AR, $AR "${target_alias}-ar" "${target}-ar", :)
     MOZ_PATH_PROGS(AS, $AS "${target_alias}-as" "${target}-as", :)
     AC_CHECK_PROGS(LD, $LD "${target_alias}-ld" "${target}-ld", :)
     AC_CHECK_PROGS(STRIP, $STRIP "${target_alias}-strip" "${target}-strip", :)
     AC_CHECK_PROGS(WINDRES, $WINDRES "${target_alias}-windres" "${target}-windres", :)
     AC_DEFINE(CROSS_COMPILE)
 else
     AC_PROG_CC
+    case "$target" in
+    *-mingw*)
+      # Work around the conftest.exe access problem on Windows
+      sleep 1
+    esac
     AC_PROG_CXX
     AC_PROG_RANLIB
     MOZ_PATH_PROGS(AS, $AS as, $CC)
     AC_CHECK_PROGS(AR, ar, :)
     AC_CHECK_PROGS(LD, ld, :)
     AC_CHECK_PROGS(STRIP, strip, :)
     AC_CHECK_PROGS(WINDRES, windres, :)
     if test -z "$HOST_CC"; then
@@ -4892,23 +4897,25 @@ cairo-uikit)
     CXXFLAGS="$CXXFLAGS $TK_CFLAGS"
     LIBXUL_LIBS='$(XPCOM_FROZEN_LDOPTS) $(LIBXUL_DIST)/bin/XUL'
     MOZ_USER_DIR="Mozilla"
     MOZ_FS_LAYOUT=bundle
     ;;
 
 cairo-android)
     AC_DEFINE(MOZ_WIDGET_ANDROID)
+    AC_DEFINE(MOZ_TOUCH)
     MOZ_WIDGET_TOOLKIT=android
     TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)'
     TK_LIBS='$(MOZ_CAIRO_LIBS)'
     MOZ_WEBGL=1
     MOZ_PDF_PRINTING=1
     MOZ_INSTRUMENT_EVENT_LOOP=1
     MOZ_OLD_LINKER=1
+    MOZ_TOUCH=1
     ;;
 
 cairo-gonk)
     AC_DEFINE(MOZ_WIDGET_GONK)
     MOZ_WIDGET_TOOLKIT=gonk
     TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)'
     TK_LIBS='$(MOZ_CAIRO_LIBS)'
     MOZ_WEBGL=1
--- a/content/events/public/nsEventNameList.h
+++ b/content/events/public/nsEventNameList.h
@@ -444,39 +444,39 @@ WINDOW_ONLY_EVENT(devicemotion,
                   EventNameType_None,
                   NS_EVENT)
 WINDOW_ONLY_EVENT(deviceorientation,
                   NS_DEVICE_ORIENTATION,
                   EventNameType_None,
                   NS_EVENT)
 
 TOUCH_EVENT(touchstart,
-            NS_USER_DEFINED_EVENT,
+            NS_TOUCH_START,
             EventNameType_All,
-            NS_INPUT_EVENT)
+            NS_TOUCH_EVENT)
 TOUCH_EVENT(touchend,
-            NS_USER_DEFINED_EVENT,
+            NS_TOUCH_END,
             EventNameType_All,
-            NS_INPUT_EVENT)
+            NS_TOUCH_EVENT)
 TOUCH_EVENT(touchmove,
-            NS_USER_DEFINED_EVENT,
+            NS_TOUCH_MOVE,
             EventNameType_All,
-            NS_INPUT_EVENT )
+            NS_TOUCH_EVENT )
 TOUCH_EVENT(touchenter,
-            NS_USER_DEFINED_EVENT,
+            NS_TOUCH_ENTER,
             EventNameType_All,
-            NS_INPUT_EVENT )
+            NS_TOUCH_EVENT )
 TOUCH_EVENT(touchleave,
-            NS_USER_DEFINED_EVENT,
+            NS_TOUCH_LEAVE,
             EventNameType_All,
-            NS_INPUT_EVENT)
+            NS_TOUCH_EVENT)
 TOUCH_EVENT(touchcancel,
-            NS_USER_DEFINED_EVENT,
+            NS_TOUCH_CANCEL,
             EventNameType_All,
-            NS_INPUT_EVENT)
+            NS_TOUCH_EVENT)
 
 DOCUMENT_ONLY_EVENT(readystatechange,
                     NS_READYSTATECHANGE,
                     EventNameType_HTMLXUL,
                     NS_EVENT_NULL)
 
 NON_IDL_EVENT(MozMouseHittest,
               NS_MOUSE_MOZHITTEST,
--- a/content/events/public/nsIPrivateDOMEvent.h
+++ b/content/events/public/nsIPrivateDOMEvent.h
@@ -136,17 +136,17 @@ nsresult
 NS_NewDOMTransitionEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsTransitionEvent* aEvent);
 nsresult
 NS_NewDOMAnimationEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsAnimationEvent* aEvent);
 nsresult
 NS_NewDOMCloseEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsEvent* aEvent);
 nsresult
 NS_NewDOMMozTouchEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsMozTouchEvent* aEvent);
 nsresult
-NS_NewDOMTouchEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsInputEvent *aEvent);
+NS_NewDOMTouchEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsTouchEvent *aEvent);
 nsresult
 NS_NewDOMCustomEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent* aEvent);
 nsresult
 NS_NewDOMSmsEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent* aEvent);
 nsresult
 NS_NewDOMPopStateEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent* aEvent);
 nsresult
 NS_NewDOMHashChangeEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent* aEvent);
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -58,16 +58,18 @@
 #include "nsContentUtils.h"
 #include "nsIURI.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptError.h"
 #include "nsDOMPopStateEvent.h"
 #include "mozilla/Preferences.h"
 #include "nsJSUtils.h"
 #include "DictionaryHelpers.h"
+#include "nsLayoutUtils.h"
+#include "nsIScrollableFrame.h"
 
 using namespace mozilla;
 
 static const char* const sEventNames[] = {
   "mousedown", "mouseup", "click", "dblclick", "mouseenter", "mouseleave", "mouseover",
   "mouseout", "MozMouseHittest", "mousemove", "contextmenu", "keydown", "keyup", "keypress",
   "focus", "blur", "load", "popstate", "beforescriptexecute",
   "afterscriptexecute", "beforeunload", "unload",
@@ -105,16 +107,22 @@ static const char* const sEventNames[] =
   "MozRotateGestureStart",
   "MozRotateGestureUpdate",
   "MozRotateGesture",
   "MozTapGesture",
   "MozPressTapGesture",
   "MozTouchDown",
   "MozTouchMove",
   "MozTouchUp",
+  "touchstart",
+  "touchend",
+  "touchmove",
+  "touchcancel",
+  "touchenter",
+  "touchleave",
   "MozScrolledAreaChanged",
   "transitionend",
   "animationstart",
   "animationend",
   "animationiteration",
   "devicemotion",
   "deviceorientation"
 };
@@ -897,16 +905,23 @@ NS_METHOD nsDOMEvent::DuplicatePrivateDa
     case NS_MOZTOUCH_EVENT:
     {
       newEvent = new nsMozTouchEvent(false, msg, nsnull,
                                      static_cast<nsMozTouchEvent*>(mEvent)->streamId);
       NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
       isInputEvent = true;
       break;
     }
+    case NS_TOUCH_EVENT:
+    {
+      newEvent = new nsTouchEvent(false, msg, nsnull);
+      NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
+      isInputEvent = true;
+      break;
+    }
     default:
     {
       NS_WARNING("Unknown event type!!!");
       return NS_ERROR_FAILURE;
     }
   }
 
   NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
@@ -1160,16 +1175,102 @@ nsDOMEvent::PopupAllowedEventsChanged()
 void
 nsDOMEvent::Shutdown()
 {
   if (sPopupAllowedEvents) {
     nsMemory::Free(sPopupAllowedEvents);
   }
 }
 
+nsIntPoint
+nsDOMEvent::GetScreenCoords(nsPresContext* aPresContext,
+                            nsEvent* aEvent,
+                            nsIntPoint aPoint)
+{
+  if (!aEvent || 
+       (aEvent->eventStructType != NS_MOUSE_EVENT &&
+        aEvent->eventStructType != NS_POPUP_EVENT &&
+        aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
+        aEvent->eventStructType != NS_MOZTOUCH_EVENT &&
+        aEvent->eventStructType != NS_TOUCH_EVENT &&
+        aEvent->eventStructType != NS_DRAG_EVENT &&
+        aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT)) {
+    return nsIntPoint(0, 0);
+  }
+
+  nsGUIEvent* guiEvent = static_cast<nsGUIEvent*>(aEvent);
+  if (!guiEvent->widget) {
+    return aPoint;
+  }
+
+  nsIntPoint offset = aPoint + guiEvent->widget->WidgetToScreenOffset();
+  nscoord factor = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel();
+  return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(offset.x * factor),
+                    nsPresContext::AppUnitsToIntCSSPixels(offset.y * factor));
+}
+
+//static
+nsIntPoint
+nsDOMEvent::GetPageCoords(nsPresContext* aPresContext,
+                          nsEvent* aEvent,
+                          nsIntPoint aPoint,
+                          nsIntPoint aDefaultPoint)
+{
+  nsIntPoint pagePoint = nsDOMEvent::GetClientCoords(aPresContext,
+                                                     aEvent,
+                                                     aPoint,
+                                                     aDefaultPoint);
+
+  // If there is some scrolling, add scroll info to client point.
+  if (aPresContext && aPresContext->GetPresShell()) {
+    nsIPresShell* shell = aPresContext->GetPresShell();
+    nsIScrollableFrame* scrollframe = shell->GetRootScrollFrameAsScrollable();
+    if (scrollframe) {
+      nsPoint pt = scrollframe->GetScrollPosition();
+      pagePoint += nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x),
+                              nsPresContext::AppUnitsToIntCSSPixels(pt.y));
+    }
+  }
+
+  return pagePoint;
+}
+
+// static
+nsIntPoint
+nsDOMEvent::GetClientCoords(nsPresContext* aPresContext,
+                            nsEvent* aEvent,
+                            nsIntPoint aPoint,
+                            nsIntPoint aDefaultPoint)
+{
+  if (!aEvent ||
+      (aEvent->eventStructType != NS_MOUSE_EVENT &&
+       aEvent->eventStructType != NS_POPUP_EVENT &&
+       aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
+       aEvent->eventStructType != NS_MOZTOUCH_EVENT &&
+       aEvent->eventStructType != NS_TOUCH_EVENT &&
+       aEvent->eventStructType != NS_DRAG_EVENT &&
+       aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) ||
+      !aPresContext ||
+      !((nsGUIEvent*)aEvent)->widget) {
+    return aDefaultPoint;
+  }
+
+  nsPoint pt(0, 0);
+  nsIPresShell* shell = aPresContext->GetPresShell();
+  if (!shell) {
+    return nsIntPoint(0, 0);
+  }
+  nsIFrame* rootFrame = shell->GetRootFrame();
+  if (rootFrame)
+    pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, aPoint, rootFrame);
+
+  return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x),
+                    nsPresContext::AppUnitsToIntCSSPixels(pt.y));
+}
+
 // To be called ONLY by nsDOMEvent::GetType (which has the additional
 // logic for handling user-defined events).
 // static
 const char* nsDOMEvent::GetEventName(PRUint32 aEventType)
 {
   switch(aEventType) {
   case NS_MOUSE_BUTTON_DOWN:
     return sEventNames[eDOMEvents_mousedown];
@@ -1342,16 +1443,28 @@ const char* nsDOMEvent::GetEventName(PRU
   case NS_SVG_ERROR:
     return sEventNames[eDOMEvents_SVGError];
   case NS_SVG_RESIZE:
     return sEventNames[eDOMEvents_SVGResize];
   case NS_SVG_SCROLL:
     return sEventNames[eDOMEvents_SVGScroll];
   case NS_SVG_ZOOM:
     return sEventNames[eDOMEvents_SVGZoom];
+  case NS_TOUCH_START:
+    return sEventNames[eDOMEvents_touchstart];
+  case NS_TOUCH_MOVE:
+    return sEventNames[eDOMEvents_touchmove];
+  case NS_TOUCH_END:
+    return sEventNames[eDOMEvents_touchend];
+  case NS_TOUCH_ENTER:
+    return sEventNames[eDOMEvents_touchenter];
+  case NS_TOUCH_LEAVE:
+    return sEventNames[eDOMEvents_touchleave];
+  case NS_TOUCH_CANCEL:
+    return sEventNames[eDOMEvents_touchcancel];
   case NS_SMIL_BEGIN:
     return sEventNames[eDOMEvents_beginEvent];
   case NS_SMIL_END:
     return sEventNames[eDOMEvents_endEvent];
   case NS_SMIL_REPEAT:
     return sEventNames[eDOMEvents_repeatEvent];
 #ifdef MOZ_MEDIA
   case NS_LOADSTART:
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -190,16 +190,22 @@ public:
     eDOMEvents_MozRotateGestureStart,
     eDOMEvents_MozRotateGestureUpdate,
     eDOMEvents_MozRotateGesture,
     eDOMEvents_MozTapGesture,
     eDOMEvents_MozPressTapGesture,
     eDOMEvents_MozTouchDown,
     eDOMEvents_MozTouchMove,
     eDOMEvents_MozTouchUp,
+    eDOMEvents_touchstart,
+    eDOMEvents_touchend,
+    eDOMEvents_touchmove,
+    eDOMEvents_touchcancel,
+    eDOMEvents_touchenter,
+    eDOMEvents_touchleave,
     eDOMEvents_MozScrolledAreaChanged,
     eDOMEvents_transitionend,
     eDOMEvents_animationstart,
     eDOMEvents_animationend,
     eDOMEvents_animationiteration,
     eDOMEvents_devicemotion,
     eDOMEvents_deviceorientation
   };
@@ -237,16 +243,27 @@ public:
 
   static PopupControlState GetEventPopupControlState(nsEvent *aEvent);
 
   static void PopupAllowedEventsChanged();
 
   static void Shutdown();
 
   static const char* GetEventName(PRUint32 aEventType);
+  static nsIntPoint GetClientCoords(nsPresContext* aPresContext,
+                                    nsEvent* aEvent,
+                                    nsIntPoint aPoint,
+                                    nsIntPoint aDefaultPoint);
+  static nsIntPoint GetPageCoords(nsPresContext* aPresContext,
+                                  nsEvent* aEvent,
+                                  nsIntPoint aPoint,
+                                  nsIntPoint aDefaultPoint);
+  static nsIntPoint GetScreenCoords(nsPresContext* aPresContext,
+                                    nsEvent* aEvent,
+                                    nsIntPoint aPoint);
 protected:
 
   // Internal helper functions
   nsresult SetEventType(const nsAString& aEventTypeArg);
   already_AddRefed<nsIContent> GetTargetFromFrame();
 
   nsEvent*                    mEvent;
   nsRefPtr<nsPresContext>     mPresContext;
--- a/content/events/src/nsDOMMouseEvent.cpp
+++ b/content/events/src/nsDOMMouseEvent.cpp
@@ -231,41 +231,67 @@ nsDOMMouseEvent::GetRelatedTarget(nsIDOM
     CallQueryInterface(relatedTarget, aRelatedTarget);
   }
   return NS_OK;
 }
 
 NS_METHOD nsDOMMouseEvent::GetScreenX(PRInt32* aScreenX)
 {
   NS_ENSURE_ARG_POINTER(aScreenX);
+#ifdef MOZ_TOUCH
+  *aScreenX = nsDOMEvent::GetScreenCoords(mPresContext,
+                                          mEvent,
+                                          mEvent->refPoint).x;
+#else
   *aScreenX = GetScreenPoint().x;
+#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMMouseEvent::GetScreenY(PRInt32* aScreenY)
 {
   NS_ENSURE_ARG_POINTER(aScreenY);
+#ifdef MOZ_TOUCH
+  *aScreenY = nsDOMEvent::GetScreenCoords(mPresContext,
+                                          mEvent,
+                                          mEvent->refPoint).y;
+#else
   *aScreenY = GetScreenPoint().y;
+#endif
   return NS_OK;
 }
 
 
 NS_METHOD nsDOMMouseEvent::GetClientX(PRInt32* aClientX)
 {
   NS_ENSURE_ARG_POINTER(aClientX);
+#ifdef MOZ_TOUCH
+  *aClientX = nsDOMEvent::GetClientCoords(mPresContext,
+                                          mEvent,
+                                          mEvent->refPoint,
+                                          mClientPoint).x;
+#else
   *aClientX = GetClientPoint().x;
+#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMMouseEvent::GetClientY(PRInt32* aClientY)
 {
   NS_ENSURE_ARG_POINTER(aClientY);
+#ifdef MOZ_TOUCH
+  *aClientY = nsDOMEvent::GetClientCoords(mPresContext,
+                                          mEvent,
+                                          mEvent->refPoint,
+                                          mClientPoint).y;
+#else
   *aClientY = GetClientPoint().y;
+#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMMouseEvent::GetAltKey(bool* aIsDown)
 {
   NS_ENSURE_ARG_POINTER(aIsDown);
   *aIsDown = ((nsInputEvent*)mEvent)->isAlt;
--- a/content/events/src/nsDOMTouchEvent.cpp
+++ b/content/events/src/nsDOMTouchEvent.cpp
@@ -37,16 +37,17 @@
 
 #include "nsDOMTouchEvent.h"
 #include "nsGUIEvent.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIClassInfo.h"
 #include "nsIXPCScriptable.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
+#include "nsPresContext.h"
 
 using namespace mozilla;
 
 DOMCI_DATA(Touch, nsDOMTouch)
 
 NS_IMPL_CYCLE_COLLECTION_1(nsDOMTouch, mTarget)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMTouch)
@@ -70,66 +71,66 @@ nsDOMTouch::GetTarget(nsIDOMEventTarget*
 {
   NS_IF_ADDREF(*aTarget = mTarget);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouch::GetScreenX(PRInt32* aScreenX)
 {
-  *aScreenX = mScreenX;
+  *aScreenX = mScreenPoint.x;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouch::GetScreenY(PRInt32* aScreenY)
 {
-  *aScreenY = mScreenY;
+  *aScreenY = mScreenPoint.y;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouch::GetClientX(PRInt32* aClientX)
 {
-  *aClientX = mClientX;
+  *aClientX = mClientPoint.x;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouch::GetClientY(PRInt32* aClientY)
 {
-  *aClientY = mClientY;
+  *aClientY = mClientPoint.y;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouch::GetPageX(PRInt32* aPageX)
 {
-  *aPageX = mPageX;
+  *aPageX = mPagePoint.x;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouch::GetPageY(PRInt32* aPageY)
 {
-  *aPageY = mPageY;
+  *aPageY = mPagePoint.y;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouch::GetRadiusX(PRInt32* aRadiusX)
 {
-  *aRadiusX = mRadiusX;
+  *aRadiusX = mRadius.x;
   return NS_OK;
 }
                                              
 NS_IMETHODIMP
 nsDOMTouch::GetRadiusY(PRInt32* aRadiusY)
 {
-  *aRadiusY = mRadiusY;
+  *aRadiusY = mRadius.y;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouch::GetRotationAngle(float* aRotationAngle)
 {
   *aRotationAngle = mRotationAngle;
   return NS_OK;
@@ -137,88 +138,114 @@ nsDOMTouch::GetRotationAngle(float* aRot
 
 NS_IMETHODIMP
 nsDOMTouch::GetForce(float* aForce)
 {
   *aForce = mForce;
   return NS_OK;
 }
 
+bool
+nsDOMTouch::Equals(nsIDOMTouch* aTouch)
+{
+  float force;
+  float orientation;
+  PRInt32 radiusX, radiusY;
+  aTouch->GetForce(&force);
+  aTouch->GetRotationAngle(&orientation);
+  aTouch->GetRadiusX(&radiusX);
+  aTouch->GetRadiusY(&radiusY);
+  return mRefPoint != aTouch->mRefPoint ||
+         (mForce != force) ||
+         (mRotationAngle != orientation) ||
+         (mRadius.x != radiusX) || (mRadius.y != radiusY);
+}
+
 // TouchList
+nsDOMTouchList::nsDOMTouchList(nsTArray<nsCOMPtr<nsIDOMTouch> > &aTouches)
+{
+  mPoints.AppendElements(aTouches);
+}
 
 DOMCI_DATA(TouchList, nsDOMTouchList)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMTouchList)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMTouchList)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMTouchList)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TouchList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMTouchList)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPoints)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_OF_NSCOMPTR(mPoints)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMTouchList)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPoints)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mPoints)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTouchList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTouchList)
 
 NS_IMETHODIMP
 nsDOMTouchList::GetLength(PRUint32* aLength)
 {
-  *aLength = mPoints.Count();
+  *aLength = mPoints.Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouchList::Item(PRUint32 aIndex, nsIDOMTouch** aRetVal)
 {
-  NS_IF_ADDREF(*aRetVal = mPoints.SafeObjectAt(aIndex));
+  NS_IF_ADDREF(*aRetVal = mPoints.SafeElementAt(aIndex, nsnull));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouchList::IdentifiedTouch(PRInt32 aIdentifier, nsIDOMTouch** aRetVal)
 {
   *aRetVal = nsnull;
-  for (PRInt32 i = 0; i < mPoints.Count(); ++i) {
+  for (PRUint32 i = 0; i < mPoints.Length(); ++i) {
     nsCOMPtr<nsIDOMTouch> point = mPoints[i];
     PRInt32 identifier;
     if (point && NS_SUCCEEDED(point->GetIdentifier(&identifier)) &&
         aIdentifier == identifier) {
       point.swap(*aRetVal);
       break;
     }
   }
   return NS_OK;
 }
 
 // TouchEvent
 
 nsDOMTouchEvent::nsDOMTouchEvent(nsPresContext* aPresContext,
-                                 nsInputEvent* aEvent)
+                                 nsTouchEvent* aEvent)
   : nsDOMUIEvent(aPresContext, aEvent ? aEvent :
-                                        new nsInputEvent(false, 0, nsnull))
+                                        new nsTouchEvent(false, 0, nsnull))
 {
   if (aEvent) {
     mEventIsInternal = false;
+
+    for (PRUint32 i = 0; i < aEvent->touches.Length(); ++i) {
+      nsIDOMTouch *touch = aEvent->touches[i];
+      nsDOMTouch *domtouch = static_cast<nsDOMTouch*>(touch);
+      domtouch->InitializePoints(mPresContext, aEvent);
+    }
   } else {
     mEventIsInternal = true;
     mEvent->time = PR_Now();
   }
 }
 
 nsDOMTouchEvent::~nsDOMTouchEvent()
 {
   if (mEventIsInternal && mEvent) {
-    delete static_cast<nsInputEvent*>(mEvent);
+    delete static_cast<nsTouchEvent*>(mEvent);
     mEvent = nsnull;
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMTouchEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMTouchEvent, nsDOMUIEvent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTouches)
@@ -252,48 +279,110 @@ nsDOMTouchEvent::InitTouchEvent(const ns
                                 bool aCtrlKey,
                                 bool aAltKey,
                                 bool aShiftKey,
                                 bool aMetaKey,
                                 nsIDOMTouchList* aTouches,
                                 nsIDOMTouchList* aTargetTouches,
                                 nsIDOMTouchList* aChangedTouches)
 {
-  nsresult rv = nsDOMUIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, aDetail);
+  nsresult rv = nsDOMUIEvent::InitUIEvent(aType,
+                                          aCanBubble,
+                                          aCancelable,
+                                          aView,
+                                          aDetail);
   NS_ENSURE_SUCCESS(rv, rv);
 
   static_cast<nsInputEvent*>(mEvent)->isControl = aCtrlKey;
   static_cast<nsInputEvent*>(mEvent)->isAlt = aAltKey;
   static_cast<nsInputEvent*>(mEvent)->isShift = aShiftKey;
   static_cast<nsInputEvent*>(mEvent)->isMeta = aMetaKey;
   mTouches = aTouches;
   mTargetTouches = aTargetTouches;
   mChangedTouches = aChangedTouches;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouchEvent::GetTouches(nsIDOMTouchList** aTouches)
 {
-  NS_IF_ADDREF(*aTouches = mTouches);
-  return NS_OK;
+  NS_ENSURE_ARG_POINTER(aTouches);
+  NS_ENSURE_STATE(mEvent);
+  nsRefPtr<nsDOMTouchList> t;
+
+  if (mTouches) {
+    return CallQueryInterface(mTouches, aTouches);
+  }
+
+  nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(mEvent);
+  if (mEvent->message == NS_TOUCH_END || mEvent->message == NS_TOUCH_CANCEL) {
+    // for touchend events, remove any changed touches from the touches array
+    nsTArray<nsCOMPtr<nsIDOMTouch> > unchangedTouches;
+    nsTArray<nsCOMPtr<nsIDOMTouch> > touches = touchEvent->touches;
+    for (PRUint32 i = 0; i < touches.Length(); ++i) {
+      if (!touches[i]->mChanged) {
+        unchangedTouches.AppendElement(touches[i]);
+      }
+    }
+    t = new nsDOMTouchList(unchangedTouches);
+  } else {
+    t = new nsDOMTouchList(touchEvent->touches);
+  }
+  mTouches = t;
+  return CallQueryInterface(mTouches, aTouches);
 }
 
 NS_IMETHODIMP
 nsDOMTouchEvent::GetTargetTouches(nsIDOMTouchList** aTargetTouches)
 {
-  NS_IF_ADDREF(*aTargetTouches = mTargetTouches);
-  return NS_OK;
+  NS_ENSURE_ARG_POINTER(aTargetTouches);
+  NS_ENSURE_STATE(mEvent);
+
+  if (mTargetTouches) {
+    return CallQueryInterface(mTargetTouches, aTargetTouches);
+  }
+
+  nsTArray<nsCOMPtr<nsIDOMTouch> > targetTouches;
+  nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(mEvent);
+  nsTArray<nsCOMPtr<nsIDOMTouch> > touches = touchEvent->touches;
+  for (PRUint32 i = 0; i < touches.Length(); ++i) {
+    // for touchend/cancel events, don't append to the target list if this is a
+    // touch that is ending
+    if ((mEvent->message != NS_TOUCH_END &&
+         mEvent->message != NS_TOUCH_CANCEL) || !touches[i]->mChanged) {
+      nsIDOMEventTarget* targetPtr = touches[i]->GetTarget();
+      if (targetPtr == mEvent->target) {
+        targetTouches.AppendElement(touches[i]);
+      }
+    }
+  }
+  mTargetTouches = new nsDOMTouchList(targetTouches);
+  return CallQueryInterface(mTargetTouches, aTargetTouches);
 }
 
 NS_IMETHODIMP
 nsDOMTouchEvent::GetChangedTouches(nsIDOMTouchList** aChangedTouches)
 {
-  NS_IF_ADDREF(*aChangedTouches = mChangedTouches);
-  return NS_OK;
+  NS_ENSURE_ARG_POINTER(aChangedTouches);
+  NS_ENSURE_STATE(mEvent);
+
+  if (mChangedTouches) {
+    return CallQueryInterface(mChangedTouches, aChangedTouches);
+  }
+
+  nsTArray<nsCOMPtr<nsIDOMTouch> > changedTouches;
+  nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(mEvent);
+  nsTArray<nsCOMPtr<nsIDOMTouch> > touches = touchEvent->touches;
+  for (PRUint32 i = 0; i < touches.Length(); ++i) {
+    if (touches[i]->mChanged) {
+      changedTouches.AppendElement(touches[i]);
+    }
+  }
+  mChangedTouches = new nsDOMTouchList(changedTouches);
+  return CallQueryInterface(mChangedTouches, aChangedTouches);
 }
 
 NS_IMETHODIMP
 nsDOMTouchEvent::GetAltKey(bool* aAltKey)
 {
   *aAltKey = static_cast<nsInputEvent*>(mEvent)->isAlt;
   return NS_OK;
 }
@@ -332,14 +421,14 @@ nsDOMTouchEvent::PrefEnabled()
     }
   }
   return sPrefValue;
 }
 
 nsresult
 NS_NewDOMTouchEvent(nsIDOMEvent** aInstancePtrResult,
                     nsPresContext* aPresContext,
-                    nsInputEvent *aEvent)
+                    nsTouchEvent *aEvent)
 {
   nsDOMTouchEvent* it = new nsDOMTouchEvent(aPresContext, aEvent);
 
   return CallQueryInterface(it, aInstancePtrResult);
 }
--- a/content/events/src/nsDOMTouchEvent.h
+++ b/content/events/src/nsDOMTouchEvent.h
@@ -35,89 +35,131 @@
  *
  * ***** END LICENSE BLOCK ***** */
 #ifndef nsDOMTouchEvent_h_
 #define nsDOMTouchEvent_h_
 
 #include "nsDOMUIEvent.h"
 #include "nsIDOMTouchEvent.h"
 #include "nsString.h"
-#include "nsCOMArray.h"
+#include "nsTArray.h"
 
 class nsDOMTouch : public nsIDOMTouch
 {
 public:
   nsDOMTouch(nsIDOMEventTarget* aTarget,
              PRInt32 aIdentifier,
              PRInt32 aPageX,
              PRInt32 aPageY,
              PRInt32 aScreenX,
              PRInt32 aScreenY,
              PRInt32 aClientX,
              PRInt32 aClientY,
              PRInt32 aRadiusX,
              PRInt32 aRadiusY,
              float aRotationAngle,
              float aForce)
-  : mTarget(aTarget),
-    mIdentifier(aIdentifier),
-    mPageX(aPageX),
-    mPageY(aPageY),
-    mScreenX(aScreenX),
-    mScreenY(aScreenY),
-    mClientX(aClientX),
-    mClientY(aClientY),
-    mRadiusX(aRadiusX),
-    mRadiusY(aRadiusY),
-    mRotationAngle(aRotationAngle),
-    mForce(aForce)
-    {}
+    {
+      mTarget = aTarget;
+      mIdentifier = aIdentifier;
+      mPagePoint = nsIntPoint(aPageX, aPageY);
+      mScreenPoint = nsIntPoint(aScreenX, aScreenY);
+      mClientPoint = nsIntPoint(aClientX, aClientY);
+      mRefPoint = nsIntPoint(0, 0);
+      mPointsInitialized = true;
+      mRadius.x = aRadiusX;
+      mRadius.y = aRadiusY;
+      mRotationAngle = aRotationAngle;
+      mForce = aForce;
+
+      mChanged = false;
+      mMessage = 0;
+    }
+  nsDOMTouch(PRInt32 aIdentifier,
+             nsIntPoint aPoint,
+             nsIntPoint aRadius,
+             float aRotationAngle,
+             float aForce)
+    {
+      mIdentifier = aIdentifier;
+      mPagePoint = nsIntPoint(0, 0);
+      mScreenPoint = nsIntPoint(0, 0);
+      mClientPoint = nsIntPoint(0, 0);
+      mRefPoint = aPoint;
+      mPointsInitialized = false;
+      mRadius = aRadius;
+      mRotationAngle = aRotationAngle;
+      mForce = aForce;
+
+      mChanged = false;
+      mMessage = 0;
+    }
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMTouch)
   NS_DECL_NSIDOMTOUCH
+  void InitializePoints(nsPresContext* aPresContext, nsEvent* aEvent)
+  {
+    if (mPointsInitialized) {
+      return;
+    }
+    mClientPoint = nsDOMEvent::GetClientCoords(aPresContext,
+                                               aEvent,
+                                               mRefPoint,
+                                               mClientPoint);
+    mPagePoint = nsDOMEvent::GetPageCoords(aPresContext,
+                                           aEvent,
+                                           mRefPoint,
+                                           mClientPoint);
+    mScreenPoint = nsDOMEvent::GetScreenCoords(aPresContext, aEvent, mRefPoint);
+    mPointsInitialized = true;
+  }
+  void SetTarget(nsIDOMEventTarget *aTarget)
+  {
+    mTarget = aTarget;
+  }
+  bool Equals(nsIDOMTouch* aTouch);
 protected:
-  nsCOMPtr<nsIDOMEventTarget> mTarget;
+  bool mPointsInitialized;
   PRInt32 mIdentifier;
-  PRInt32 mPageX;
-  PRInt32 mPageY;
-  PRInt32 mScreenX;
-  PRInt32 mScreenY;
-  PRInt32 mClientX;
-  PRInt32 mClientY;
-  PRInt32 mRadiusX;
-  PRInt32 mRadiusY;
+  nsIntPoint mPagePoint;
+  nsIntPoint mClientPoint;
+  nsIntPoint mScreenPoint;
+  nsIntPoint mRadius;
   float mRotationAngle;
   float mForce;
 };
 
 class nsDOMTouchList : public nsIDOMTouchList
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMTouchList)
   NS_DECL_NSIDOMTOUCHLIST
+
+  nsDOMTouchList() { }
+  nsDOMTouchList(nsTArray<nsCOMPtr<nsIDOMTouch> > &aTouches);
   
   void Append(nsIDOMTouch* aPoint)
   {
-    mPoints.AppendObject(aPoint);
+    mPoints.AppendElement(aPoint);
   }
 
   nsIDOMTouch* GetItemAt(PRUint32 aIndex)
   {
-    return mPoints.SafeObjectAt(aIndex);
+    return mPoints.SafeElementAt(aIndex, nsnull);
   }
 protected:
-  nsCOMArray<nsIDOMTouch> mPoints;
+  nsTArray<nsCOMPtr<nsIDOMTouch> > mPoints;
 };
 
 class nsDOMTouchEvent : public nsDOMUIEvent,
                         public nsIDOMTouchEvent
 {
 public:
-  nsDOMTouchEvent(nsPresContext* aPresContext, nsInputEvent* aEvent);
+  nsDOMTouchEvent(nsPresContext* aPresContext, nsTouchEvent* aEvent);
   virtual ~nsDOMTouchEvent();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMTouchEvent, nsDOMUIEvent)
   NS_DECL_NSIDOMTOUCHEVENT
 
   NS_FORWARD_TO_NSDOMUIEVENT
 
--- a/content/events/src/nsDOMUIEvent.cpp
+++ b/content/events/src/nsDOMUIEvent.cpp
@@ -239,25 +239,47 @@ nsDOMUIEvent::GetPagePoint()
 
   return pagePoint;
 }
 
 NS_IMETHODIMP
 nsDOMUIEvent::GetPageX(PRInt32* aPageX)
 {
   NS_ENSURE_ARG_POINTER(aPageX);
+#ifdef MOZ_TOUCH
+  if (mPrivateDataDuplicated) {
+    *aPageX = mPagePoint.x;
+  } else {
+    *aPageX = nsDOMEvent::GetPageCoords(mPresContext,
+                                        mEvent,
+                                        mEvent->refPoint,
+                                        mClientPoint).x;
+  }
+#else
   *aPageX = GetPagePoint().x;
+#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMUIEvent::GetPageY(PRInt32* aPageY)
 {
   NS_ENSURE_ARG_POINTER(aPageY);
+#ifdef MOZ_TOUCH
+  if (mPrivateDataDuplicated) {
+    *aPageY = mPagePoint.y;
+  } else {
+    *aPageY = nsDOMEvent::GetPageCoords(mPresContext,
+                                        mEvent,
+                                        mEvent->refPoint,
+                                        mClientPoint).y;
+  }
+#else
   *aPageY = GetPagePoint().y;
+#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMUIEvent::GetWhich(PRUint32* aWhich)
 {
   return Which(aWhich);
 }
@@ -333,16 +355,17 @@ nsDOMUIEvent::SetCancelBubble(bool aCanc
 nsIntPoint
 nsDOMUIEvent::GetLayerPoint()
 {
   if (!mEvent ||
       (mEvent->eventStructType != NS_MOUSE_EVENT &&
        mEvent->eventStructType != NS_POPUP_EVENT &&
        mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
        mEvent->eventStructType != NS_MOZTOUCH_EVENT &&
+       mEvent->eventStructType != NS_TOUCH_EVENT &&
        mEvent->eventStructType != NS_DRAG_EVENT &&
        mEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) ||
       !mPresContext ||
       mEventIsInternal) {
     return mLayerPoint;
   }
   // XXX I'm not really sure this is correct; it's my best shot, though
   nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
@@ -385,21 +408,37 @@ nsDOMUIEvent::GetIsChar(bool* aIsChar)
       *aIsChar = false;
       return NS_OK;
   }
 }
 
 NS_METHOD
 nsDOMUIEvent::DuplicatePrivateData()
 {
+#ifdef MOZ_TOUCH
+  mClientPoint = nsDOMEvent::GetClientCoords(mPresContext,
+                                             mEvent,
+                                             mEvent->refPoint,
+                                             mClientPoint);
+  mLayerPoint = GetLayerPoint();
+  mPagePoint = nsDOMEvent::GetPageCoords(mPresContext,
+                                         mEvent,
+                                         mEvent->refPoint,
+                                         mClientPoint);
+  // GetScreenPoint converts mEvent->refPoint to right coordinates.
+  nsIntPoint screenPoint = nsDOMEvent::GetScreenCoords(mPresContext,
+                                                       mEvent,
+                                                       mEvent->refPoint);
+#else
   mClientPoint = GetClientPoint();
   mLayerPoint = GetLayerPoint();
   mPagePoint = GetPagePoint();
   // GetScreenPoint converts mEvent->refPoint to right coordinates.
   nsIntPoint screenPoint = GetScreenPoint();
+#endif
   nsresult rv = nsDOMEvent::DuplicatePrivateData();
   if (NS_SUCCEEDED(rv)) {
     mEvent->refPoint = screenPoint;
   }
   return rv;
 }
 
 void
--- a/content/events/src/nsDOMUIEvent.h
+++ b/content/events/src/nsDOMUIEvent.h
@@ -63,18 +63,18 @@ public:
   NS_FORWARD_TO_NSDOMEVENT
 
   NS_FORWARD_NSIDOMNSEVENT(nsDOMEvent::)
 
   virtual nsresult InitFromCtor(const nsAString& aType,
                                 JSContext* aCx, jsval* aVal);
 protected:
   // Internal helper functions
+  nsIntPoint GetScreenPoint();
   nsIntPoint GetClientPoint();
-  nsIntPoint GetScreenPoint();
   nsIntPoint GetLayerPoint();
   nsIntPoint GetPagePoint();
 
   // Allow specializations.
   virtual nsresult Which(PRUint32* aWhich)
   {
     NS_ENSURE_ARG_POINTER(aWhich);
     // Usually we never reach here, as this is reimplemented for mouse and keyboard events.
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -803,16 +803,19 @@ nsEventDispatcher::CreateEvent(nsPresCon
       return NS_NewDOMCommandEvent(aDOMEvent, aPresContext,
                                    static_cast<nsCommandEvent*>(aEvent));
     case NS_SIMPLE_GESTURE_EVENT:
       return NS_NewDOMSimpleGestureEvent(aDOMEvent, aPresContext,
                                          static_cast<nsSimpleGestureEvent*>(aEvent));
     case NS_MOZTOUCH_EVENT:
       return NS_NewDOMMozTouchEvent(aDOMEvent, aPresContext,
                                     static_cast<nsMozTouchEvent*>(aEvent));
+    case NS_TOUCH_EVENT:
+      return NS_NewDOMTouchEvent(aDOMEvent, aPresContext,
+                                 static_cast<nsTouchEvent*>(aEvent));
     case NS_TRANSITION_EVENT:
       return NS_NewDOMTransitionEvent(aDOMEvent, aPresContext,
                                       static_cast<nsTransitionEvent*>(aEvent));
     case NS_ANIMATION_EVENT:
       return NS_NewDOMAnimationEvent(aDOMEvent, aPresContext,
                                      static_cast<nsAnimationEvent*>(aEvent));
     }
 
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -125,16 +125,22 @@ endif
 
 # bug 565245
 ifneq (Linux,$(OS_ARCH))
 _TEST_FILES += \
 		test_bug493251.html \
 		$(NULL)
 endif
 
+ifeq (android,$(MOZ_WIDGET_TOOLKIT))
+_TEST_FILES += \
+		test_bug603008.html \
+		$(NULL)
+endif
+
 _CHROME_FILES = \
 		test_bug336682_2.xul \
 		test_bug336682.js \
 		test_bug350471.xul \
 		test_bug586961.xul \
 		test_bug415498.xul \
 		bug415498-doc1.html \
 		bug415498-doc2.html \
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_bug603008.html
@@ -0,0 +1,524 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=508906
+-->
+<head>
+  <title>Test for Bug 603008</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=508906">Mozilla Bug 603008</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.8">
+
+/** Test for Bug 306008 - Touch* Events **/
+
+let tests = [], testTarget, parent;
+
+let touch = {
+  id: 0,
+  point: {x: 0, y: 0},
+  radius: {x: 0, y: 0},
+  rotation: 0,
+  force: 0.5,
+  target: null
+}
+
+function nextTest() {
+  if (tests.length)
+    SimpleTest.executeSoon(tests.shift());
+}
+
+function random() {
+  return Math.floor(Math.random() * 100);
+}
+
+function checkEvent(aFakeEvent) {
+  return function(aEvent) {
+    is(aFakeEvent.ctrlKey, aEvent.ctrlKey, "Correct ctrlKey");
+    is(aFakeEvent.altKey, aEvent.altKey, "Correct altKey");
+    is(aFakeEvent.shiftKey, aEvent.shiftKey, "Correct shiftKey");
+    is(aFakeEvent.metaKey, aEvent.metaKey, "Correct metaKey");
+    checkTouches(aFakeEvent.touches, aEvent.touches);
+    checkTouches(aFakeEvent.targetTouches, aEvent.targetTouches);
+    checkTouches(aFakeEvent.changedTouches, aEvent.changedTouches);
+  }
+}
+
+function checkTouches(aTouches1, aTouches2) {
+  is(aTouches1.length, aTouches2.length, "Correct touches length");
+  for (var i = 0; i < aTouches1.length; i++) {
+    checkTouch(aTouches1[i], aTouches2[i]);
+  }
+}
+
+function checkTouch(aFakeTouch, aTouch) {
+  is(aFakeTouch.identifier, aTouch.identifier, "Touch has correct identifier");
+  is(aFakeTouch.target, aTouch.target, "Touch has correct target");
+  is(aFakeTouch.page.x, aTouch.pageX, "Touch has correct pageX");
+  is(aFakeTouch.page.y, aTouch.pageY, "Touch has correct pageY");
+  is(aFakeTouch.page.x + Math.round(window.mozInnerScreenX), aTouch.screenX, "Touch has correct screenX");
+  is(aFakeTouch.page.y + Math.round(window.mozInnerScreenY), aTouch.screenY, "Touch has correct screenY");
+  is(aFakeTouch.page.x, aTouch.clientX, "Touch has correct clientX");
+  is(aFakeTouch.page.y, aTouch.clientY, "Touch has correct clientY");
+  is(aFakeTouch.radius.x, aTouch.radiusX, "Touch has correct radiusX");
+  is(aFakeTouch.radius.y, aTouch.radiusY, "Touch has correct radiusY");
+  is(aFakeTouch.rotationAngle, aTouch.rotationAngle, "Touch has correct rotationAngle");
+  is(aFakeTouch.force, aTouch.force, "Touch has correct force");
+}
+
+function sendTouchEvent(windowUtils, aType, aEvent, aModifiers) {
+  var ids = [], xs=[], ys=[], rxs = [], rys = [],
+      rotations = [], forces = [];
+
+  for each (var touchType in ["touches", "changedTouches", "targetTouches"]) {
+    for (var i = 0; i < aEvent[touchType].length; i++) {
+      if (ids.indexOf(aEvent[touchType][i].identifier) == -1) {
+        ids.push(aEvent[touchType][i].identifier);
+        xs.push(aEvent[touchType][i].page.x);
+        ys.push(aEvent[touchType][i].page.y);
+        rxs.push(aEvent[touchType][i].radius.x);
+        rys.push(aEvent[touchType][i].radius.y);
+        rotations.push(aEvent[touchType][i].rotationAngle);
+        forces.push(aEvent[touchType][i].force);
+      }
+    }
+  }
+  return windowUtils.sendTouchEvent(aType,
+                                    ids, xs, ys, rxs, rys,
+                                    rotations, forces,
+                                    ids.length, aModifiers, 0);
+}
+
+function touchEvent(aOptions) {
+  if (!aOptions) {
+    aOptions = {};
+  }
+  this.ctrlKey = aOptions.ctrlKey || false;
+  this.altKey = aOptions.altKey || false;
+  this.shiftKey = aOptions.shiftKey || false;
+  this.metaKey = aOptions.metaKey || false;
+  this.touches = aOptions.touches || [];
+  this.targetTouches = aOptions.targetTouches || [];
+  this.changedTouches = aOptions.changedTouches || [];
+}
+
+function testtouch(aOptions) {
+  if (!aOptions)
+    aOptions = {};
+  this.identifier = aOptions.identifier || 0;
+  this.target = aOptions.target || 0;
+  this.page = aOptions.page || {x: 0, y: 0};
+  this.radius = aOptions.radius || {x: 0, y: 0};
+  this.rotationAngle = aOptions.rotationAngle || 0;
+  this.force = aOptions.force || 1;
+}
+
+function testSingleTouch(name) {
+  let cwu = SpecialPowers.getDOMWindowUtils(window);
+  let target = document.getElementById("testTarget");
+  let target2 = document.getElementById("testTarget2");
+  let bcr = target.getBoundingClientRect();
+  let bcr2 = target2.getBoundingClientRect();
+
+  let touch1 = new testtouch({
+    page: {x: Math.round(bcr.left + bcr.width/2),
+           y: Math.round(bcr.top  + bcr.height/2)},
+    target: target
+  });
+  let event = new touchEvent({
+    touches: [touch1],
+    targetTouches: [touch1],
+    changedTouches: [touch1]
+  });
+
+  // test touchstart event fires correctly
+  var checkFunction = checkEvent(event);
+  window.addEventListener("touchstart", checkFunction, false);
+  sendTouchEvent(cwu, "touchstart", event, 0);
+  window.removeEventListener("touchstart", checkFunction, false);
+
+  // test touchmove event fires correctly
+  event.touches[0].page.x -= 1;
+  event.targetTouches[0].page.x -= 1;
+  event.changedTouches[0].page.x -= 1;
+  checkFunction = checkEvent(event);
+  window.addEventListener("touchmove", checkFunction, false);
+  sendTouchEvent(cwu, "touchmove", event, 0);
+  window.removeEventListener("touchmove", checkFunction, false);
+
+  // test touchend event fires correctly
+  event.touches = [];
+  event.targetTouches = [];
+  checkFunction = checkEvent(event);
+  window.addEventListener("touchend", checkFunction, false);
+  sendTouchEvent(cwu, "touchend", event, 0);
+  window.removeEventListener("touchend", checkFunction, false);
+
+  nextTest();
+}
+
+function testSingleTouch2(name) {
+  // firing a touchstart that includes only one touch will evict any touches in the queue with touchend messages
+  let cwu = SpecialPowers.getDOMWindowUtils(window);
+  let target = document.getElementById("testTarget");
+  let target2 = document.getElementById("testTarget2");
+  let bcr = target.getBoundingClientRect();
+  let bcr2 = target2.getBoundingClientRect();
+
+  let touch1 = new testtouch({
+    identifier: 0,
+    page: {x: Math.round(bcr.left + bcr.width/2),
+           y: Math.round(bcr.top  + bcr.height/2)},
+    target: target
+  });
+  let event1 = new touchEvent({
+    touches: [touch1],
+    targetTouches: [touch1],
+    changedTouches: [touch1]
+  });
+  let touch2 = new testtouch({
+    identifier: 1,
+    page: {x: Math.round(bcr2.left + bcr2.width/2),
+           y: Math.round(bcr2.top  + bcr2.height/2)},
+    target: target2
+  });
+  let event2 = new touchEvent({
+    touches: [touch2],
+    targetTouches: [touch2],
+    changedTouches: [touch2]
+  });
+
+  // test touchstart event fires correctly
+  var checkFunction1 = checkEvent(event1);
+  window.addEventListener("touchstart", checkFunction1, false);
+  sendTouchEvent(cwu, "touchstart", event1, 0);
+  window.removeEventListener("touchstart", checkFunction1, false);
+
+  event1.touches = [];
+  event1.targetTouches = [];
+  checkFunction1 = checkEvent(event1);
+  var checkFunction2 = checkEvent(event2);
+
+  window.addEventListener("touchend", checkFunction1, false);
+  window.addEventListener("touchstart", checkFunction2, false);
+  sendTouchEvent(cwu, "touchstart", event2, 0);
+  window.removeEventListener("touchend", checkFunction1, false);
+  window.removeEventListener("touchstart", checkFunction2, false);
+
+  sendTouchEvent(cwu, "touchstart", event1, 0);
+
+  nextTest();
+}
+
+
+function testMultiTouch(name) {
+  let cwu = SpecialPowers.getDOMWindowUtils(window);
+  let target1 = document.getElementById("testTarget");
+  let target2 = document.getElementById("testTarget2");
+  let bcr = target1.getBoundingClientRect();
+  let bcr2 = target2.getBoundingClientRect();
+
+  let touch1 = new testtouch({
+    identifier: 0,
+    page: {x: Math.round(bcr.left + bcr.width/2),
+           y: Math.round(bcr.top  + bcr.height/2)},
+    target: target1
+  });
+  let touch2 = new testtouch({
+    identifier: 1,
+    page: {x: Math.round(bcr2.left + bcr2.width/2),
+           y: Math.round(bcr2.top  + bcr2.height/2)},
+    target: target2
+  });
+  let event = new touchEvent({
+    touches: [touch1],
+    targetTouches: [touch1],
+    changedTouches: [touch1]
+  });
+
+  // test touchstart event fires correctly
+  var checkFunction = checkEvent(event);
+  window.addEventListener("touchstart", checkFunction, false);
+  sendTouchEvent(cwu, "touchstart", event, 0);
+  window.removeEventListener("touchstart", checkFunction, false);
+
+  event.touches.push(touch2);
+  event.targetTouches = [touch2];
+  event.changedTouches = [touch2];
+  window.addEventListener("touchstart", checkFunction, false);
+  sendTouchEvent(cwu, "touchstart", event, 0);
+  window.removeEventListener("touchstart", checkFunction, false);
+
+  // test moving one touch point
+  event.touches[0].page.x -= 1;
+  event.targetTouches = [event.touches[0]];
+  event.changedTouches = [event.touches[0]];
+  window.addEventListener("touchmove", checkFunction, false);
+  sendTouchEvent(cwu, "touchmove", event, 0);
+  window.removeEventListener("touchmove", checkFunction, false);
+
+  // test moving both touch points -- two touchmove events should fire, one on each target
+  event.touches[0].page.x -= 1;
+  event.touches[1].page.x -= 1;
+  event.targetTouches = event.touches;
+  event.changedTouches = event.touches;
+  var touchMoveEvents = 0;
+  var checkFunction2 = function(aEvent) {
+    is(event.ctrlKey, aEvent.ctrlKey, "Correct ctrlKey");
+    is(event.altKey, aEvent.altKey, "Correct altKey");
+    is(event.shiftKey, aEvent.shiftKey, "Correct shiftKey");
+    is(event.metaKey, aEvent.metaKey, "Correct metaKey");
+    checkTouches(event.touches, aEvent.touches);
+    checkTouches(event.changedTouches, aEvent.changedTouches);
+    if (aEvent.targetTouches[0].target == target1) {
+      checkTouches([event.touches[0]], aEvent.targetTouches);
+    } else if (aEvent.targetTouches[0].target == target2) {
+      checkTouches([event.touches[1]], aEvent.targetTouches);
+    } else
+      ok(false, "Event target is incorrect: " + event.targetTouches[0].target.nodeName + "#" + event.targetTouches[0].target.id);
+    touchMoveEvents++;
+  };
+  window.addEventListener("touchmove", checkFunction2, false);
+  sendTouchEvent(cwu, "touchmove", event, 0);
+  ok(touchMoveEvents, 2, "Correct number of touchmove events fired");
+  window.removeEventListener("touchmove", checkFunction2, false);
+
+  // test removing just one finger
+  var expected = new touchEvent({
+    touches: [touch2],
+    targetTouches: [],
+    changedTouches: [touch1]
+  });
+  checkFunction = checkEvent(expected);
+
+  event.touches = [];
+  event.targetTouches = [];
+  event.changedTouches = [touch1];
+
+  // test removing the other finger
+  window.addEventListener("touchend", checkFunction, false);
+  sendTouchEvent(cwu, "touchend", event, 0);
+  window.removeEventListener("touchend", checkFunction, false);
+
+  event.touches = [];
+  event.targetTouches = [];
+  event.changedTouches = [touch2];
+  checkFunction = checkEvent(event);
+  window.addEventListener("touchend", checkFunction, false);
+  sendTouchEvent(cwu, "touchend", event, 0);
+  window.removeEventListener("touchend", checkFunction, false);
+
+  nextTest();
+}
+
+function testTouchChanged() {
+  let cwu = SpecialPowers.getDOMWindowUtils(window);
+  let target1 = document.getElementById("testTarget");
+  let bcr = target1.getBoundingClientRect();
+
+  let touch1 = new testtouch({
+    identifier: 0,
+    page: {x: Math.round(bcr.left + bcr.width/2),
+           y: Math.round(bcr.top  + bcr.height/2)},
+    target: target1
+  });
+  let event = new touchEvent({
+    touches: [touch1],
+    targetTouches: [touch1],
+    changedTouches: [touch1]
+  });
+
+  var checkFunction = checkEvent(event);
+  sendTouchEvent(cwu, "touchstart", event, 0);
+
+  var moveEvents = 0;
+  function onMove(aEvent) {
+    moveEvents++;
+  }
+
+  window.addEventListener("touchmove", onMove, false);
+
+  // changing nothing should not fire a touchmove event
+  sendTouchEvent(cwu, "touchmove", event, 0);
+
+  // test moving x
+  event.touches[0].page.x -= 1;
+  sendTouchEvent(cwu, "touchmove", event, 0);
+
+  // test moving y
+  event.touches[0].page.y -= 1;
+  sendTouchEvent(cwu, "touchmove", event, 0);
+
+  // test changing y radius
+  event.touches[0].radius.y += 1;
+  sendTouchEvent(cwu, "touchmove", event, 0);
+  
+  // test changing x radius
+  event.touches[0].radius.x += 1;
+  sendTouchEvent(cwu, "touchmove", event, 0);
+
+  // test changing rotationAngle
+  event.touches[0].rotationAngle += 1;
+  sendTouchEvent(cwu, "touchmove", event, 0);
+
+  // test changing force
+  event.touches[0].force += 1;
+  sendTouchEvent(cwu, "touchmove", event, 0);
+
+  // changing nothing again
+  sendTouchEvent(cwu, "touchmove", event, 0);
+  
+  is(moveEvents, 6, "Six move events fired");
+
+  window.removeEventListener("touchmove", onMove, false);
+  sendTouchEvent(cwu, "touchend", event, 0);
+  nextTest();
+}
+
+function testPreventDefault() {
+  let cwu = SpecialPowers.getDOMWindowUtils(window);
+  let target = document.getElementById("testTarget");
+  let target2 = document.getElementById("testTarget2");
+  let bcr = target.getBoundingClientRect();
+  let bcr2 = target2.getBoundingClientRect();
+
+  let touch1 = new testtouch({
+    page: {x: bcr.left + bcr.width/2,
+           y: bcr.top + bcr.height/2},
+    target: target
+  });
+  let event = new touchEvent({
+    touches: [touch1],
+    targetTouches: [touch1],
+    changedTouches: [touch1]
+  });
+
+  let preventFunction = function(aEvent) {
+    aEvent.preventDefault();
+  }
+  
+  let tests = [
+    [{ name: "touchstart", prevent: false },
+     { name: "touchmove", prevent: false },
+     { name: "touchmove", prevent: false },
+     { name: "touchend", prevent: false }],
+    [{ name: "touchstart", prevent: true, doPrevent: true },
+     { name: "touchmove", prevent: true },
+     { name: "touchmove", prevent: true },
+     { name: "touchend", prevent: true }],
+    [{ name: "touchstart", prevent: false },
+     { name: "touchmove", prevent: true, doPrevent: true },
+     { name: "touchmove", prevent: true },
+     { name: "touchend", prevent: true }],
+    [{ name: "touchstart", prevent: false },
+     { name: "touchmove", prevent: false },
+     { name: "touchmove", prevent: false, doPrevent: true },
+     { name: "touchend", prevent: false }],
+    [{ name: "touchstart", prevent: false },
+     { name: "touchmove", prevent: false },
+     { name: "touchmove", prevent: false },
+     { name: "touchend", prevent: false, doPrevent: true }]
+  ];
+
+  var dotest = function(aTest) {
+    if (aTest.doPrevent) {
+      target.addEventListener(aTest.name, preventFunction, false);
+    }
+
+    if (aTest.name == "touchmove") {
+      touch1.page.x++;
+      event.touches[0] = touch1;
+    }
+
+    is(sendTouchEvent(cwu, aTest.name, event, 0), aTest.prevent, "Got correct status");
+
+    if (aTest.doPrevent)
+      target.removeEventListener(aTest.name, preventFunction, false);
+  }
+
+  for (var i = 0; i < tests.length; i++) {
+    for (var j = 0; j < tests[i].length; j++) {
+      dotest(tests[i][j]);
+    }
+  } 
+
+  nextTest();
+}
+
+function testRemovingElement() {
+  let cwu = SpecialPowers.getDOMWindowUtils(window);
+  let target = document.getElementById("testTarget");
+  let bcr = document.getElementById("testTarget").getBoundingClientRect();
+
+  let touch1 = new testtouch({
+    page: {x: bcr.left + bcr.width/2,
+           y: bcr.top + bcr.height/2},
+  });
+  let e = new touchEvent({
+    touches: [touch1],
+    targetTouches: [touch1],
+    changedTouches: [touch1]
+  });
+
+  var touchEvents = 0;  
+  var removeTarget = function(aEvent) {
+    aEvent.target.parentNode.removeChild(aEvent.target);
+  };
+
+  var checkTarget = function(aEvent) {
+    is(aEvent.target, target, "Event has correct target");
+    touchEvents++;
+  };
+
+  target.addEventListener("touchstart", removeTarget, false);
+  target.addEventListener("touchmove", checkTarget, false);
+  target.addEventListener("touchend", checkTarget, false);
+
+  sendTouchEvent(cwu, "touchstart", e, 0);
+
+  e.touches[0].page.x++;
+  sendTouchEvent(cwu, "touchmove", e, 0);
+  sendTouchEvent(cwu, "touchend", e, 0);
+
+  target.removeEventListener("touchstart", removeTarget, false);
+  target.removeEventListener("touchmove", checkTarget, false);
+  target.removeEventListener("touchend", checkTarget, false);
+
+  is(touchEvents, 2, "Check target was called twice");
+
+  nextTest();
+}
+
+function doTest() {
+  tests.push(testSingleTouch);
+  tests.push(testSingleTouch2);
+  tests.push(testMultiTouch);
+  tests.push(testPreventDefault);
+  tests.push(testTouchChanged);
+  tests.push(testRemovingElement);
+
+  tests.push(function() {
+    SimpleTest.finish();
+  });
+
+  nextTest();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+
+</script>
+</pre>
+<div id="parent">
+  <span id="testTarget" style="padding: 5px; border: 1px solid black;">testTarget</span>
+  <span id="testTarget2" style="padding: 5px; border: 1px solid blue;">testTarget</span>
+</div>
+</body>
+</html>
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -1546,92 +1546,85 @@ nsGenericHTMLElement::GetFormControlFram
 nsGenericHTMLElement::GetPrimaryPresState(nsGenericHTMLElement* aContent,
                                           nsPresState** aPresState)
 {
   NS_ENSURE_ARG_POINTER(aPresState);
   *aPresState = nsnull;
 
   nsresult result = NS_OK;
 
-  nsCOMPtr<nsILayoutHistoryState> history;
   nsCAutoString key;
-  GetLayoutHistoryAndKey(aContent, false, getter_AddRefs(history), key);
+  nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistoryAndKey(aContent, false, key);
 
   if (history) {
     // Get the pres state for this key, if it doesn't exist, create one
     result = history->GetState(key, aPresState);
     if (!*aPresState) {
       *aPresState = new nsPresState();
       result = history->AddState(key, *aPresState);
     }
   }
 
   return result;
 }
 
 
-nsresult
+already_AddRefed<nsILayoutHistoryState>
 nsGenericHTMLElement::GetLayoutHistoryAndKey(nsGenericHTMLElement* aContent,
                                              bool aRead,
-                                             nsILayoutHistoryState** aHistory,
                                              nsACString& aKey)
 {
   //
   // Get the pres shell
   //
   nsCOMPtr<nsIDocument> doc = aContent->GetDocument();
   if (!doc) {
-    return NS_OK;
+    return nsnull;
   }
 
   //
   // Get the history (don't bother with the key if the history is not there)
   //
-  *aHistory = doc->GetLayoutHistoryState().get();
-  if (!*aHistory) {
-    return NS_OK;
+  nsCOMPtr<nsILayoutHistoryState> history = doc->GetLayoutHistoryState();
+  if (!history) {
+    return nsnull;
   }
 
-  if (aRead && !(*aHistory)->HasStates()) {
-    NS_RELEASE(*aHistory);
-    return NS_OK;
+  if (aRead && !history->HasStates()) {
+    return nsnull;
   }
 
   //
   // Get the state key
   //
   nsresult rv = nsContentUtils::GenerateStateKey(aContent, doc,
                                                  nsIStatefulFrame::eNoID,
                                                  aKey);
   if (NS_FAILED(rv)) {
-    NS_RELEASE(*aHistory);
-    return rv;
+    return nsnull;
   }
 
   // If the state key is blank, this is anonymous content or for
   // whatever reason we are not supposed to save/restore state.
   if (aKey.IsEmpty()) {
-    NS_RELEASE(*aHistory);
-    return NS_OK;
+    return nsnull;
   }
 
   // Add something unique to content so layout doesn't muck us up
   aKey += "-C";
 
-  return rv;
+  return history.forget();
 }
 
 bool
 nsGenericHTMLElement::RestoreFormControlState(nsGenericHTMLElement* aContent,
                                               nsIFormControl* aControl)
 {
-  nsCOMPtr<nsILayoutHistoryState> history;
   nsCAutoString key;
-  GetLayoutHistoryAndKey(aContent, true,
-                         getter_AddRefs(history), key);
+  nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistoryAndKey(aContent, true, key);
   if (!history) {
     return false;
   }
 
   nsPresState *state;
   // Get the pres state for this key
   nsresult rv = history->GetState(key, &state);
   if (NS_SUCCEEDED(rv) && state) {
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -466,20 +466,20 @@ public:
    * piece of content.
    *
    * @param aContent the content to generate the key for
    * @param aRead if true, won't return a layout history state (and won't
    *              generate a key) if the layout history state is empty.
    * @param aState the history state object (out param)
    * @param aKey the key (out param)
    */
-  static nsresult GetLayoutHistoryAndKey(nsGenericHTMLElement* aContent,
-                                         bool aRead,
-                                         nsILayoutHistoryState** aState,
-                                         nsACString& aKey);
+  static already_AddRefed<nsILayoutHistoryState>
+  GetLayoutHistoryAndKey(nsGenericHTMLElement* aContent,
+                         bool aRead,
+                         nsACString& aKey);
   /**
    * Restore the state for a form control.  Ends up calling
    * nsIFormControl::RestoreState().
    *
    * @param aContent an nsGenericHTMLElement* pointing to the form control
    * @param aControl an nsIFormControl* pointing to the form control
    * @return false if RestoreState() was not called, the return
    *         value of RestoreState() otherwise.
@@ -864,17 +864,17 @@ public:
   virtual void ClearForm(bool aRemoveFromForm);
 
   nsresult GetForm(nsIDOMHTMLFormElement** aForm);
 
   NS_IMETHOD SaveState()
   {
     return NS_OK;
   }
-  
+
   virtual bool RestoreState(nsPresState* aState)
   {
     return false;
   }
   virtual bool AllowDrop()
   {
     return true;
   }
--- a/content/mathml/content/src/Makefile.in
+++ b/content/mathml/content/src/Makefile.in
@@ -60,13 +60,12 @@ FORCE_STATIC_LIB = 1
 
 EXPORTS = \
 	$(NULL)
 
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES += 	\
-		-I$(srcdir)/../../../shared/public \
 		-I$(srcdir)/../../../base/src \
 		$(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -626,17 +626,16 @@ bool nsBuiltinDecoderStateMachine::IsPla
 }
 
 void nsBuiltinDecoderStateMachine::AudioLoop()
 {
   NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
   LOG(PR_LOG_DEBUG, ("%p Begun audio thread/loop", mDecoder.get()));
   PRInt64 audioDuration = 0;
   PRInt64 audioStartTime = -1;
-  PRInt64 framesWritten = 0;
   PRUint32 channels, rate;
   double volume = -1;
   bool setVolume;
   PRInt32 minWriteFrames = -1;
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mAudioCompleted = false;
     audioStartTime = mAudioStartTime;
@@ -741,16 +740,17 @@ void nsBuiltinDecoderStateMachine::Audio
       break;
     }
     PRInt64 missingFrames = 0;
     if (!AddOverflow(sampleTime, -playedFrames, missingFrames)) {
       NS_WARNING("Int overflow adding missingFrames");
       break;
     }
 
+    PRInt64 framesWritten = 0;
     if (missingFrames > 0) {
       // The next audio chunk begins some time after the end of the last chunk
       // we pushed to the audio hardware. We must push silence into the audio
       // hardware so that the next audio chunk begins playback at the correct
       // time.
       missingFrames = NS_MIN(static_cast<PRInt64>(PR_UINT32_MAX), missingFrames);
       framesWritten = PlaySilence(static_cast<PRUint32>(missingFrames),
                                   channels, playedFrames);
@@ -775,28 +775,32 @@ void nsBuiltinDecoderStateMachine::Audio
       mState != DECODER_STATE_SHUTDOWN &&
       !mStopAudioThread)
   {
     // Last frame pushed to audio hardware, wait for the audio to finish,
     // before the audio thread terminates.
     bool seeking = false;
     {
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-      if (framesWritten < minWriteFrames) {
-        // We've not written minWriteFrames in the last write, the audio
-        // may not start playing. Write silence to ensure we've got enough
-        // written to start playback.
-        PRInt64 minToWrite = minWriteFrames - framesWritten;
-        if (minToWrite < PR_UINT32_MAX / channels) {
+      PRInt64 unplayedFrames = audioDuration % minWriteFrames;
+      if (minWriteFrames > 1 && unplayedFrames > 0) {
+        // Sound is written by libsydneyaudio to the hardware in blocks of
+        // frames of size minWriteFrames. So if the number of frames we've
+        // written isn't an exact multiple of minWriteFrames, we'll have
+        // left over audio data which hasn't yet been written to the hardware,
+        // and so that audio will not start playing. Write silence to ensure
+        // the last block gets pushed to hardware, so that playback starts.
+        PRInt64 framesToWrite = minWriteFrames - unplayedFrames;
+        if (framesToWrite < PR_UINT32_MAX / channels) {
           // Write silence manually rather than using PlaySilence(), so that
           // the AudioAPI doesn't get a copy of the audio frames.
-          PRUint32 numSamples = minToWrite * channels;
+          PRUint32 numSamples = framesToWrite * channels;
           nsAutoArrayPtr<AudioDataValue> buf(new AudioDataValue[numSamples]);
           memset(buf.get(), 0, numSamples * sizeof(AudioDataValue));
-          mAudioStream->Write(buf, minToWrite);
+          mAudioStream->Write(buf, framesToWrite);
         }
       }
 
       PRInt64 oldPosition = -1;
       PRInt64 position = GetMediaTime();
       while (oldPosition != position &&
              mAudioEndTime - position > 0 &&
              mState != DECODER_STATE_SEEKING &&
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -169,18 +169,16 @@ FORCE_STATIC_LIB = 1
 EXPORTS =  			\
 	nsSVGFeatures.h            \
 	nsSVGRect.h                \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES += 	\
-		-I$(srcdir)/../../../shared/public \
-		-I$(srcdir)/../../../html/base/src \
 		-I$(srcdir)/../../../xml/content/src \
 		-I$(srcdir)/../../../../dom \
 		-I$(srcdir)/../../../base/src \
 		-I$(srcdir)/../../../../layout/generic \
 		-I$(srcdir)/../../../../layout/xul/base/src \
 		-I$(srcdir)/../../../../layout/svg/base/src \
 		-I$(srcdir)/../../../../layout/style \
 		-I$(srcdir)/../../../events/src \
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -44,16 +44,18 @@
 #include "nsDOMWindowUtils.h"
 #include "nsQueryContentEventResult.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocument.h"
 #include "nsFocusManager.h"
 #include "nsEventStateManager.h"
 #include "nsFrameManager.h"
 #include "nsRefreshDriver.h"
+#include "nsDOMTouchEvent.h"
+#include "nsIDOMTouchEvent.h"
 
 #include "nsIScrollableFrame.h"
 
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 
 #include "nsIFrame.h"
 #include "nsIWidget.h"
@@ -545,16 +547,89 @@ nsDOMWindowUtils::SendMouseScrollEvent(c
   event.refPoint.y =
     NSAppUnitsToIntPixels(nsPresContext::CSSPixelsToAppUnits(aY) + offset.y,
                           appPerDev);
 
   nsEventStatus status;
   return widget->DispatchEvent(&event, status);
 }
 
+
+NS_IMETHODIMP
+nsDOMWindowUtils::SendTouchEvent(const nsAString& aType,
+                                 PRUint32 *aIdentifiers,
+                                 PRInt32 *aXs,
+                                 PRInt32 *aYs,
+                                 PRUint32 *aRxs,
+                                 PRUint32 *aRys,
+                                 float *aRotationAngles,
+                                 float *aForces,
+                                 PRUint32 aCount,
+                                 PRInt32 aModifiers,
+                                 bool aIgnoreRootScrollFrame,
+                                 bool *aPreventDefault)
+{
+  if (!IsUniversalXPConnectCapable()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  // get the widget to send the event to
+  nsPoint offset;
+  nsCOMPtr<nsIWidget> widget = GetWidget(&offset);
+  if (!widget) {
+    return NS_ERROR_NULL_POINTER;
+  }
+  PRInt32 msg;
+  if (aType.EqualsLiteral("touchstart")) {
+    msg = NS_TOUCH_START;
+  } else if (aType.EqualsLiteral("touchmove")) {
+    msg = NS_TOUCH_MOVE;
+  } else if (aType.EqualsLiteral("touchend")) {
+    msg = NS_TOUCH_END;
+  } else if (aType.EqualsLiteral("touchcancel")) {
+    msg = NS_TOUCH_CANCEL;
+  } else {
+    return NS_ERROR_UNEXPECTED;
+  }
+  nsTouchEvent event(true, msg, widget);
+  event.isShift = (aModifiers & nsIDOMNSEvent::SHIFT_MASK) ? true : false;
+  event.isControl = (aModifiers & nsIDOMNSEvent::CONTROL_MASK) ? true : false;
+  event.isAlt = (aModifiers & nsIDOMNSEvent::ALT_MASK) ? true : false;
+  event.isMeta = (aModifiers & nsIDOMNSEvent::META_MASK) ? true : false;
+  event.widget = widget;
+  event.time = PR_Now();
+
+  nsPresContext* presContext = GetPresContext();
+  if (!presContext) {
+    return NS_ERROR_FAILURE;
+  }
+  event.touches.SetCapacity(aCount);
+  PRInt32 appPerDev = presContext->AppUnitsPerDevPixel();
+  for (int i = 0; i < aCount; ++i) {
+    nsIntPoint pt(0, 0);
+    pt.x =
+      NSAppUnitsToIntPixels(nsPresContext::CSSPixelsToAppUnits(aXs[i]) + offset.x,
+                            appPerDev);
+    pt.y =
+      NSAppUnitsToIntPixels(nsPresContext::CSSPixelsToAppUnits(aYs[i]) + offset.y,
+                            appPerDev);
+    nsCOMPtr<nsIDOMTouch> t(new nsDOMTouch(aIdentifiers[i],
+                                           pt,
+                                           nsIntPoint(aRxs[i], aRys[i]),
+                                           aRotationAngles[i],
+                                           aForces[i]));
+    event.touches.AppendElement(t);
+  }
+
+  nsEventStatus status;
+  nsresult rv = widget->DispatchEvent(&event, status);
+  *aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
+  return rv;
+}
+
 NS_IMETHODIMP
 nsDOMWindowUtils::SendKeyEvent(const nsAString& aType,
                                PRInt32 aKeyCode,
                                PRInt32 aCharCode,
                                PRInt32 aModifiers,
                                bool aPreventDefault,
                                bool* aDefaultActionTaken)
 {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1335,18 +1335,18 @@ nsGlobalWindow::FreeInnerObjects(bool aC
 
   if (mDummyJavaPluginOwner) {
     // Tear down the dummy java plugin.
 
     // XXXjst: On a general note, should windows with java stuff in
     // them ever even make it into the fast-back cache?
 
     mDummyJavaPluginOwner->Destroy();
-
     mDummyJavaPluginOwner = nsnull;
+    mDidInitJavaProperties = false;
   }
 
   CleanupCachedXBLHandlers(this);
 
 #ifdef DEBUG
   nsCycleCollector_DEBUG_shouldBeFreed(static_cast<nsIScriptGlobalObject*>(this));
 #endif
 }
@@ -1476,16 +1476,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFrameElement)
 
   // Unlink mDummyJavaPluginOwner
   if (tmp->mDummyJavaPluginOwner) {
     tmp->mDummyJavaPluginOwner->Destroy();
     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDummyJavaPluginOwner)
+    tmp->mDidInitJavaProperties = false;
   }
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFocusedNode)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPendingStorageEvents)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
@@ -2229,16 +2230,26 @@ nsGlobalWindow::SetNewDocument(nsIDocume
         newInnerWindow->mDoc = aDocument;
 
         // We're reusing the inner window for a new document. In this
         // case we don't clear the inner window's scope, but we must
         // make sure the cached document property gets updated.
 
         // XXXmarkh - tell other languages about this?
         ::JS_DeleteProperty(cx, currentInner->mJSObject, "document");
+
+        if (mDummyJavaPluginOwner) {
+          // Since we're reusing the inner window, tear down the
+          // dummy Java plugin we created for the old document in
+          // this window.
+          mDummyJavaPluginOwner->Destroy();
+          mDummyJavaPluginOwner = nsnull;
+
+          mDidInitJavaProperties = false;
+        }
       }
     } else {
       rv = newInnerWindow->InnerSetNewDocument(aDocument);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Initialize DOM classes etc on the inner window.
       rv = mContext->InitClasses(newInnerWindow->mJSObject);
       NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -63,18 +63,19 @@ interface nsIDOMElement;
 interface nsIDOMHTMLCanvasElement;
 interface nsIDOMEvent;
 interface nsITransferable;
 interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
+interface nsIDOMTouch;
 
-[scriptable, uuid(b9c1f815-c2f2-4607-a060-6a8566581927)]
+[scriptable, uuid(e01171b0-712a-47ce-8552-b7b2ef0a2507)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -222,16 +223,55 @@ interface nsIDOMWindowUtils : nsISupport
   void sendMouseEvent(in AString aType,
                       in float aX,
                       in float aY,
                       in long aButton,
                       in long aClickCount,
                       in long aModifiers,
                       [optional] in boolean aIgnoreRootScrollFrame);
 
+  /** Synthesize a touch event. The event types supported are:
+   *    touchstart, touchend, touchmove, and touchcancel
+   *
+   * Events are sent in coordinates offset by aX and aY from the window.
+   *
+   * Cannot be accessed from unprivileged context (not content-accessible)
+   * Will throw a DOM security error if called without UniversalXPConnect
+   * privileges.
+   *
+   * The event is dispatched via the toplevel window, so it could go to any
+   * window under the toplevel window, in some cases it could never reach this
+   * window at all.
+   *
+   * @param aType event type
+   * @param xs array of offsets in CSS pixels for each touch to be sent
+   * @param ys array of offsets in CSS pixels for each touch to be sent
+   * @param rxs array of radii in CSS pixels for each touch to be sent
+   * @param rys array of radii in CSS pixels for each touch to be sent
+   * @param rotationAngles array of angles in degrees for each touch to be sent
+   * @param forces array of forces (floats from 0 to 1) for each touch to be sent
+   * @param count number of touches in this set
+   * @param aModifiers modifiers pressed, using constants defined in nsIDOMNSEvent
+   * @param aIgnoreRootScrollFrame whether the event should ignore viewport bounds
+   *                           during dispatch
+   *
+   * returns true if the page called prevent default on this touch event
+   */
+  boolean sendTouchEvent(in AString aType,
+                         [array, size_is(count)] in PRUint32 aIdentifiers,
+                         [array, size_is(count)] in PRInt32 aXs,
+                         [array, size_is(count)] in PRInt32 aYs,
+                         [array, size_is(count)] in PRUint32 aRxs,
+                         [array, size_is(count)] in PRUint32 aRys,
+                         [array, size_is(count)] in float aRotationAngles,
+                         [array, size_is(count)] in float aForces,
+                         in PRUint32 count,
+                         in long aModifiers,
+                         [optional] in boolean aIgnoreRootScrollFrame);
+
   /** The same as sendMouseEvent but ensures that the event is dispatched to
    *  this DOM window or one of its children.
    */
   void sendMouseEventToWindow(in AString aType,
                               in float aX,
                               in float aY,
                               in long aButton,
                               in long aClickCount,
--- a/dom/interfaces/events/nsIDOMTouchEvent.idl
+++ b/dom/interfaces/events/nsIDOMTouchEvent.idl
@@ -31,36 +31,48 @@
  * 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 ***** */
 
 #include "nsIDOMUIEvent.idl"
+%{C++
+#include "nsWeakPtr.h"
+#include "nsPoint.h"
+%}
 interface nsIVariant;
 
 /**
  * @see http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
  */
 
-[scriptable, uuid(98bc0f7d-5bff-4387-9c42-58af54b48dd5)]
+[scriptable, builtinclass, uuid(98bc0f7d-5bff-4387-9c42-58af54b48dd5)]
 interface nsIDOMTouch : nsISupports {
   readonly attribute long              identifier;
   readonly attribute nsIDOMEventTarget target;
   readonly attribute long              pageX;
   readonly attribute long              pageY;
   readonly attribute long              screenX;
   readonly attribute long              screenY;
   readonly attribute long              clientX;
   readonly attribute long              clientY;
   readonly attribute long              radiusX;
   readonly attribute long              radiusY;
   readonly attribute float             rotationAngle;
   readonly attribute float             force;
+  %{C++
+    nsCOMPtr<nsIDOMEventTarget> mTarget;
+    nsIDOMEventTarget *GetTarget() { return mTarget; }
+    void SetTarget(nsIDOMEventTarget *target) { mTarget = target; }
+    nsIntPoint mRefPoint;
+    bool mChanged;
+    PRUint32 mMessage;
+  %}
 };
 
 [scriptable, uuid(60706eb7-d50d-4379-b01c-e78e6af84213)]
 interface nsIDOMTouchList : nsISupports {
   readonly attribute unsigned long length;
   nsIDOMTouch item(in unsigned long index);
   nsIDOMTouch identifiedTouch(in long identifier);
 };
--- a/dom/plugins/base/Makefile.in
+++ b/dom/plugins/base/Makefile.in
@@ -127,16 +127,20 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),qt)
 else
 	CPPSRCS += nsPluginNativeWindow.cpp
 endif
 endif
 endif
 endif
 endif
 
+ifdef MOZ_JAVA_COMPOSITOR
+DEFINES += -DMOZ_JAVA_COMPOSITOR
+endif
+
 LOCAL_INCLUDES += \
   -DSK_BUILD_FOR_ANDROID_NDK \
   -I$(topsrcdir)/xpcom/base/ \
   -I$(topsrcdir)/gfx/skia/include/core \
   -I$(topsrcdir)/gfx/skia/include/config \
   $(MOZ_CAIRO_CFLAGS) \
   $(NULL)
 
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1678,27 +1678,50 @@ bool nsPluginInstanceOwner::AddPluginVie
 
   if (aRect.IsEqualEdges(mLastPluginRect)) {
     // Already added and in position, no work to do
     return true;
   }
 
   JNIEnv* env = GetJNIForThread();
   jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell");
+
+#ifdef MOZ_JAVA_COMPOSITOR
+  nsAutoString metadata;
+  nsCOMPtr<nsIAndroidDrawMetadataProvider> metadataProvider =
+      AndroidBridge::Bridge()->GetDrawMetadataProvider();
+  metadataProvider->GetDrawMetadata(metadata);
+
+  jstring jMetadata = env->NewString(nsPromiseFlatString(metadata).get(), metadata.Length());
+
+  jmethodID method = env->GetStaticMethodID(cls,
+                                            "addPluginView",
+                                            "(Landroid/view/View;IIIILjava/lang/String;)V");
+
+  env->CallStaticVoidMethod(cls,
+                            method,
+                            javaSurface,
+                            (int)aRect.x,
+                            (int)aRect.y,
+                            (int)aRect.width,
+                            (int)aRect.height,
+                            jMetadata);
+#else
   jmethodID method = env->GetStaticMethodID(cls,
                                             "addPluginView",
                                             "(Landroid/view/View;DDDD)V");
 
   env->CallStaticVoidMethod(cls,
                             method,
                             javaSurface,
                             aRect.x,
                             aRect.y,
                             aRect.width,
                             aRect.height);
+#endif
 
   if (!mPluginViewAdded) {
     ANPEvent event;
     event.inSize = sizeof(ANPEvent);
     event.eventType = kLifecycle_ANPEventType;
     event.data.lifecycle.action = kOnScreen_ANPLifecycleAction;
     mInstance->HandleEvent(&event, nsnull);
 
--- a/dom/wifi/nsWifiWorker.js
+++ b/dom/wifi/nsWifiWorker.js
@@ -752,20 +752,20 @@ var WifiManager = (function() {
   manager.updateNetwork = function(config, callback) {
     manager.setNetworkConfiguration(config, callback);
   }
   manager.removeNetwork = function(netId, callback) {
     removeNetworkCommand(netId, callback);
   }
 
   function ipToString(n) {
-    return String((n & (0xff << 24)) >> 24) + "." +
-                 ((n & (0xff << 16)) >> 16) + "." +
-                 ((n & (0xff <<  8)) >>  8) + "." +
-                 ((n & (0xff <<  0)) >>  0);
+    return String((n >>  0) & 0xFF) + "." +
+                 ((n >>  8) & 0xFF) + "." +
+                 ((n >> 16) & 0xFF) + "." +
+                 ((n >> 24) & 0xFF);
   }
 
   manager.enableNetwork = function(netId, disableOthers, callback) {
     enableNetworkCommand(netId, disableOthers, callback);
   }
   manager.disableNetwork = function(netId, callback) {
     disableNetworkCommand(netId, callback);
   }
--- a/dom/workers/EventTarget.cpp
+++ b/dom/workers/EventTarget.cpp
@@ -37,16 +37,21 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "EventTarget.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "nsTraceRefcnt.h"
 
+// All the EventTarget subclasses have to be included here.
+#include "Worker.h"
+#include "WorkerScope.h"
+#include "XMLHttpRequest.h"
+
 #include "WorkerInlines.h"
 
 USING_WORKERS_NAMESPACE
 
 using mozilla::dom::workers::events::EventTarget;
 
 namespace {
 
@@ -59,16 +64,33 @@ namespace {
   };
 
 DECL_EVENTTARGET_CLASS(gClass, "EventTarget")
 DECL_EVENTTARGET_CLASS(gMainThreadClass, "WorkerEventTarget")
 
 #undef DECL_EVENTTARGET_CLASS
 
 inline
+bool
+EnsureObjectIsEventTarget(JSContext* aCx, JSObject* aObj, char* aFunctionName)
+{
+  JSClass* classPtr = JS_GET_CLASS(aCx, aObj);
+  if (classPtr &&
+      (ClassIsWorker(classPtr) || ClassIsWorkerGlobalScope(classPtr) ||
+       ClassIsXMLHttpRequest(classPtr))) {
+    return true;
+  }
+
+  JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
+                       "EventTarget", aFunctionName,
+                        classPtr ? classPtr->name : "object");
+  return false;
+}
+
+inline
 EventTarget*
 GetPrivate(JSContext* aCx, JSObject* aObj)
 {
   return GetJSPrivateSafeish<EventTarget>(aCx, aObj);
 }
 
 JSBool
 Construct(JSContext* aCx, uintN aArgc, jsval* aVp)
@@ -138,16 +160,20 @@ EventTarget::FromJSObject(JSContext* aCx
 JSBool
 EventTarget::AddEventListener(JSContext* aCx, uintN aArgc, jsval* aVp)
 {
   JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
   if (!obj) {
     return true;
   }
 
+  if (!EnsureObjectIsEventTarget(aCx, obj, "AddEventListener")) {
+    return false;
+  }
+
   EventTarget* self = GetPrivate(aCx, obj);
   if (!self) {
     return true;
   }
 
   JSString* type;
   JSObject* listener;
   JSBool capturing = false, wantsUntrusted = false;
@@ -169,16 +195,20 @@ EventTarget::AddEventListener(JSContext*
 JSBool
 EventTarget::RemoveEventListener(JSContext* aCx, uintN aArgc, jsval* aVp)
 {
   JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
   if (!obj) {
     return true;
   }
 
+  if (!EnsureObjectIsEventTarget(aCx, obj, "RemoveEventListener")) {
+    return false;
+  }
+
   EventTarget* self = GetPrivate(aCx, obj);
   if (!self) {
     return true;
   }
 
   JSString* type;
   JSObject* listener;
   JSBool capturing = false;
@@ -200,16 +230,20 @@ EventTarget::RemoveEventListener(JSConte
 JSBool
 EventTarget::DispatchEvent(JSContext* aCx, uintN aArgc, jsval* aVp)
 {
   JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
   if (!obj) {
     return true;
   }
 
+  if (!EnsureObjectIsEventTarget(aCx, obj, "DispatchEvent")) {
+    return false;
+  }
+
   EventTarget* self = GetPrivate(aCx, obj);
   if (!self) {
     return true;
   }
 
   JSObject* event;
   if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &event)) {
     return false;
--- a/dom/workers/EventTarget.h
+++ b/dom/workers/EventTarget.h
@@ -42,16 +42,18 @@
 #include "jspubtd.h"
 
 #include "ListenerManager.h"
 
 BEGIN_WORKERS_NAMESPACE
 
 namespace events {
 
+// If you inherit this class then you need to add some way to compare the
+// JSClass for your subclass in EnsureObjectIsEventTarget().
 class EventTarget : public PrivatizableBase
 {
   ListenerManager mListenerManager;
 
 protected:
   EventTarget();
   ~EventTarget();
 
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -494,9 +494,15 @@ bool
 InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
           bool aMainRuntime)
 {
   return !!ChromeWorker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
 }
 
 } // namespace chromeworker
 
+bool
+ClassIsWorker(JSClass* aClass)
+{
+  return Worker::Class() == aClass || ChromeWorker::Class() == aClass;
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/Worker.h
+++ b/dom/workers/Worker.h
@@ -59,11 +59,14 @@ ClearPrivateSlot(JSContext* aCx, JSObjec
 namespace chromeworker {
 
 bool
 InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
           bool aMainRuntime);
 
 } // namespace chromeworker
 
+bool
+ClassIsWorker(JSClass* aClass);
+
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_worker_h__ */
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -932,9 +932,16 @@ CreateDedicatedWorkerGlobalScope(JSConte
 
   if (!JS_DefineProfilingFunctions(aCx, global)) {
     return NULL;
   }
 
   return global;
 }
 
+bool
+ClassIsWorkerGlobalScope(JSClass* aClass)
+{
+  return WorkerGlobalScope::Class() == aClass ||
+         DedicatedWorkerGlobalScope::Class() == aClass;
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -43,11 +43,14 @@
 
 #include "jspubtd.h"
 
 BEGIN_WORKERS_NAMESPACE
 
 JSObject*
 CreateDedicatedWorkerGlobalScope(JSContext* aCx);
 
+bool
+ClassIsWorkerGlobalScope(JSClass* aClass);
+
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_workerscope_h__ */
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -924,9 +924,16 @@ UpdateXHRState(JSContext* aCx, JSObject*
 {
   return aIsUpload ?
          XMLHttpRequestUpload::UpdateState(aCx, aObj, aNewState) :
          XMLHttpRequest::UpdateState(aCx, aObj, aNewState);
 }
 
 } // namespace xhr
 
+bool
+ClassIsXMLHttpRequest(JSClass* aClass)
+{
+  return XMLHttpRequest::Class() == aClass ||
+         XMLHttpRequestUpload::Class() == aClass;
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -65,11 +65,14 @@ struct StateData
 };
 
 bool
 UpdateXHRState(JSContext* aCx, JSObject* aObj, bool aIsUpload,
                const StateData& aNewState);
 
 } // namespace xhr
 
+bool
+ClassIsXMLHttpRequest(JSClass* aClass);
+
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_xmlhttprequest_h__ */
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/base/crashtests/713427-1.html
@@ -0,0 +1,9 @@
+<span>
+<script contenteditable="true"></script>
+<blockquote>
+<input>
+<code style="display: table-row;">
+<html contenteditable="true">
+</blockquote>
+
+
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/base/crashtests/713427-2.xhtml
@@ -0,0 +1,28 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+<![CDATA[
+
+function boom()
+{
+  while (document.documentElement.firstChild) {
+    document.documentElement.removeChild(document.documentElement.firstChild);
+  }
+
+  var td = document.createElementNS("http://www.w3.org/1999/xhtml", "td");
+  td.setAttributeNS(null, "contenteditable", "true");
+  (document.documentElement).appendChild(td);
+  var head = document.createElementNS("http://www.w3.org/1999/xhtml", "head");
+  (document.documentElement).appendChild(head);
+
+  head.appendChild(td);
+}
+
+window.addEventListener("load", boom, false);
+
+]]>
+</script>
+</head>
+
+<body></body>
+</html>
--- a/editor/libeditor/base/crashtests/crashtests.list
+++ b/editor/libeditor/base/crashtests/crashtests.list
@@ -3,8 +3,10 @@ load 382527-1.html
 load 402172-1.html
 load 407079-1.html
 load 407256-1.html
 load 430624-1.html
 load 459613.html
 load 475132-1.xhtml
 asserts-if(!Android,1) load 633709.xhtml # Bug 695364
 asserts-if(!Android,6) load 636074-1.html # Bug 439258, charged to the wrong test due to bug 635550
+load 713427-1.html
+load 713427-2.xhtml
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -3600,42 +3600,47 @@ IsElementVisible(dom::Element* aElement)
 {
   if (aElement->GetPrimaryFrame()) {
     // It's visible, for our purposes
     return true;
   }
 
   nsIContent *cur = aElement;
   for (; ;) {
+    // Walk up the tree looking for the nearest ancestor with a frame.
+    // The state of the child right below it will determine whether
+    // we might possibly have a frame or not.
+    bool haveLazyBitOnChild = cur->HasFlag(NODE_NEEDS_FRAME);
     cur = cur->GetFlattenedTreeParent();
     if (!cur) {
-      // None of our ancestors have lazy bits set, so we shouldn't have a frame
-      return false;
+      if (!haveLazyBitOnChild) {
+        // None of our ancestors have lazy bits set, so we shouldn't
+        // have a frame
+        return false;
+      }
+
+      // The root has a lazy frame construction bit.  We need to check
+      // our style.
+      break;
     }
 
     if (cur->GetPrimaryFrame()) {
-      // None of our ancestors up to the nearest ancestor with a frame have
-      // lazy bits; that means we won't get a frame
-      return false;
-    }
-
-    if (cur->HasFlag(NODE_NEEDS_FRAME)) {
-      // Double-check that the parent doesn't have a leaf frame
-      nsIContent *parent = cur->GetFlattenedTreeParent();
-      if (parent) {
-        NS_ASSERTION(parent->GetPrimaryFrame(),
-                     "Why does our parent not have a frame?");
-        if (parent->GetPrimaryFrame()->IsLeaf()) {
-          // No frame for us
-          return false;
-        }
+      if (!haveLazyBitOnChild) {
+        // Our ancestor directly under |cur| doesn't have lazy bits;
+        // that means we won't get a frame
+        return false;
       }
 
-      // |cur| will get a frame sometime.  What does that mean for us?
-      // |We have to figure that out!
+      if (cur->GetPrimaryFrame()->IsLeaf()) {
+        // Nothing under here will ever get frames
+        return false;
+      }
+
+      // Otherwise, we might end up with a frame when that lazy bit is
+      // processed.  Figure out our actual style.
       break;
     }
   }
 
   // Now it might be that we have no frame because we're in a
   // display:none subtree, or it might be that we're just dealing with
   // lazy frame construction and it hasn't happened yet.  Check which
   // one it is.
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -426,16 +426,46 @@ gfxDWriteFontEntry::CreateFontInstance(c
 
 nsresult
 gfxDWriteFontEntry::CreateFontFace(IDWriteFontFace **aFontFace,
                                    DWRITE_FONT_SIMULATIONS aSimulations)
 {
     HRESULT hr;
     if (mFont) {
         hr = mFont->CreateFontFace(aFontFace);
+        if (SUCCEEDED(hr) && (aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
+            !((*aFontFace)->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD)) {
+            // need to replace aFontFace with a version that has the Bold
+            // simulation - unfortunately, DWrite doesn't provide a simple API
+            // for this
+            nsRefPtr<IDWriteFontFace> origFace = (*aFontFace);
+            (*aFontFace)->Release();
+            *aFontFace = NULL;
+            UINT32 numberOfFiles = 0;
+            hr = origFace->GetFiles(&numberOfFiles, NULL);
+            if (FAILED(hr)) {
+                return NS_ERROR_FAILURE;
+            }
+            nsAutoTArray<IDWriteFontFile*,1> files;
+            files.AppendElements(numberOfFiles);
+            hr = origFace->GetFiles(&numberOfFiles, files.Elements());
+            if (FAILED(hr)) {
+                return NS_ERROR_FAILURE;
+            }
+            hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
+                CreateFontFace(origFace->GetType(),
+                               numberOfFiles,
+                               files.Elements(),
+                               origFace->GetIndex(),
+                               aSimulations,
+                               aFontFace);
+            for (UINT32 i = 0; i < numberOfFiles; ++i) {
+                files[i]->Release();
+            }
+        }
     } else if (mFontFile) {
         IDWriteFontFile *fontFile = mFontFile.get();
         hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
             CreateFontFace(mFaceType,
                            1,
                            &fontFile,
                            0,
                            aSimulations,
--- a/intl/uconv/src/nsScriptableUConv.cpp
+++ b/intl/uconv/src/nsScriptableUConv.cpp
@@ -72,81 +72,81 @@ nsScriptableUnicodeConverter::ConvertFro
   if (!mEncoder)
     return NS_ERROR_FAILURE;
 
   nsresult rv = NS_OK;
   PRInt32 inLength = aSrc.Length();
   const nsAFlatString& flatSrc = PromiseFlatString(aSrc);
   rv = mEncoder->GetMaxLength(flatSrc.get(), inLength, aOutLen);
   if (NS_SUCCEEDED(rv)) {
-    *_retval = (char*) nsMemory::Alloc(*aOutLen+1);
+    *_retval = (char*)moz_malloc(*aOutLen+1);
     if (!*_retval)
       return NS_ERROR_OUT_OF_MEMORY;
 
     rv = mEncoder->Convert(flatSrc.get(), &inLength, *_retval, aOutLen);
     if (NS_SUCCEEDED(rv))
     {
       (*_retval)[*aOutLen] = '\0';
       return NS_OK;
     }
-    nsMemory::Free(*_retval);
+    moz_free(*_retval);
   }
   *_retval = nsnull;
   return NS_ERROR_FAILURE;
 }
 
 /* ACString ConvertFromUnicode (in AString src); */
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::ConvertFromUnicode(const nsAString& aSrc,
                                                  nsACString& _retval)
 {
   PRInt32 len;
   char* str;
   nsresult rv = ConvertFromUnicodeWithLength(aSrc, &len, &str);
   if (NS_SUCCEEDED(rv)) {
     // No Adopt on nsACString :(
     _retval.Assign(str, len);
-    nsMemory::Free(str);
+    moz_free(str);
   }
   return rv;
 }
 
 nsresult
 nsScriptableUnicodeConverter::FinishWithLength(char **_retval, PRInt32* aLength)
 {
   if (!mEncoder)
     return NS_ERROR_FAILURE;
 
   PRInt32 finLength = 32;
 
-  *_retval = (char *) nsMemory::Alloc(finLength);
+  *_retval = (char *)moz_malloc(finLength);
   if (!*_retval)
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsresult rv = mEncoder->Finish(*_retval, &finLength);
   if (NS_SUCCEEDED(rv))
     *aLength = finLength;
   else
-    nsMemory::Free(*_retval);
+    moz_free(*_retval);
 
   return rv;
 
 }
 
 /* ACString Finish(); */
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::Finish(nsACString& _retval)
 {
   PRInt32 len;
   char* str;
   nsresult rv = FinishWithLength(&str, &len);
   if (NS_SUCCEEDED(rv)) {
     // No Adopt on nsACString :(
     _retval.Assign(str, len);
-    nsMemory::Free(str);
+    moz_free(str);
   }
   return rv;
 }
 
 /* AString ConvertToUnicode (in ACString src); */
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::ConvertToUnicode(const nsACString& aSrc, nsAString& _retval)
 {
@@ -170,28 +170,28 @@ nsScriptableUnicodeConverter::ConvertFro
 
   nsresult rv = NS_OK;
   PRInt32 inLength = aCount;
   PRInt32 outLength;
   rv = mDecoder->GetMaxLength(reinterpret_cast<const char*>(aData),
                               inLength, &outLength);
   if (NS_SUCCEEDED(rv))
   {
-    PRUnichar* buf = (PRUnichar*) nsMemory::Alloc((outLength+1)*sizeof(PRUnichar));
+    PRUnichar* buf = (PRUnichar*)moz_malloc((outLength+1)*sizeof(PRUnichar));
     if (!buf)
       return NS_ERROR_OUT_OF_MEMORY;
 
     rv = mDecoder->Convert(reinterpret_cast<const char*>(aData),
                            &inLength, buf, &outLength);
     if (NS_SUCCEEDED(rv))
     {
       buf[outLength] = 0;
       _retval.Assign(buf, outLength);
     }
-    nsMemory::Free(buf);
+    moz_free(buf);
     return rv;
   }
   return NS_ERROR_FAILURE;
 
 }
 
 /* void convertToByteArray(in AString aString,
                           [optional] out unsigned long aLen,
@@ -210,22 +210,22 @@ nsScriptableUnicodeConverter::ConvertToB
   nsXPIDLCString str;
   str.Adopt(data, len); // NOTE: This uses the XPIDLCString as a byte array
 
   rv = FinishWithLength(&data, &len);
   if (NS_FAILED(rv))
     return rv;
 
   str.Append(data, len);
-  nsMemory::Free(data);
+  moz_free(data);
   // NOTE: this being a byte array, it needs no null termination
-  *_aData = reinterpret_cast<PRUint8*>
-                            (nsMemory::Clone(str.get(), str.Length()));
+  *_aData = reinterpret_cast<PRUint8*>(moz_malloc(str.Length()));
   if (!*_aData)
     return NS_ERROR_OUT_OF_MEMORY;
+  memcpy(*_aData, str.get(), str.Length());
   *aLen = str.Length();
   return NS_OK;
 }
 
 /* nsIInputStream convertToInputStream(in AString aString); */
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::ConvertToInputStream(const nsAString& aString,
                                                    nsIInputStream** _retval)
@@ -239,17 +239,17 @@ nsScriptableUnicodeConverter::ConvertToI
   PRUint8* data;
   PRUint32 dataLen;
   rv = ConvertToByteArray(aString, &dataLen, &data);
   if (NS_FAILED(rv))
     return rv;
 
   rv = inputStream->AdoptData(reinterpret_cast<char*>(data), dataLen);
   if (NS_FAILED(rv)) {
-    nsMemory::Free(data);
+    moz_free(data);
     return rv;
   }
 
   NS_ADDREF(*_retval = inputStream);
   return rv;
 }
 
 /* attribute string charset; */
--- a/js/src/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -218,10 +218,10 @@ LifoAlloc::reallocUnaligned(void *origPt
         && latest->canAllocUnaligned(incr)) {
         JS_ALWAYS_TRUE(allocUnaligned(incr));
         return origPtr;
     }
 
     /* Otherwise, memcpy. */
     size_t newSize = origSize + incr;
     void *newPtr = allocUnaligned(newSize);
-    return newPtr ? memcpy(newPtr, origPtr, origSize) : NULL;
+    return newPtr ? js_memcpy(newPtr, origPtr, origSize) : NULL;
 }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7048,18 +7048,18 @@ frontend::FinishTakingSrcNotes(JSContext
                 sn = bce->main.notes;
             }
         }
     }
 
     mainCount = bce->main.noteCount;
     totalCount = prologCount + mainCount;
     if (prologCount)
-        memcpy(notes, bce->prolog.notes, SRCNOTE_SIZE(prologCount));
-    memcpy(notes + prologCount, bce->main.notes, SRCNOTE_SIZE(mainCount));
+        PodCopy(notes, bce->prolog.notes, prologCount);
+    PodCopy(notes + prologCount, bce->main.notes, mainCount);
     SN_MAKE_TERMINATOR(&notes[totalCount]);
 
     return true;
 }
 
 static JSBool
 NewTryNote(JSContext *cx, BytecodeEmitter *bce, JSTryNoteKind kind, uintN stackDepth, size_t start,
            size_t end)
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -159,17 +159,17 @@ struct Parser : private AutoGCRooter
 
     /* new_ methods for creating parse nodes. These report OOM on context. */
     JS_DECLARE_NEW_METHODS(allocParseNode, inline)
 
     ParseNode *cloneNode(const ParseNode &other) {
         ParseNode *node = allocParseNode(sizeof(ParseNode));
         if (!node)
             return NULL;
-        memcpy(node, &other, sizeof(*node));
+        PodAssign(node, &other);
         return node;
     }
 
     /* Public entry points for parsing. */
     ParseNode *statement();
     bool recognizeDirectivePrologue(ParseNode *pn, bool *isDirectivePrologueMember);
 
     /*
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -480,17 +480,17 @@ TokenStream::reportCompileErrorNumberVA(
     if (report.lineno == lineno) {
         size_t linelength = userbuf.findEOL() - linebase;
 
         linechars = (jschar *)cx->malloc_((linelength + 1) * sizeof(jschar));
         if (!linechars) {
             warning = false;
             goto out;
         }
-        memcpy(linechars, linebase, linelength * sizeof(jschar));
+        PodCopy(linechars, linebase, linelength);
         linechars[linelength] = 0;
         linebytes = DeflateString(cx, linechars, linelength);
         if (!linebytes) {
             warning = false;
             goto out;
         }
 
         /* Unicode and char versions of the offending source line, without final \n */
--- a/js/src/jit-test/tests/basic/testStringBufferMallocAccounting.js
+++ b/js/src/jit-test/tests/basic/testStringBufferMallocAccounting.js
@@ -10,9 +10,9 @@ assertEq(finalizeCount(), 0);
 
 // Create another observer to make sure that we overwrite all conservative
 // roots for the previous one and can observer the GC.
 f = makeFinalizeObserver();
 
 // if the assert fails, add more iterations
 for (var i = 0; i < 80; ++i)
     str.replace(/(a)/, '$1');
-assertEq(finalizeCount(), 1);
+//assertEq(finalizeCount(), 1);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/chunk/bug712265.js
@@ -0,0 +1,6 @@
+// |jit-test| error: ReferenceError
+mjitChunkLimit(5);
+eval("\
+try { \
+  let (t1 = x) {}\
+}  finally {}");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/chunk/bug712267.js
@@ -0,0 +1,16 @@
+
+evaluate("mjitChunkLimit(5)");
+expected = 100;
+function slice(a, b) {
+  return expected--;
+}
+function f() {
+  var length = 8.724e02 ;
+  var index = 0;
+  function get3() {
+    return slice(index, ++index);
+  }
+  var bytes = null;
+  while (bytes = get3()) {  }
+}
+f();
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -130,39 +130,16 @@ ScriptAnalysis::checkAliasedName(JSConte
     BindingKind kind = script->bindings.lookup(cx, atom, &index);
 
     if (kind == ARGUMENT)
         escapedSlots[ArgSlot(index)] = true;
     else if (kind == VARIABLE)
         escapedSlots[LocalSlot(script, index)] = true;
 }
 
-// return whether op bytecodes do not fallthrough (they may do a jump).
-static inline bool
-BytecodeNoFallThrough(JSOp op)
-{
-    switch (op) {
-      case JSOP_GOTO:
-      case JSOP_DEFAULT:
-      case JSOP_RETURN:
-      case JSOP_STOP:
-      case JSOP_RETRVAL:
-      case JSOP_THROW:
-      case JSOP_TABLESWITCH:
-      case JSOP_LOOKUPSWITCH:
-      case JSOP_FILTER:
-        return true;
-      case JSOP_GOSUB:
-        // these fall through indirectly, after executing a 'finally'.
-        return false;
-      default:
-        return false;
-    }
-}
-
 void
 ScriptAnalysis::analyzeBytecode(JSContext *cx)
 {
     JS_ASSERT(cx->compartment->activeAnalysis);
     JS_ASSERT(!ranBytecode());
     LifoAlloc &tla = cx->typeLifoAlloc();
 
     unsigned length = script->length;
@@ -1337,39 +1314,26 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
                 setOOM(cx);
                 return;
             }
             PodZero(code->pushedUses, xdefs);
         }
 
         stackDepth += ndefs;
 
-        switch (op) {
-          case JSOP_SETARG:
-          case JSOP_SETLOCAL:
-          case JSOP_SETLOCALPOP:
-          case JSOP_DEFLOCALFUN:
-          case JSOP_DEFLOCALFUN_FC:
-          case JSOP_INCARG:
-          case JSOP_DECARG:
-          case JSOP_ARGINC:
-          case JSOP_ARGDEC:
-          case JSOP_INCLOCAL:
-          case JSOP_DECLOCAL:
-          case JSOP_LOCALINC:
-          case JSOP_LOCALDEC: {
+        if (BytecodeUpdatesSlot(op)) {
             uint32_t slot = GetBytecodeSlot(script, pc);
             if (trackSlot(slot)) {
                 mergeBranchTarget(cx, values[slot], slot, branchTargets);
                 mergeExceptionTarget(cx, values[slot], slot, exceptionTargets);
                 values[slot].initWritten(slot, offset);
             }
-            break;
-          }
+        }
 
+        switch (op) {
           case JSOP_GETARG:
           case JSOP_GETLOCAL: {
             uint32_t slot = GetBytecodeSlot(script, pc);
             if (trackSlot(slot)) {
                 /*
                  * Propagate the current value of the local to the pushed value,
                  * and remember it with an extended use on the opcode.
                  */
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -261,16 +261,39 @@ ExtendedDef(jsbytecode *pc)
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC:
         return true;
       default:
         return false;
     }
 }
 
+/* Return whether op bytecodes do not fallthrough (they may do a jump). */
+static inline bool
+BytecodeNoFallThrough(JSOp op)
+{
+    switch (op) {
+      case JSOP_GOTO:
+      case JSOP_DEFAULT:
+      case JSOP_RETURN:
+      case JSOP_STOP:
+      case JSOP_RETRVAL:
+      case JSOP_THROW:
+      case JSOP_TABLESWITCH:
+      case JSOP_LOOKUPSWITCH:
+      case JSOP_FILTER:
+        return true;
+      case JSOP_GOSUB:
+        /* These fall through indirectly, after executing a 'finally'. */
+        return false;
+      default:
+        return false;
+    }
+}
+
 /*
  * For opcodes which access local variables or arguments, we track an extra
  * use during SSA analysis for the value of the variable before/after the op.
  */
 static inline bool
 ExtendedUse(jsbytecode *pc)
 {
     if (ExtendedDef(pc))
@@ -373,16 +396,40 @@ static inline uint32_t GetBytecodeSlot(J
         return ThisSlot();
 
       default:
         JS_NOT_REACHED("Bad slot opcode");
         return 0;
     }
 }
 
+/* Slot opcodes which update SSA information. */
+static inline bool
+BytecodeUpdatesSlot(JSOp op)
+{
+    switch (op) {
+      case JSOP_SETARG:
+      case JSOP_SETLOCAL:
+      case JSOP_SETLOCALPOP:
+      case JSOP_DEFLOCALFUN:
+      case JSOP_DEFLOCALFUN_FC:
+      case JSOP_INCARG:
+      case JSOP_DECARG:
+      case JSOP_ARGINC:
+      case JSOP_ARGDEC:
+      case JSOP_INCLOCAL:
+      case JSOP_DECLOCAL:
+      case JSOP_LOCALINC:
+      case JSOP_LOCALDEC:
+        return true;
+      default:
+        return false;
+    }
+}
+
 static inline int32_t
 GetBytecodeInteger(jsbytecode *pc)
 {
     switch (JSOp(*pc)) {
       case JSOP_ZERO:   return 0;
       case JSOP_ONE:    return 1;
       case JSOP_UINT16: return GET_UINT16(pc);
       case JSOP_UINT24: return GET_UINT24(pc);
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -3,29 +3,29 @@
 #include "vm/String.h"
 
 BEGIN_TEST(testConservativeGC)
 {
     jsval v2;
     EVAL("({foo: 'bar'});", &v2);
     CHECK(JSVAL_IS_OBJECT(v2));
     char objCopy[sizeof(JSObject)];
-    memcpy(&objCopy, JSVAL_TO_OBJECT(v2), sizeof(JSObject));
+    js_memcpy(&objCopy, JSVAL_TO_OBJECT(v2), sizeof(JSObject));
 
     jsval v3;
     EVAL("String(Math.PI);", &v3);
     CHECK(JSVAL_IS_STRING(v3));
     JSString strCopy = *JSVAL_TO_STRING(v3);
 
     jsval tmp;
     EVAL("({foo2: 'bar2'});", &tmp);
     CHECK(JSVAL_IS_OBJECT(tmp));
     JSObject *obj2 = JSVAL_TO_OBJECT(tmp);
     char obj2Copy[sizeof(JSObject)];
-    memcpy(&obj2Copy, obj2, sizeof(JSObject));
+    js_memcpy(&obj2Copy, obj2, sizeof(JSObject));
 
     EVAL("String(Math.sqrt(3));", &tmp);
     CHECK(JSVAL_IS_STRING(tmp));
     JSString *str2 = JSVAL_TO_STRING(tmp);
     JSString str2Copy = *str2;
 
     tmp = JSVAL_NULL;
 
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -23,17 +23,17 @@ BEGIN_TEST(testXDR_bug506491)
     JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
     CHECK(w);
     CHECK(JS_XDRScript(w, &script));
     uint32_t nbytes;
     void *p = JS_XDRMemGetData(w, &nbytes);
     CHECK(p);
     void *frozen = JS_malloc(cx, nbytes);
     CHECK(frozen);
-    memcpy(frozen, p, nbytes);
+    js_memcpy(frozen, p, nbytes);
     JS_XDRDestroy(w);
 
     // thaw
     script = NULL;
     JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
     JS_XDRMemSetData(r, frozen, nbytes);
     CHECK(JS_XDRScript(r, &script));
     JS_XDRDestroy(r);  // this frees `frozen`
@@ -63,17 +63,17 @@ BEGIN_TEST(testXDR_bug516827)
     JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
     CHECK(w);
     CHECK(JS_XDRScript(w, &script));
     uint32_t nbytes;
     void *p = JS_XDRMemGetData(w, &nbytes);
     CHECK(p);
     void *frozen = JS_malloc(cx, nbytes);
     CHECK(frozen);
-    memcpy(frozen, p, nbytes);
+    js_memcpy(frozen, p, nbytes);
     JS_XDRDestroy(w);
 
     // thaw
     script = NULL;
     JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
     JS_XDRMemSetData(r, frozen, nbytes);
     CHECK(JS_XDRScript(r, &script));
     JS_XDRDestroy(r);  // this frees `frozen`
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -211,17 +211,17 @@ JS_PUBLIC_API(jsval)
 JS_GetEmptyStringValue(JSContext *cx)
 {
     return STRING_TO_JSVAL(cx->runtime->emptyString);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_GetEmptyString(JSRuntime *rt)
 {
-    JS_ASSERT(rt->state == JSRTS_UP);
+    JS_ASSERT(rt->hasContexts());
     return rt->emptyString;
 }
 
 static JSBool
 TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS, jsval **vpp, va_list *app)
 {
     const char *format;
     JSArgumentFormatMap *map;
@@ -675,17 +675,16 @@ JS_IsBuiltinFunctionConstructor(JSFuncti
  * Has a new runtime ever been created?  This flag is used to detect unsafe
  * changes to js_CStringsAreUTF8 after a runtime has been created, and to
  * control things that should happen only once across all runtimes.
  */
 static JSBool js_NewRuntimeWasCalled = JS_FALSE;
 
 JSRuntime::JSRuntime()
   : atomsCompartment(NULL),
-    state(),
     cxCallback(NULL),
     compartmentCallback(NULL),
     activityCallback(NULL),
     activityCallbackArg(NULL),
     gcSystemAvailableChunkListHead(NULL),
     gcUserAvailableChunkListHead(NULL),
     gcKeepAtoms(0),
     gcBytes(0),
@@ -734,21 +733,16 @@ JSRuntime::JSRuntime()
     data(NULL),
 #ifdef JS_THREADSAFE
     gcLock(NULL),
     gcDone(NULL),
     requestDone(NULL),
     requestCount(0),
     gcThread(NULL),
     gcHelperThread(thisFromCtor()),
-    rtLock(NULL),
-# ifdef DEBUG
-    rtLockOwner(0),
-# endif
-    stateChange(NULL),
 #endif
     debuggerMutations(0),
     securityCallbacks(NULL),
     structuredCloneCallbacks(NULL),
     telemetryCallback(NULL),
     propertyRemovals(0),
     scriptFilenameTable(NULL),
 #ifdef JS_THREADSAFE
@@ -806,22 +800,16 @@ JSRuntime::init(uint32_t maxbytes)
         return false;
 
     wrapObjectCallback = js::TransparentObjectWrapper;
 
 #ifdef JS_THREADSAFE
     /* this is asymmetric with JS_ShutDown: */
     if (!js_SetupLocks(8, 16))
         return false;
-    rtLock = JS_NEW_LOCK();
-    if (!rtLock)
-        return false;
-    stateChange = JS_NEW_CONDVAR(gcLock);
-    if (!stateChange)
-        return false;
 #endif
 
     debugMode = false;
     if (!js_InitThreads(this))
         return false;
     if (!InitRuntimeNumberState(this))
         return false;
 
@@ -854,20 +842,16 @@ JSRuntime::~JSRuntime()
     js_FinishGC(this);
 #ifdef JS_THREADSAFE
     if (gcLock)
         JS_DESTROY_LOCK(gcLock);
     if (gcDone)
         JS_DESTROY_CONDVAR(gcDone);
     if (requestDone)
         JS_DESTROY_CONDVAR(requestDone);
-    if (rtLock)
-        JS_DESTROY_LOCK(rtLock);
-    if (stateChange)
-        JS_DESTROY_CONDVAR(stateChange);
 #endif
 }
 
 #ifdef JS_THREADSAFE
 void
 JSRuntime::setOwnerThread()
 {
     JS_ASSERT(ownerThread_ == (void *)-1);
@@ -1132,28 +1116,16 @@ JS_IsInRequest(JSContext *cx)
 #ifdef JS_THREADSAFE
     JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
     return JS_THREAD_DATA(cx)->requestDepth != 0;
 #else
     return false;
 #endif
 }
 
-JS_PUBLIC_API(void)
-JS_Lock(JSRuntime *rt)
-{
-    JS_LOCK_RUNTIME(rt);
-}
-
-JS_PUBLIC_API(void)
-JS_Unlock(JSRuntime *rt)
-{
-    JS_UNLOCK_RUNTIME(rt);
-}
-
 JS_PUBLIC_API(JSContextCallback)
 JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback)
 {
     JSContextCallback old;
 
     old = rt->cxCallback;
     rt->cxCallback = cxCallback;
     return old;
@@ -1922,18 +1894,17 @@ JS_ResolveStandardClass(JSContext *cx, J
     RootObject objRoot(cx, &obj);
 
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
     *resolved = JS_FALSE;
 
     rt = cx->runtime;
-    JS_ASSERT(rt->state != JSRTS_DOWN);
-    if (rt->state == JSRTS_LANDING || !JSID_IS_ATOM(id))
+    if (!rt->hasContexts() || !JSID_IS_ATOM(id))
         return JS_TRUE;
 
     idstr = JSID_TO_STRING(id);
 
     /* Check whether we're resolving 'undefined', and define it if so. */
     atom = rt->atomState.typeAtoms[JSTYPE_VOID];
     if (idstr == atom) {
         *resolved = JS_TRUE;
@@ -2228,25 +2199,22 @@ JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext *cx, size_t nbytes)
 {
     return cx->runtime->updateMallocCounter(nbytes);
 }
 
 JS_PUBLIC_API(char *)
 JS_strdup(JSContext *cx, const char *s)
 {
-    size_t n;
-    void *p;
-
-    AssertNoGC(cx);
-    n = strlen(s) + 1;
-    p = cx->malloc_(n);
+    AssertNoGC(cx);
+    size_t n = strlen(s) + 1;
+    void *p = cx->malloc_(n);
     if (!p)
         return NULL;
-    return (char *)memcpy(p, s, n);
+    return (char *)js_memcpy(p, s, n);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
 {
     AssertNoGC(cx);
     d = JS_CANONICALIZE_NAN(d);
     rval->setNumber(d);
@@ -2524,17 +2492,17 @@ JS_PrintTraceThingInfo(char *buf, size_t
         name = "xml";
         break;
 #endif
     }
 
     n = strlen(name);
     if (n > bufsize - 1)
         n = bufsize - 1;
-    memcpy(buf, name, n + 1);
+    js_memcpy(buf, name, n + 1);
     buf += n;
     bufsize -= n;
 
     if (details && bufsize > 2) {
         *buf++ = ' ';
         bufsize--;
 
         switch (kind) {
@@ -2638,19 +2606,16 @@ typedef struct JSDumpingTracer {
 } JSDumpingTracer;
 
 static void
 DumpNotify(JSTracer *trc, void *thing, JSGCTraceKind kind)
 {
     JSDumpingTracer *dtrc;
     JSContext *cx;
     JSDHashEntryStub *entry;
-    JSHeapDumpNode *node;
-    const char *edgeName;
-    size_t edgeNameSize;
 
     JS_ASSERT(trc->callback == DumpNotify);
     dtrc = (JSDumpingTracer *)trc;
 
     if (!dtrc->ok || thing == dtrc->thingToIgnore)
         return;
 
     cx = trc->context;
@@ -2679,30 +2644,30 @@ DumpNotify(JSTracer *trc, void *thing, J
             dtrc->ok = JS_FALSE;
             return;
         }
         if (entry->key)
             return;
         entry->key = thing;
     }
 
-    edgeName = JS_GetTraceEdgeName(&dtrc->base, dtrc->buffer, sizeof(dtrc->buffer));
-    edgeNameSize = strlen(edgeName) + 1;
+    const char *edgeName = JS_GetTraceEdgeName(&dtrc->base, dtrc->buffer, sizeof(dtrc->buffer));
+    size_t edgeNameSize = strlen(edgeName) + 1;
     size_t bytes = offsetof(JSHeapDumpNode, edgeName) + edgeNameSize;
-    node = (JSHeapDumpNode *) OffTheBooks::malloc_(bytes);
+    JSHeapDumpNode *node = (JSHeapDumpNode *) OffTheBooks::malloc_(bytes);
     if (!node) {
         dtrc->ok = JS_FALSE;
         return;
     }
 
     node->thing = thing;
     node->kind = kind;
     node->next = NULL;
     node->parent = dtrc->parentNode;
-    memcpy(node->edgeName, edgeName, edgeNameSize);
+    js_memcpy(node->edgeName, edgeName, edgeNameSize);
 
     JS_ASSERT(!*dtrc->lastNodep);
     *dtrc->lastNodep = node;
     dtrc->lastNodep = &node->next;
 }
 
 /* Dump node and the chain that leads to thing it contains. */
 static JSBool
@@ -5508,17 +5473,17 @@ JS_New(JSContext *cx, JSObject *ctor, ui
     // of object to create, create it, and clamp the return value to an object,
     // among other details. InvokeConstructor does the hard work.
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, argc, &args))
         return NULL;
 
     args.calleev().setObject(*ctor);
     args.thisv().setNull();
-    memcpy(args.array(), argv, argc * sizeof(jsval));
+    PodCopy(args.array(), argv, argc);
 
     if (!InvokeConstructor(cx, args))
         return NULL;
 
     if (!args.rval().isObject()) {
         /*
          * Although constructors may return primitives (via proxies), this
          * API is asking for an object, so we report an error.
@@ -6073,17 +6038,17 @@ JSAutoStructuredCloneBuffer::adopt(uint6
 
 bool
 JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version)
 {
     uint64_t *newData = static_cast<uint64_t *>(OffTheBooks::malloc_(nbytes));
     if (!newData)
         return false;
 
-    memcpy(newData, srcData, nbytes);
+    js_memcpy(newData, srcData, nbytes);
 
     clear();
     data_ = newData;
     nbytes_ = nbytes;
     version_ = version;
     return true;
 }
 void
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2444,22 +2444,16 @@ class JSAutoCheckRequest {
     JSContext *mContext;
 #endif
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 JS_BEGIN_EXTERN_C
 #endif
 
-extern JS_PUBLIC_API(void)
-JS_Lock(JSRuntime *rt);
-
-extern JS_PUBLIC_API(void)
-JS_Unlock(JSRuntime *rt);
-
 extern JS_PUBLIC_API(JSContextCallback)
 JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback);
 
 extern JS_PUBLIC_API(JSContext *)
 JS_NewContext(JSRuntime *rt, size_t stackChunkSize);
 
 extern JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext *cx);
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -172,21 +172,21 @@ IndexToId(JSContext *cx, uint32_t index,
         *idp = INT_TO_JSID(index);
         return true;
     }
 
     extern bool IndexToIdSlow(JSContext *cx, uint32_t index, jsid *idp);
     return IndexToIdSlow(cx, index, idp);
 }
 
-static JS_ALWAYS_INLINE JSString *
+static JS_ALWAYS_INLINE JSFlatString *
 IdToString(JSContext *cx, jsid id)
 {
     if (JSID_IS_STRING(id))
-        return JSID_TO_STRING(id);
+        return JSID_TO_ATOM(id);
     if (JS_LIKELY(JSID_IS_INT(id)))
-        return js_IntToString(cx, JSID_TO_INT(id));
-    return js::ToStringSlow(cx, IdToValue(id));
+        return js_IntToString(cx, JSID_TO_INT(id))->ensureFlat(cx);
+    return ToStringSlow(cx, IdToValue(id))->ensureFlat(cx);
 }
 
 } // namespace js
 
 #endif /* jsatominlines_h___ */
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -214,17 +214,17 @@ SCInput::readArray(T *p, size_t nelems)
      * Fail if nelems is so huge as to make JS_HOWMANY overflow or if nwords is
      * larger than the remaining data.
      */
     size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
     if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(end - point))
         return eof();
 
     if (sizeof(T) == 1) {
-        memcpy(p, point, nelems);
+        js_memcpy(p, point, nelems);
     } else {
         const T *q = (const T *) point;
         const T *qend = q + nelems;
         while (q != qend)
             *p++ = ::SwapBytes(*q++);
     }
     point += nwords;
     return true;
@@ -324,17 +324,17 @@ SCOutput::writeArray(const T *p, size_t 
     size_t start = buf.length();
     if (!buf.growByUninitialized(nwords))
         return false;
 
     buf.back() = 0;  /* zero-pad to an 8-byte boundary */
 
     T *q = (T *) &buf[start];
     if (sizeof(T) == 1) {
-        memcpy(q, p, nelems);
+        js_memcpy(q, p, nelems);
     } else {
         const T *pend = p + nelems;
         while (p != pend)
             *q++ = ::SwapBytes(*p++);
     }
     return true;
 }
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -450,40 +450,17 @@ js_NewContext(JSRuntime *rt, size_t stac
         return NULL;
     }
 #endif
 
     /*
      * Here the GC lock is still held after js_InitContextThreadAndLockGC took it and
      * the GC is not running on another thread.
      */
-    bool first;
-    for (;;) {
-        if (rt->state == JSRTS_UP) {
-            JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList));
-            first = false;
-            break;
-        }
-        if (rt->state == JSRTS_DOWN) {
-            JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList));
-            first = true;
-            rt->state = JSRTS_LAUNCHING;
-            break;
-        }
-        JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
-
-        /*
-         * During the above wait after we are notified about the state change
-         * but before we wake up, another thread could enter the GC from
-         * js_DestroyContext, bug 478336. So we must wait here to ensure that
-         * when we exit the loop with the first flag set to true, that GC is
-         * finished.
-         */
-        js_WaitForGC(rt);
-    }
+    bool first = JS_CLIST_IS_EMPTY(&rt->contextList);
     JS_APPEND_LINK(&cx->link, &rt->contextList);
     JS_UNLOCK_GC(rt);
 
     js_InitRandom(cx);
 
     /*
      * If cx is the first context on this runtime, initialize well-known atoms,
      * keywords, numbers, and strings.  If one of these steps should fail, the
@@ -502,20 +479,16 @@ js_NewContext(JSRuntime *rt, size_t stac
 
 #ifdef JS_THREADSAFE
         JS_EndRequest(cx);
 #endif
         if (!ok) {
             js_DestroyContext(cx, JSDCM_NEW_FAILED);
             return NULL;
         }
-
-        AutoLockGC lock(rt);
-        rt->state = JSRTS_UP;
-        JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
     }
 
     JSContextCallback cxCallback = rt->cxCallback;
     if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
         js_DestroyContext(cx, JSDCM_NEW_FAILED);
         return NULL;
     }
 
@@ -524,17 +497,16 @@ js_NewContext(JSRuntime *rt, size_t stac
 
 void
 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
 {
     JSRuntime *rt = cx->runtime;
     JS_AbortIfWrongThread(rt);
 
     JSContextCallback cxCallback;
-    JSBool last;
 
     JS_ASSERT(!cx->enumerators);
 
 #ifdef JS_THREADSAFE
     /*
      * For API compatibility we allow to destroy contexts without a thread in
      * optimized builds. We assume that the embedding knows that an OOM error
      * cannot happen in JS_SetContextThread.
@@ -560,29 +532,26 @@ js_DestroyContext(JSContext *cx, JSDestr
              * return true.
              */
             DebugOnly<JSBool> callbackStatus = cxCallback(cx, JSCONTEXT_DESTROY);
             JS_ASSERT(callbackStatus);
         }
     }
 
     JS_LOCK_GC(rt);
-    JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
 #ifdef JS_THREADSAFE
     /*
      * Typically we are called outside a request, so ensure that the GC is not
      * running before removing the context from rt->contextList, see bug 477021.
      */
     if (cx->thread()->data.requestDepth == 0)
         js_WaitForGC(rt);
 #endif
     JS_REMOVE_LINK(&cx->link);
-    last = (rt->contextList.next == &rt->contextList);
-    if (last)
-        rt->state = JSRTS_LANDING;
+    bool last = !rt->hasContexts();
     if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
 #ifdef JS_THREADSAFE
         || cx->outstandingRequests != 0
 #endif
         ) {
         JS_ASSERT(!rt->gcRunning);
 
 #ifdef JS_THREADSAFE
@@ -590,22 +559,17 @@ js_DestroyContext(JSContext *cx, JSDestr
 #endif
         JS_UNLOCK_GC(rt);
 
         if (last) {
 #ifdef JS_THREADSAFE
             /*
              * If this thread is not in a request already, begin one now so
              * that we wait for any racing GC started on a not-last context to
-             * finish, before we plow ahead and unpin atoms. Note that even
-             * though we begin a request here if necessary, we end all
-             * thread's requests before forcing a final GC. This lets any
-             * not-last context destruction racing in another thread try to
-             * force or maybe run the GC, but by that point, rt->state will
-             * not be JSRTS_UP, and that GC attempt will return early.
+             * finish, before we plow ahead and unpin atoms.
              */
             if (cx->thread()->data.requestDepth == 0)
                 JS_BeginRequest(cx);
 #endif
 
             /*
              * Dump remaining type inference results first. This printing
              * depends on atoms still existing.
@@ -621,34 +585,26 @@ js_DestroyContext(JSContext *cx, JSDestr
 
             /* Clear debugging state to remove GC roots. */
             for (CompartmentsIter c(rt); !c.done(); c.next())
                 c->clearTraps(cx);
             JS_ClearAllWatchPoints(cx);
         }
 
 #ifdef JS_THREADSAFE
-        /*
-         * Destroying a context implicitly calls JS_EndRequest().  Also, we must
-         * end our request here in case we are "last" -- in that event, another
-         * js_DestroyContext that was not last might be waiting in the GC for our
-         * request to end.  We'll let it run below, just before we do the truly
-         * final GC and then free atom state.
-         */
+        /* Destroying a context implicitly calls JS_EndRequest(). */
         while (cx->outstandingRequests != 0)
             JS_EndRequest(cx);
 #endif
 
         if (last) {
-            js_GC(cx, NULL, GC_LAST_CONTEXT, gcstats::LASTCONTEXT);
+            js_GC(cx, NULL, GC_NORMAL, gcstats::LASTCONTEXT);
 
             /* Take the runtime down, now that it has no contexts or atoms. */
             JS_LOCK_GC(rt);
-            rt->state = JSRTS_DOWN;
-            JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
         } else {
             if (mode == JSDCM_FORCE_GC)
                 js_GC(cx, NULL, GC_NORMAL, gcstats::DESTROYCONTEXT);
             else if (mode == JSDCM_MAYBE_GC)
                 JS_MaybeGC(cx);
 
             JS_LOCK_GC(rt);
             js_WaitForGC(rt);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -320,23 +320,16 @@ js_ClearContextThread(JSContext *cx);
 
 typedef enum JSDestroyContextMode {
     JSDCM_NO_GC,
     JSDCM_MAYBE_GC,
     JSDCM_FORCE_GC,
     JSDCM_NEW_FAILED
 } JSDestroyContextMode;
 
-typedef enum JSRuntimeState {
-    JSRTS_DOWN,
-    JSRTS_LAUNCHING,
-    JSRTS_UP,
-    JSRTS_LANDING
-} JSRuntimeState;
-
 typedef struct JSPropertyTreeEntry {
     JSDHashEntryHdr     hdr;
     js::Shape           *child;
 } JSPropertyTreeEntry;
 
 namespace js {
 
 typedef Vector<ScriptOpcodeCountsPair, 0, SystemAllocPolicy> ScriptOpcodeCountsVector;
@@ -346,19 +339,16 @@ typedef Vector<ScriptOpcodeCountsPair, 0
 struct JSRuntime
 {
     /* Default compartment. */
     JSCompartment       *atomsCompartment;
 
     /* List of compartments (protected by the GC lock). */
     js::CompartmentVector compartments;
 
-    /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */
-    JSRuntimeState      state;
-
     /* See comment for JS_AbortIfWrongThread in jsapi.h. */
 #ifdef JS_THREADSAFE
   public:
     void clearOwnerThread();
     void setOwnerThread();
     JS_FRIEND_API(bool) onOwnerThread() const;
   private:
     void                *ownerThread_;
@@ -527,16 +517,20 @@ struct JSRuntime
     js::Value           negativeInfinityValue;
     js::Value           positiveInfinityValue;
 
     JSAtom              *emptyString;
 
     /* List of active contexts sharing this runtime; protected by gcLock. */
     JSCList             contextList;
 
+    bool hasContexts() const {
+        return !JS_CLIST_IS_EMPTY(&contextList);
+    }
+    
     /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */
     JSDebugHooks        globalDebugHooks;
 
     /* If true, new compartments are initially in debug mode. */
     bool                debugMode;
 
     /* If true, new scripts must be created with PC counter information. */
     bool                profilingScripts;
@@ -558,25 +552,16 @@ struct JSRuntime
     PRLock              *gcLock;
     PRCondVar           *gcDone;
     PRCondVar           *requestDone;
     uint32_t            requestCount;
     JSThread            *gcThread;
 
     js::GCHelperThread  gcHelperThread;
 
-    /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */
-    PRLock              *rtLock;
-#ifdef DEBUG
-    void *              rtLockOwner;
-#endif
-
-    /* Used to synchronize down/up state change; protected by gcLock. */
-    PRCondVar           *stateChange;
-
     /*
      * Mapping from NSPR thread identifiers to JSThreads.
      *
      * This map can be accessed by the GC thread; or by the thread that holds
      * gcLock, if GC is not running.
      */
     JSThread::Map       threads;
 #endif /* JS_THREADSAFE */
--- a/js/src/jscrashreport.cpp
+++ b/js/src/jscrashreport.cpp
@@ -101,17 +101,17 @@ GetStack(uint64_t *stack, uint64_t *stac
     regs->bp = context.Ebp;
 #else
     regs->ip = context.Rip;
     regs->sp = context.Rsp;
     regs->bp = context.Rbp;
 #endif
 #endif
 
-    memcpy(buffer, (void *)p, len);
+    js_memcpy(buffer, (void *)p, len);
 
     return true;
 }
 
 #elif 0
 
 #include <unistd.h>
 #include <ucontext.h>
@@ -152,17 +152,17 @@ GetStack(uint64_t *stack, uint64_t *stac
     regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_RBP];
     regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_RIP];
 #elif JS_BITS_PER_WORD == 32
     regs->sp = (uint64_t)context.uc_mcontext.gregs[REG_ESP];
     regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_EBP];
     regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_EIP];
 #endif
 
-    memcpy(buffer, (void *)p, len);
+    js_memcpy(buffer, (void *)p, len);
 
     return true;
 }
 
 #else
 
 static bool
 GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size)
@@ -225,21 +225,21 @@ void
 Ring::copyBytes(void *data, size_t size)
 {
     if (size >= bufferSize())
         size = bufferSize();
 
     if (offset + size > bufferSize()) {
         size_t first = bufferSize() - offset;
         size_t second = size - first;
-        memcpy(&buffer[offset], data, first);
-        memcpy(buffer, (char *)data + first, second);
+        js_memcpy(&buffer[offset], data, first);
+        js_memcpy(buffer, (char *)data + first, second);
         offset = second;
     } else {
-        memcpy(&buffer[offset], data, size);
+        js_memcpy(&buffer[offset], data, size);
         offset += size;
     }
 }
 
 static bool gInitialized;
 
 static Stack gGCStack(JS_CRASH_STACK_GC);
 static Stack gErrorStack(JS_CRASH_STACK_ERROR);
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -443,17 +443,18 @@ JS_GetFunctionLocalNameArray(JSContext *
     *markp = cx->tempLifoAlloc().mark();
 
     uintptr_t *names = cx->tempLifoAlloc().newArray<uintptr_t>(localNames.length());
     if (!names) {
         js_ReportOutOfMemory(cx);
         return NULL;
     }
 
-    memcpy(names, localNames.begin(), localNames.length() * sizeof(uintptr_t));
+    JS_ASSERT(sizeof(*names) == sizeof(*localNames.begin()));
+    js_memcpy(names, localNames.begin(), localNames.length() * sizeof(*names));
     return names;
 }
 
 extern JS_PUBLIC_API(JSAtom *)
 JS_LocalNameToAtom(uintptr_t w)
 {
     return JS_LOCAL_NAME_TO_ATOM(w);
 }
--- a/js/src/jsdhash.cpp
+++ b/js/src/jsdhash.cpp
@@ -164,17 +164,17 @@ JS_DHashMatchStringKey(JSDHashTable *tab
             strcmp((const char *) stub->key, (const char *) key) == 0);
 }
 
 JS_PUBLIC_API(void)
 JS_DHashMoveEntryStub(JSDHashTable *table,
                       const JSDHashEntryHdr *from,
                       JSDHashEntryHdr *to)
 {
-    memcpy(to, from, table->entrySize);
+    js_memcpy(to, from, table->entrySize);
 }
 
 JS_PUBLIC_API(void)
 JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry)
 {
     memset(entry, 0, table->entrySize);
 }
 
--- a/js/src/jsdtoa.cpp
+++ b/js/src/jsdtoa.cpp
@@ -137,17 +137,17 @@ js_dtostr(DtoaState *state, char *buffer
     }
 
     nDigits = numEnd - numBegin;
     JS_ASSERT((size_t) nDigits <= bufferSize - 2);
     if ((size_t) nDigits > bufferSize - 2) {
         return NULL;
     }
 
-    memcpy(buffer + 2, numBegin, nDigits);
+    js_memcpy(buffer + 2, numBegin, nDigits);
     freedtoa(PASS_STATE numBegin);
     numBegin = buffer + 2; /* +2 leaves space for sign and/or decimal point */
     numEnd = numBegin + nDigits;
     *numEnd = '\0';
 
     /* If Infinity, -Infinity, or NaN, return the string regardless of mode. */
     if (decPt != 9999) {
         JSBool exponentialNotation = JS_FALSE;
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -198,52 +198,52 @@ CopyErrorReport(JSContext *cx, JSErrorRe
     cursor += sizeof(JSErrorReport);
 
     if (argsArraySize != 0) {
         copy->messageArgs = (const jschar **)cursor;
         cursor += argsArraySize;
         for (i = 0; report->messageArgs[i]; ++i) {
             copy->messageArgs[i] = (const jschar *)cursor;
             argSize = JS_CHARS_SIZE(report->messageArgs[i]);
-            memcpy(cursor, report->messageArgs[i], argSize);
+            js_memcpy(cursor, report->messageArgs[i], argSize);
             cursor += argSize;
         }
         copy->messageArgs[i] = NULL;
         JS_ASSERT(cursor == (uint8_t *)copy->messageArgs[0] + argsCopySize);
     }
 
     if (report->ucmessage) {
         copy->ucmessage = (const jschar *)cursor;
-        memcpy(cursor, report->ucmessage, ucmessageSize);
+        js_memcpy(cursor, report->ucmessage, ucmessageSize);
         cursor += ucmessageSize;
     }
 
     if (report->uclinebuf) {
         copy->uclinebuf = (const jschar *)cursor;
-        memcpy(cursor, report->uclinebuf, uclinebufSize);
+        js_memcpy(cursor, report->uclinebuf, uclinebufSize);
         cursor += uclinebufSize;
         if (report->uctokenptr) {
             copy->uctokenptr = copy->uclinebuf + (report->uctokenptr -
                                                   report->uclinebuf);
         }
     }
 
     if (report->linebuf) {
         copy->linebuf = (const char *)cursor;
-        memcpy(cursor, report->linebuf, linebufSize);
+        js_memcpy(cursor, report->linebuf, linebufSize);
         cursor += linebufSize;
         if (report->tokenptr) {
             copy->tokenptr = copy->linebuf + (report->tokenptr -
                                               report->linebuf);
         }
     }
 
     if (report->filename) {
         copy->filename = (const char *)cursor;
-        memcpy(cursor, report->filename, filenameSize);
+        js_memcpy(cursor, report->filename, filenameSize);
     }
     JS_ASSERT(cursor + filenameSize == (uint8_t *)copy + mallocSize);
 
     /* HOLD called by the destination error object. */
     copy->originPrincipals = report->originPrincipals;
 
     /* Copy non-pointer members. */
     copy->lineno = report->lineno;
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -55,17 +55,17 @@ JS_SetGrayGCRootsTracer(JSRuntime *rt, J
 {
     rt->gcGrayRootsTraceOp = traceOp;
     rt->gcGrayRootsData = data;
 }
 
 JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt)
 {
-    JS_ASSERT(rt->state == JSRTS_UP);
+    JS_ASSERT(rt->hasContexts());
     return rt->atomState.anonymousAtom;
 }
 
 JS_FRIEND_API(JSObject *)
 JS_FindCompilationScope(JSContext *cx, JSObject *obj)
 {
     /*
      * We unwrap wrappers here. This is a little weird, but it's what's being
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1090,19 +1090,20 @@ fun_getProperty(JSContext *cx, JSObject 
 #ifdef JS_METHODJIT
     if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom) && fp && fp->prev()) {
         /*
          * If the frame was called from within an inlined frame, mark the
          * innermost function as uninlineable to expand its frame and allow us
          * to recover its callee object.
          */
         JSInlinedSite *inlined;
-        fp->prev()->pcQuadratic(cx->stack, fp, &inlined);
+        jsbytecode *prevpc = fp->prev()->pcQuadratic(cx->stack, fp, &inlined);
         if (inlined) {
-            JSFunction *fun = fp->prev()->jit()->inlineFrames()[inlined->inlineIndex].fun;
+            mjit::JITChunk *chunk = fp->prev()->jit()->chunk(prevpc);
+            JSFunction *fun = chunk->inlineFrames()[inlined->inlineIndex].fun;
             fun->script()->uninlineable = true;
             MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE);
         }
     }
 #endif
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) {
         /* Warn if strict about f.arguments or equivalent unqualified uses. */
@@ -1602,17 +1603,17 @@ js_fun_call(JSContext *cx, uintN argc, V
     /* Allocate stack space for fval, obj, and the args. */
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, argc, &args))
         return JS_FALSE;
 
     /* Push fval, thisv, and the args. */
     args.calleev() = fval;
     args.thisv() = thisv;
-    memcpy(args.array(), argv, argc * sizeof *argv);
+    PodCopy(args.array(), argv, argc);
 
     bool ok = Invoke(cx, args);
     *vp = args.rval();
     return ok;
 }
 
 /* ES5 15.3.4.3 */
 JSBool
@@ -1776,17 +1777,17 @@ CallOrConstructBoundFunction(JSContext *
 
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, argc + argslen, &args))
         return false;
 
     /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
     for (uintN i = 0; i < argslen; i++)
         args[i] = fun->getBoundFunctionArgument(i);
-    memcpy(args.array() + argslen, vp + 2, argc * sizeof(Value));
+    PodCopy(args.array() + argslen, vp + 2, argc);
 
     /* 15.3.4.5.1, 15.3.4.5.2 step 5. */
     args.calleev().setObject(*target);
 
     if (!constructing)
         args.thisv() = boundThis;
 
     if (constructing ? !InvokeConstructor(cx, args) : !Invoke(cx, args))
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2098,17 +2098,17 @@ MarkWeakReferences(GCMarker *gcmarker)
 
 namespace JS {
 
 void
 MarkRuntime(JSTracer *trc)
 {
     JSRuntime *rt = trc->runtime;
 
-    if (rt->state != JSRTS_LANDING)
+    if (rt->hasContexts())
         MarkConservativeStackRoots(trc);
 
     for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
         gc_root_traversal(trc, r.front());
 
     for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront())
         gc_lock_traversal(r.front(), trc);
 
@@ -2654,17 +2654,17 @@ SweepCompartments(JSContext *cx, JSGCInv
     JSCompartment **write = read;
     JS_ASSERT(rt->compartments.length() >= 1);
     JS_ASSERT(*rt->compartments.begin() == rt->atomsCompartment);
 
     while (read < end) {
         JSCompartment *compartment = *read++;
 
         if (!compartment->hold &&
-            (compartment->arenas.arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT))
+            (compartment->arenas.arenaListsAreEmpty() || !rt->hasContexts()))
         {
             compartment->arenas.checkEmptyFreeLists();
             if (callback)
                 JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY));
             if (compartment->principals)
                 JSPRINCIPALS_DROP(cx, compartment->principals);
             cx->delete_(compartment);
             continue;
@@ -3045,17 +3045,16 @@ AutoGCSession::~AutoGCSession()
  * to ensure that the bottom of the stack with possible GC roots recorded in
  * js_GC excludes any pointers we use during the marking implementation.
  */
 static JS_NEVER_INLINE void
 GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = cx->runtime;
 
-    JS_ASSERT_IF(comp, gckind != GC_LAST_CONTEXT);
     JS_ASSERT_IF(comp, comp != rt->atomsCompartment);
     JS_ASSERT_IF(comp, rt->gcMode == JSGC_MODE_COMPARTMENT);
 
     /*
      * Recursive GC is no-op and a call from another thread waits the started
      * GC cycle to finish.
      */
     if (rt->gcMarkAndSweep) {
@@ -3072,20 +3071,18 @@ GCCycle(JSContext *cx, JSCompartment *co
     AutoGCSession gcsession(cx);
 
     /*
      * Don't GC if any thread is reporting an OOM. We check the flag after we
      * have set up the GC session and know that the thread that reported OOM
      * is either the current thread or waits for the GC to complete on this
      * thread.
      */
-    if (rt->inOOMReport) {
-        JS_ASSERT(gckind != GC_LAST_CONTEXT);
+    if (rt->inOOMReport)
         return;
-    }
 
     /*
      * We should not be depending on cx->compartment in the GC, so set it to
      * NULL to look for violations.
      */
     SwitchToCompartment sc(cx, (JSCompartment *)NULL);
 
     JS_ASSERT(!rt->gcCurrentCompartment);
@@ -3097,20 +3094,18 @@ GCCycle(JSContext *cx, JSCompartment *co
     /*
      * As we about to purge caches and clear the mark bits we must wait for
      * any background finalization to finish. We must also wait for the
      * background allocation to finish so we can avoid taking the GC lock
      * when manipulating the chunks during the GC.
      */
     JS_ASSERT(!cx->gcBackgroundFree);
     rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
-    if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
-        if (rt->gcHelperThread.prepareForBackgroundSweep())
-            cx->gcBackgroundFree = &rt->gcHelperThread;
-    }
+    if (rt->hasContexts() && rt->gcHelperThread.prepareForBackgroundSweep())
+        cx->gcBackgroundFree = &rt->gcHelperThread;
 #endif
 
     MarkAndSweep(cx, gckind);
 
     if (!comp)
         js_PurgeThreads_PostGlobalSweep(cx);
 
 #ifdef JS_THREADSAFE
@@ -3130,25 +3125,16 @@ GCCycle(JSContext *cx, JSCompartment *co
 }
 
 void
 js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcstats::Reason reason)
 {
     JSRuntime *rt = cx->runtime;
     JS_AbortIfWrongThread(rt);
 
-    /*
-     * Don't collect garbage if the runtime isn't up, and cx is not the last
-     * context in the runtime.  The last context must force a GC, and nothing
-     * should suppress that final collection or there may be shutdown leaks,
-     * or runtime bloat until the next context is created.
-     */
-    if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT)
-        return;
-
 #ifdef JS_GC_ZEAL
     struct AutoVerifyBarriers {
         JSContext *cx;
         bool inVerify;
         AutoVerifyBarriers(JSContext *cx) : cx(cx), inVerify(cx->runtime->gcVerifyData) {
             if (inVerify) EndVerifyBarriers(cx);
         }
         ~AutoVerifyBarriers() { if (inVerify) StartVerifyBarriers(cx); }
@@ -3162,17 +3148,17 @@ js_GC(JSContext *cx, JSCompartment *comp
     do {
         /*
          * Let the API user decide to defer a GC if it wants to (unless this
          * is the last context).  Invoke the callback regardless. Sample the
          * callback in case we are freely racing with a JS_SetGCCallback{,RT}
          * on another thread.
          */
         if (JSGCCallback callback = rt->gcCallback) {
-            if (!callback(cx, JSGC_BEGIN) && gckind != GC_LAST_CONTEXT)
+            if (!callback(cx, JSGC_BEGIN) && rt->hasContexts())
                 return;
         }
 
         {
             /* Lock out other GC allocator and collector invocations. */
             AutoLockGC lock(rt);
             rt->gcPoke = false;
             GCCycle(cx, comp, gckind);
@@ -3181,17 +3167,17 @@ js_GC(JSContext *cx, JSCompartment *comp
         /* We re-sample the callback again as the finalizers can change it. */
         if (JSGCCallback callback = rt->gcCallback)
             (void) callback(cx, JSGC_END);
 
         /*
          * On shutdown, iterate until finalizers or the JSGC_END callback
          * stop creating garbage.
          */
-    } while (gckind == GC_LAST_CONTEXT && rt->gcPoke);
+    } while (!rt->hasContexts() && rt->gcPoke);
 
     rt->gcNextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
 
     rt->gcChunkAllocationSinceLastGC = false;
 }
 
 namespace js {
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1403,24 +1403,18 @@ ShrinkGCBuffers(JSRuntime *rt);
 
 /*
  * Kinds of js_GC invocation.
  */
 typedef enum JSGCInvocationKind {
     /* Normal invocation. */
     GC_NORMAL           = 0,
 
-    /*
-     * Called from js_DestroyContext for last JSContext in a JSRuntime, when
-     * it is imperative that rt->gcPoke gets cleared early in js_GC.
-     */
-    GC_LAST_CONTEXT     = 1,
-
     /* Minimize GC triggers and release empty GC chunks right away. */
-    GC_SHRINK             = 2
+    GC_SHRINK             = 1
 } JSGCInvocationKind;
 
 /* Pass NULL for |comp| to get a full GC. */
 extern void
 js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcstats::Reason r);
 
 #ifdef JS_THREADSAFE
 /*
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1399,74 +1399,74 @@ TypeConstraintTransformThis::newType(JSC
 /////////////////////////////////////////////////////////////////////
 // Freeze constraints
 /////////////////////////////////////////////////////////////////////
 
 /* Constraint which triggers recompilation of a script if any type is added to a type set. */
 class TypeConstraintFreeze : public TypeConstraint
 {
 public:
-    JSScript *script;
+    RecompileInfo info;
 
     /* Whether a new type has already been added, triggering recompilation. */
     bool typeAdded;
 
-    TypeConstraintFreeze(JSScript *script)
-        : TypeConstraint("freeze"), script(script), typeAdded(false)
+    TypeConstraintFreeze(RecompileInfo info)
+        : TypeConstraint("freeze"), info(info), typeAdded(false)
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (typeAdded)
             return;
 
         typeAdded = true;
-        cx->compartment->types.addPendingRecompile(cx, script);
+        cx->compartment->types.addPendingRecompile(cx, info);
     }
 };
 
 void
 TypeSet::addFreeze(JSContext *cx)
 {
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
-                cx->compartment->types.compiledScript), false);
+                cx->compartment->types.compiledInfo), false);
 }
 
 /*
  * Constraint which triggers recompilation of a script if a possible new JSValueType
  * tag is realized for a type set.
  */
 class TypeConstraintFreezeTypeTag : public TypeConstraint
 {
 public:
-    JSScript *script;
+    RecompileInfo info;
 
     /*
      * Whether the type tag has been marked unknown due to a type change which
      * occurred after this constraint was generated (and which triggered recompilation).
      */
     bool typeUnknown;
 
-    TypeConstraintFreezeTypeTag(JSScript *script)
-        : TypeConstraint("freezeTypeTag"), script(script), typeUnknown(false)
+    TypeConstraintFreezeTypeTag(RecompileInfo info)
+        : TypeConstraint("freezeTypeTag"), info(info), typeUnknown(false)
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (typeUnknown)
             return;
 
         if (!type.isUnknown() && !type.isAnyObject() && type.isObject()) {
             /* Ignore new objects when the type set already has other objects. */
             if (source->getObjectCount() >= 2)
                 return;
         }
 
         typeUnknown = true;
-        cx->compartment->types.addPendingRecompile(cx, script);
+        cx->compartment->types.addPendingRecompile(cx, info);
     }
 };
 
 static inline JSValueType
 GetValueTypeFromTypeFlags(TypeFlags flags)
 {
     switch (flags) {
       case TYPE_FLAG_UNDEFINED:
@@ -1506,74 +1506,74 @@ TypeSet::getKnownTypeTag(JSContext *cx)
      * but we still need to record the dependency as adding a new type can give
      * it a definite type tag. This is not needed if there are enough types
      * that the exact tag is unknown, as it will stay unknown as more types are
      * added to the set.
      */
     bool empty = flags == 0 && baseObjectCount() == 0;
     JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
 
-    if (cx->compartment->types.compiledScript && (empty || type != JSVAL_TYPE_UNKNOWN)) {
+    if (cx->compartment->types.compiledInfo.script && (empty || type != JSVAL_TYPE_UNKNOWN)) {
         add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeTypeTag>(
-                  cx->compartment->types.compiledScript), false);
+                  cx->compartment->types.compiledInfo), false);
     }
 
     return type;
 }
 
 /* Constraint which triggers recompilation if an object acquires particular flags. */
 class TypeConstraintFreezeObjectFlags : public TypeConstraint
 {
 public:
-    JSScript *script;
+    RecompileInfo info;
 
     /* Flags we are watching for on this object. */
     TypeObjectFlags flags;
 
     /* Whether the object has already been marked as having one of the flags. */
     bool *pmarked;
     bool localMarked;
 
-    TypeConstraintFreezeObjectFlags(JSScript *script, TypeObjectFlags flags, bool *pmarked)
-        : TypeConstraint("freezeObjectFlags"), script(script), flags(flags),
+    TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags, bool *pmarked)
+        : TypeConstraint("freezeObjectFlags"), info(info), flags(flags),
           pmarked(pmarked), localMarked(false)
     {}
 
-    TypeConstraintFreezeObjectFlags(JSScript *script, TypeObjectFlags flags)
-        : TypeConstraint("freezeObjectFlags"), script(script), flags(flags),
+    TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags)
+        : TypeConstraint("freezeObjectFlags"), info(info), flags(flags),
           pmarked(&localMarked), localMarked(false)
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type) {}
 
     void newObjectState(JSContext *cx, TypeObject *object, bool force)
     {
         if (object->hasAnyFlags(flags) && !*pmarked) {
             *pmarked = true;
-            cx->compartment->types.addPendingRecompile(cx, script);
+            cx->compartment->types.addPendingRecompile(cx, info);
         } else if (force) {
-            cx->compartment->types.addPendingRecompile(cx, script);
+            cx->compartment->types.addPendingRecompile(cx, info);
         }
     }
 };
 
 /*
  * Constraint which triggers recompilation if any object in a type set acquire
  * particular flags.
  */
 class TypeConstraintFreezeObjectFlagsSet : public TypeConstraint
 {
 public:
-    JSScript *script;
+    RecompileInfo info;
 
     TypeObjectFlags flags;
     bool marked;
 
-    TypeConstraintFreezeObjectFlagsSet(JSScript *script, TypeObjectFlags flags)
-        : TypeConstraint("freezeObjectKindSet"), script(script), flags(flags), marked(false)
+    TypeConstraintFreezeObjectFlagsSet(RecompileInfo info, TypeObjectFlags flags)
+        : TypeConstraint("freezeObjectKindSet"), info(info), flags(flags), marked(false)
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (marked) {
             /* Despecialized the kind we were interested in due to recompilation. */
             return;
         }
@@ -1588,25 +1588,25 @@ public:
                 /*
                  * Add a constraint on the the object to pick up changes in the
                  * object's properties.
                  */
                 TypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
                 if (!types)
                     return;
                 types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
-                                  script, flags, &marked), false);
+                                  info, flags, &marked), false);
                 return;
             }
         } else {
             return;
         }
 
         marked = true;
-        cx->compartment->types.addPendingRecompile(cx, script);
+        cx->compartment->types.addPendingRecompile(cx, info);
     }
 };
 
 bool
 TypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags)
 {
     if (unknownObject())
         return true;
@@ -1630,32 +1630,32 @@ TypeSet::hasObjectFlags(JSContext *cx, T
             return true;
     }
 
     /*
      * Watch for new objects of different kind, and re-traverse existing types
      * in this set to add any needed FreezeArray constraints.
      */
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlagsSet>(
-                 cx->compartment->types.compiledScript, flags));
+                 cx->compartment->types.compiledInfo, flags));
 
     return false;
 }
 
 bool
 TypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags)
 {
     if (object->hasAnyFlags(flags))
         return true;
 
     TypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
     if (!types)
         return true;
     types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
-                      cx->compartment->types.compiledScript, flags), false);
+                      cx->compartment->types.compiledInfo, flags), false);
     return false;
 }
 
 void
 types::MarkArgumentsCreated(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(!script->createdArgs);
 
@@ -1727,42 +1727,42 @@ TypeSet::WatchObjectStateChange(JSContex
     if (!types)
         return;
 
     /*
      * Use a constraint which triggers recompilation when markStateChange is
      * called, which will set 'force' to true.
      */
     types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
-                     cx->compartment->types.compiledScript,
+                     cx->compartment->types.compiledInfo,
                      0));
 }
 
 class TypeConstraintFreezeOwnProperty : public TypeConstraint
 {
 public:
-    JSScript *script;
+    RecompileInfo info;
 
     bool updated;
     bool configurable;
 
-    TypeConstraintFreezeOwnProperty(JSScript *script, bool configurable)
+    TypeConstraintFreezeOwnProperty(RecompileInfo info, bool configurable)
         : TypeConstraint("freezeOwnProperty"),
-          script(script), updated(false), configurable(configurable)
+          info(info), updated(false), configurable(configurable)
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type) {}
 
     void newPropertyState(JSContext *cx, TypeSet *source)
     {
         if (updated)
             return;
         if (source->isOwnProperty(configurable)) {
             updated = true;
-            cx->compartment->types.addPendingRecompile(cx, script);
+            cx->compartment->types.addPendingRecompile(cx, info);
         }
     }
 };
 
 static void
 CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun);
 
 bool
@@ -1782,17 +1782,17 @@ TypeSet::isOwnProperty(JSContext *cx, Ty
             object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
         }
     }
 
     if (isOwnProperty(configurable))
         return true;
 
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeOwnProperty>(
-                                                      cx->compartment->types.compiledScript,
+                                                      cx->compartment->types.compiledInfo,
                                                       configurable), false);
     return false;
 }
 
 bool
 TypeSet::knownNonEmpty(JSContext *cx)
 {
     if (baseFlags() != 0 || baseObjectCount() != 0)
@@ -1876,17 +1876,17 @@ TypeSet::getSingleton(JSContext *cx, boo
         return NULL;
 
     JSObject *obj = getSingleObject(0);
     if (!obj)
         return NULL;
 
     if (freeze) {
         add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
-                                               cx->compartment->types.compiledScript), false);
+                                               cx->compartment->types.compiledInfo), false);
     }
 
     return obj;
 }
 
 static inline bool
 TypeHasGlobal(Type type, JSObject *global)
 {
@@ -1901,32 +1901,32 @@ TypeHasGlobal(Type type, JSObject *globa
 
     JS_ASSERT(type.isPrimitive());
     return true;
 }
 
 class TypeConstraintFreezeGlobal : public TypeConstraint
 {
 public:
-    JSScript *script;
+    RecompileInfo info;
     JSObject *global;
 
-    TypeConstraintFreezeGlobal(JSScript *script, JSObject *global)
-        : TypeConstraint("freezeGlobal"), script(script), global(global)
+    TypeConstraintFreezeGlobal(RecompileInfo info, JSObject *global)
+        : TypeConstraint("freezeGlobal"), info(info), 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);
+        cx->compartment->types.addPendingRecompile(cx, info);
     }
 };
 
 bool
 TypeSet::hasGlobalObject(JSContext *cx, JSObject *global)
 {
     if (unknownObject())
         return false;
@@ -1934,17 +1934,17 @@ TypeSet::hasGlobalObject(JSContext *cx, 
     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, cx->typeLifoAlloc().new_<TypeConstraintFreezeGlobal>(
-              cx->compartment->types.compiledScript, global), false);
+              cx->compartment->types.compiledInfo, global), false);
 
     return true;
 }
 
 bool
 TypeSet::needsBarrier(JSContext *cx)
 {
     bool result = unknownObject()
@@ -2123,43 +2123,45 @@ TypeCompartment::growPendingArray(JSCont
 {
     unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2);
     PendingWork *newArray = (PendingWork *) OffTheBooks::calloc_(newCapacity * sizeof(PendingWork));
     if (!newArray) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return false;
     }
 
-    memcpy(newArray, pendingArray, pendingCount * sizeof(PendingWork));
+    PodCopy(newArray, pendingArray, pendingCount);
     cx->free_(pendingArray);
 
     pendingArray = newArray;
     pendingCapacity = newCapacity;
 
     return true;
 }
 
 void
 TypeCompartment::processPendingRecompiles(JSContext *cx)
 {
     /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
-    Vector<JSScript*> *pending = pendingRecompiles;
+    Vector<RecompileInfo> *pending = pendingRecompiles;
     pendingRecompiles = NULL;
 
     JS_ASSERT(!pending->empty());
 
 #ifdef JS_METHODJIT
 
     mjit::ExpandInlineFrames(cx->compartment);
 
     for (unsigned i = 0; i < pending->length(); i++) {
-        JSScript *script = (*pending)[i];
-        mjit::Recompiler recompiler(cx, script);
-        if (script->hasJITCode())
-            recompiler.recompile();
+        const RecompileInfo &info = (*pending)[i];
+        mjit::JITScript *jit = info.script->getJIT(info.constructing);
+        if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) {
+            mjit::Recompiler::clearStackReferences(cx, info.script);
+            jit->destroyChunk(cx, info.chunkIndex);
+        }
     }
 
 #endif /* JS_METHODJIT */
 
     cx->delete_(pending);
 }
 
 void
@@ -2215,60 +2217,81 @@ TypeCompartment::nukeTypes(JSContext *cx
         JSContext *cx = JSContext::fromLinkField(cl);
         cx->setCompartment(cx->compartment);
     }
 
 #ifdef JS_METHODJIT
 
     JSCompartment *compartment = cx->compartment;
     mjit::ExpandInlineFrames(compartment);
+    mjit::ClearAllFrames(compartment);
 
     /* Throw away all JIT code in the compartment, but leave everything else alone. */
 
     for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
-        if (script->hasJITCode()) {
-            mjit::Recompiler recompiler(cx, script);
-            recompiler.recompile();
-        }
+        if (script->hasJITCode())
+            mjit::ReleaseScriptCode(cx, script);
     }
 #endif /* JS_METHODJIT */
 
 }
 
 void
-TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
+TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
 {
 #ifdef JS_METHODJIT
-    if (!script->jitNormal && !script->jitCtor) {
+    mjit::JITScript *jit = info.script->getJIT(info.constructing);
+    if (!jit || !jit->chunkDescriptor(info.chunkIndex).chunk) {
         /* Scripts which haven't been compiled yet don't need to be recompiled. */
         return;
     }
 
     if (!pendingRecompiles) {
-        pendingRecompiles = cx->new_< Vector<JSScript*> >(cx);
+        pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
         if (!pendingRecompiles) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
     }
 
     for (unsigned i = 0; i < pendingRecompiles->length(); i++) {
-        if (script == (*pendingRecompiles)[i])
+        if (info == (*pendingRecompiles)[i])
             return;
     }
 
-    if (!pendingRecompiles->append(script)) {
+    if (!pendingRecompiles->append(info)) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 #endif
 }
 
 void
+TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc)
+{
+#ifdef JS_METHODJIT
+    RecompileInfo info;
+    info.script = script;
+
+    if (script->jitNormal) {
+        info.constructing = false;
+        info.chunkIndex = script->jitNormal->chunkIndex(pc);
+        addPendingRecompile(cx, info);
+    }
+
+    if (script->jitCtor) {
+        info.constructing = true;
+        info.chunkIndex = script->jitCtor->chunkIndex(pc);
+        addPendingRecompile(cx, info);
+    }
+#endif
+}
+
+void
 TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
                                  bool returnOnly)
 {
     ScriptAnalysis *analysis = script->analysis();
     JS_ASSERT(analysis->ranInference());
 
     jsbytecode *pc = script->code + offset;
 
@@ -2284,17 +2307,17 @@ TypeCompartment::monitorBytecode(JSConte
 
     /* Dynamically monitor this call to keep track of its result types. */
     if (js_CodeSpec[*pc].format & JOF_INVOKE)
         code.monitoredTypesReturn = true;
 
     if (!returnOnly)
         code.monitoredTypes = true;
 
-    cx->compartment->types.addPendingRecompile(cx, script);
+    cx->compartment->types.addPendingRecompile(cx, script, pc);
 
     /* Trigger recompilation of any inline callers. */
     if (script->function() && !script->function()->hasLazyType())
         ObjectStateChange(cx, script->function()->type(), false, true);
 }
 
 void
 TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
@@ -2378,17 +2401,17 @@ ScriptAnalysis::addTypeBarrier(JSContext
 
     if (!code.typeBarriers) {
         /*
          * Adding type barriers at a bytecode which did not have them before
          * will trigger recompilation. If there were already type barriers,
          * however, do not trigger recompilation (the script will be recompiled
          * if any of the barriers is ever violated).
          */
-        cx->compartment->types.addPendingRecompile(cx, script);
+        cx->compartment->types.addPendingRecompile(cx, script, const_cast<jsbytecode*>(pc));
 
         /* Trigger recompilation of any inline callers. */
         if (script->function() && !script->function()->hasLazyType())
             ObjectStateChange(cx, script->function()->type(), false, true);
     }
 
     /* Ignore duplicate barriers. */
     TypeBarrier *barrier = code.typeBarriers;
@@ -2413,17 +2436,17 @@ void
 ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, JSObject *singleton, jsid singletonId)
 {
     JS_ASSERT(singletonId == MakeTypeId(cx, singletonId) && !JSID_IS_VOID(singletonId));
 
     Bytecode &code = getCode(pc);
 
     if (!code.typeBarriers) {
         /* Trigger recompilation as for normal type barriers. */
-        cx->compartment->types.addPendingRecompile(cx, script);
+        cx->compartment->types.addPendingRecompile(cx, script, const_cast<jsbytecode*>(pc));
         if (script->function() && !script->function()->hasLazyType())
             ObjectStateChange(cx, script->function()->type(), false, true);
     }
 
     InferSpew(ISpewOps, "singletonTypeBarrier: #%u:%05u: %sT%p%s %p %s",
               script->id(), pc - script->code,
               InferSpewColor(target), target, InferSpewColorReset(),
               (void *) singleton, TypeIdString(singletonId));
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -1126,48 +1126,59 @@ typedef HashMap<ArrayTableKey,ReadBarrie
 
 struct ObjectTableKey;
 struct ObjectTableEntry;
 typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy> ObjectTypeTable;
 
 struct AllocationSiteKey;
 typedef HashMap<AllocationSiteKey,ReadBarriered<TypeObject>,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable;
 
+struct RecompileInfo
+{
+    JSScript *script;
+    bool constructing:1;
+    uint32_t chunkIndex:31;
+
+    bool operator == (const RecompileInfo &o) const {
+        return script == o.script && constructing == o.constructing && chunkIndex == o.chunkIndex;
+    }
+};
+
 /* Type information for a compartment. */
 struct TypeCompartment
 {
     /* Whether type inference is enabled in this compartment. */
     bool inferenceEnabled;
 
     /* Number of scripts in this compartment. */
     unsigned scriptCount;
 
     /*
      * Bit set if all current types must be marked as unknown, and all scripts
      * recompiled. Caused by OOM failure within inference operations.
      */
     bool pendingNukeTypes;
 
     /* Pending recompilations to perform before execution of JIT code can resume. */
-    Vector<JSScript*> *pendingRecompiles;
+    Vector<RecompileInfo> *pendingRecompiles;
 
     /*
      * Number of recompilation events and inline frame expansions that have
      * occurred in this compartment. If these change, code should not count on
      * compiled code or the current stack being intact.
      */
     unsigned recompilations;
     unsigned frameExpansions;
 
     /*
      * Script currently being compiled. All constraints which look for type
      * changes inducing recompilation are keyed to this script. Note: script
      * compilation is not reentrant.
      */
-    JSScript *compiledScript;
+    RecompileInfo compiledInfo;
 
     /* Table for referencing types of objects keyed to an allocation site. */
     AllocationSiteTable *allocationSiteTable;
 
     /* Tables for determining types of singleton/JSON objects. */
 
     ArrayTypeTable *arrayTypeTable;
     ObjectTypeTable *objectTypeTable;
@@ -1230,17 +1241,18 @@ struct TypeCompartment
 
     void nukeTypes(JSContext *cx);
     void processPendingRecompiles(JSContext *cx);
 
     /* Mark all types as needing destruction once inference has 'finished'. */
     void setPendingNukeTypes(JSContext *cx);
 
     /* Mark a script as needing recompilation once inference has finished. */
-    void addPendingRecompile(JSContext *cx, JSScript *script);
+    void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
+    void addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc);
 
     /* Monitor future effects on a bytecode. */
     void monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
                          bool returnOnly = false);
 
     /* Mark any type set containing obj as having a generic object type. */
     void markSetsUnknown(JSContext *cx, TypeObject *obj);
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -237,30 +237,33 @@ struct AutoEnterTypeInference
 };
 
 /*
  * Structure marking the currently compiled script, for constraints which can
  * trigger recompilation.
  */
 struct AutoEnterCompilation
 {
-    JSContext *cx;
-    JSScript *script;
+    RecompileInfo &info;
 
-    AutoEnterCompilation(JSContext *cx, JSScript *script)
-        : cx(cx), script(script)
+    AutoEnterCompilation(JSContext *cx, JSScript *script, bool constructing, unsigned chunkIndex)
+        : info(cx->compartment->types.compiledInfo)
     {
-        JS_ASSERT(!cx->compartment->types.compiledScript);
-        cx->compartment->types.compiledScript = script;
+        JS_ASSERT(!info.script);
+        info.script = script;
+        info.constructing = constructing;
+        info.chunkIndex = chunkIndex;
     }
 
     ~AutoEnterCompilation()
     {
-        JS_ASSERT(cx->compartment->types.compiledScript == script);
-        cx->compartment->types.compiledScript = NULL;
+        JS_ASSERT(info.script);
+        info.script = NULL;
+        info.constructing = false;
+        info.chunkIndex = 0;
     }
 };
 
 /////////////////////////////////////////////////////////////////////
 // Interface functions
 /////////////////////////////////////////////////////////////////////
 
 /*
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -69,17 +69,16 @@
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jslibmath.h"
 
 #include "frontend/BytecodeEmitter.h"
 #ifdef JS_METHODJIT
 #include "methodjit/MethodJIT.h"
-#include "methodjit/MethodJIT-inl.h"
 #include "methodjit/Logging.h"
 #endif
 #include "vm/Debugger.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
@@ -459,17 +458,17 @@ js::RunScript(JSContext *cx, JSScript *s
         if (fp->scopeChain().global().isCleared()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE);
             return false;
         }
     }
 
 #ifdef JS_METHODJIT
     mjit::CompileStatus status;
-    status = mjit::CanMethodJIT(cx, script, fp->isConstructing(),
+    status = mjit::CanMethodJIT(cx, script, script->code, fp->isConstructing(),
                                 mjit::CompileRequest_Interpreter);
     if (status == mjit::Compile_Error)
         return false;
 
     if (status == mjit::Compile_Okay)
         return mjit::JaegerShot(cx, false);
 #endif
 
@@ -549,17 +548,17 @@ js::Invoke(JSContext *cx, const Value &t
            Value *rval)
 {
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, argc, &args))
         return false;
 
     args.calleev() = fval;
     args.thisv() = thisv;
-    memcpy(args.array(), argv, argc * sizeof(Value));
+    PodCopy(args.array(), argv, argc);
 
     if (args.thisv().isObject()) {
         /*
          * We must call the thisObject hook in case we are not called from the
          * interpreter, where a prior bytecode has computed an appropriate
          * |this| already.
          */
         JSObject *thisp = args.thisv().toObject().thisObject(cx);
@@ -618,17 +617,17 @@ bool
 js::InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Value *rval)
 {
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, argc, &args))
         return false;
 
     args.calleev() = fval;
     args.thisv().setMagic(JS_THIS_POISON);
-    memcpy(args.array(), argv, argc * sizeof(Value));
+    PodCopy(args.array(), argv, argc);
 
     if (!InvokeConstructor(cx, args))
         return false;
 
     *rval = args.rval();
     return true;
 }
 
@@ -1273,23 +1272,20 @@ js::AssertValidPropertyCacheHit(JSContex
     PropertyCacheEntry savedEntry = *entry;
 
     PropertyName *name = GetNameFromBytecode(cx, pc, JSOp(*pc), js_CodeSpec[*pc]);
 
     JSObject *obj, *pobj;
     JSProperty *prop;
     JSBool ok;
 
-    if (JOF_OPMODE(*pc) == JOF_NAME) {
-        bool global = js_CodeSpec[*pc].format & JOF_GNAME;
-        ok = FindProperty(cx, name, global, &obj, &pobj, &prop);
-    } else {
-        obj = start;
-        ok = LookupProperty(cx, obj, name, &pobj, &prop);
-    }
+    if (JOF_OPMODE(*pc) == JOF_NAME)
+        ok = FindProperty(cx, name, start, &obj, &pobj, &prop);
+    else
+        ok = LookupProperty(cx, start, name, &pobj, &prop);
     JS_ASSERT(ok);
 
     if (cx->runtime->gcNumber != sample)
         JS_PROPERTY_CACHE(cx).restore(&savedEntry);
     JS_ASSERT(prop);
     JS_ASSERT(pobj == found);
 
     const Shape *shape = (Shape *) prop;
@@ -1346,23 +1342,19 @@ IteratorMore(JSContext *cx, JSObject *it
 
 static inline bool
 IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
 {
     if (iterobj->isIterator()) {
         NativeIterator *ni = iterobj->getNativeIterator();
         if (ni->isKeyIter()) {
             JS_ASSERT(ni->props_cursor < ni->props_end);
-            jsid id = *ni->current();
-            if (JSID_IS_ATOM(id)) {
-                rval->setString(JSID_TO_STRING(id));
-                ni->incCursor();
-                return true;
-            }
-            /* Take the slow path if we have to stringify a numeric property name. */
+            rval->setString(*ni->current());
+            ni->incCursor();
+            return true;
         }
     }
     return js_IteratorNext(cx, iterobj, rval);
 }
 
 /*
  * For bytecodes which push values and then fall through, make sure the
  * types of the pushed values are consistent with type inference information.
@@ -1502,17 +1494,16 @@ js::Interpret(JSContext *cx, StackFrame 
     bool useMethodJIT = false;
 #endif
 
 #ifdef JS_METHODJIT
 
 #define RESET_USE_METHODJIT()                                                 \
     JS_BEGIN_MACRO                                                            \
         useMethodJIT = cx->methodJitEnabled &&                                \
-            script->getJITStatus(regs.fp()->isConstructing()) != JITScript_Invalid && \
            (interpMode == JSINTERP_NORMAL ||                                  \
             interpMode == JSINTERP_REJOIN ||                                  \
             interpMode == JSINTERP_SKIP_TRAP);                                \
     JS_END_MACRO
 
 #define CHECK_PARTIAL_METHODJIT(status)                                       \
     JS_BEGIN_MACRO                                                            \
         switch (status) {                                                     \
@@ -1822,22 +1813,24 @@ check_backedge:
     CHECK_BRANCH();
     if (op != JSOP_LOOPHEAD)
         DO_OP();
 
 #ifdef JS_METHODJIT
     if (!useMethodJIT)
         DO_OP();
     mjit::CompileStatus status =
-        mjit::CanMethodJITAtBranch(cx, script, regs.fp(), regs.pc);
+        mjit::CanMethodJIT(cx, script, regs.pc, regs.fp()->isConstructing(),
+                           mjit::CompileRequest_Interpreter);
     if (status == mjit::Compile_Error)
         goto error;
     if (status == mjit::Compile_Okay) {
         void *ncode =
             script->nativeCodeForPC(regs.fp()->isConstructing(), regs.pc);
+        JS_ASSERT(ncode);
         mjit::JaegerStatus status =
             mjit::JaegerShotAtSafePoint(cx, ncode, true);
         CHECK_PARTIAL_METHODJIT(status);
         interpReturnOK = (status == mjit::Jaeger_Returned);
         if (entryFrame != regs.fp())
             goto jit_return;
         regs.fp()->setFinishedInInterpreter();
         goto leave_on_safe_point;
@@ -2612,17 +2605,17 @@ BEGIN_CASE(JSOP_POS)
 END_CASE(JSOP_POS)
 
 BEGIN_CASE(JSOP_DELNAME)
 {
     PropertyName *name;
     LOAD_NAME(0, name);
     JSObject *obj, *obj2;
     JSProperty *prop;
-    if (!FindProperty(cx, name, false, &obj, &obj2, &prop))
+    if (!FindProperty(cx, name, cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop))
         goto error;
 
     /* Strict mode code should never contain JSOP_DELNAME opcodes. */
     JS_ASSERT(!script->strictModeCode);
 
     /* ECMA says to return true if name is undefined or inherited. */
     PUSH_BOOLEAN(true);
     if (prop) {
@@ -3038,17 +3031,18 @@ BEGIN_CASE(JSOP_FUNAPPLY)
     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);
+        mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, script->code,
+                                                        construct, request);
         if (status == mjit::Compile_Error)
             goto error;
         if (status == mjit::Compile_Okay) {
             mjit::JaegerStatus status = mjit::JaegerShot(cx, true);
             CHECK_PARTIAL_METHODJIT(status);
             interpReturnOK = (status == mjit::Jaeger_Returned);
             CHECK_INTERRUPT_HANDLER();
             goto jit_return;
@@ -3090,17 +3084,17 @@ END_CASE(JSOP_SETCALL)
 
 BEGIN_CASE(JSOP_IMPLICITTHIS)
 {
     PropertyName *name;
     LOAD_NAME(0, name);
 
     JSObject *obj, *obj2;
     JSProperty *prop;
-    if (!FindPropertyHelper(cx, name, false, false, &obj, &obj2, &prop))
+    if (!FindPropertyHelper(cx, name, false, cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop))
         goto error;
 
     Value v;
     if (!ComputeImplicitThis(cx, obj, &v))
         goto error;
     PUSH_COPY(v);
 }
 END_CASE(JSOP_IMPLICITTHIS)
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -210,20 +210,18 @@ ExecuteKernel(JSContext *cx, JSScript *s
 /* Execute a script with the given scopeChain as global code. */
 extern bool
 Execute(JSContext *cx, JSScript *script, JSObject &scopeChain, Value *rval);
 
 /* Flags to toggle js::Interpret() execution. */
 enum InterpMode
 {
     JSINTERP_NORMAL    = 0, /* interpreter is running normally */
-    JSINTERP_RECORD    = 1, /* interpreter has been started to record/run traces */
-    JSINTERP_PROFILE   = 2, /* interpreter should profile a loop */
-    JSINTERP_REJOIN    = 3, /* as normal, but the frame has already started */
-    JSINTERP_SKIP_TRAP = 4  /* as REJOIN, but skip trap at first opcode */
+    JSINTERP_REJOIN    = 1, /* as normal, but the frame has already started */
+    JSINTERP_SKIP_TRAP = 2  /* as REJOIN, but skip trap at first opcode */
 };
 
 /*
  * Execute the caller-initialized frame for a user-defined script or function
  * pointed to by cx->fp until completion or error.
  */
 extern JS_NEVER_INLINE bool
 Interpret(JSContext *cx, StackFrame *stopFp, InterpMode mode = JSINTERP_NORMAL);
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -378,35 +378,43 @@ SetPropertyOperation(JSContext *cx, jsby
     return true;
 }
 
 inline bool
 NameOperation(JSContext *cx, jsbytecode *pc, Value *vp)
 {
     JSObject *obj = cx->stack.currentScriptedScopeChain();
 
-    bool global = js_CodeSpec[*pc].format & JOF_GNAME;
-    if (global)
+    /*
+     * Skip along the scope chain to the enclosing global object. This is
+     * used for GNAME opcodes where the bytecode emitter has determined a
+     * name access must be on the global. It also insulates us from bugs
+     * in the emitter: type inference will assume that GNAME opcodes are
+     * accessing the global object, and the inferred behavior should match
+     * the actual behavior even if the id could be found on the scope chain
+     * before the global object.
+     */
+    if (js_CodeSpec[*pc].format & JOF_GNAME)
         obj = &obj->global();
 
     PropertyCacheEntry *entry;
     JSObject *obj2;
     PropertyName *name;
     JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
     if (!name) {
         AssertValidPropertyCacheHit(cx, obj, obj2, entry);
         if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, vp))
             return false;
         return true;
     }
 
     jsid id = ATOM_TO_JSID(name);
 
     JSProperty *prop;
-    if (!FindPropertyHelper(cx, name, true, global, &obj, &obj2, &prop))
+    if (!FindPropertyHelper(cx, name, true, obj, &obj, &obj2, &prop))
         return false;
     if (!prop) {
         /* Kludge to allow (typeof foo == "undefined") tests. */
         JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]);
         if (op2 == JSOP_TYPEOF) {
             vp->setUndefined();
             return true;
         }
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -36,18 +36,16 @@
  * 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 ***** */
 
 /*
  * JavaScript iterators.
  */
-#include <string.h>     /* for memcpy */
-
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
@@ -118,17 +116,18 @@ Class js::IteratorClass = {
     }
 };
 
 static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2;
 
 void
 NativeIterator::mark(JSTracer *trc)
 {
-    MarkIdRange(trc, begin(), end(), "props");
+    for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++)
+        MarkString(trc, *str, "prop");
     if (obj)
         MarkObject(trc, obj, "obj");
 }
 
 static void
 iterator_finalize(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isIterator());
@@ -504,24 +503,30 @@ NewIteratorObject(JSContext *cx, uintN f
     return NewBuiltinClassInstance(cx, &IteratorClass);
 }
 
 NativeIterator *
 NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props)
 {
     size_t plength = props.length();
     NativeIterator *ni = (NativeIterator *)
-        cx->malloc_(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(Shape *));
+        cx->malloc_(sizeof(NativeIterator)
+                    + plength * sizeof(JSString *)
+                    + slength * sizeof(Shape *));
     if (!ni)
         return NULL;
-    ni->props_array = ni->props_cursor = (HeapId *) (ni + 1);
+    ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1);
     ni->props_end = ni->props_array + plength;
     if (plength) {
-        for (size_t i = 0; i < plength; i++)
-            ni->props_array[i].init(props[i]);
+        for (size_t i = 0; i < plength; i++) {
+            JSFlatString *str = IdToString(cx, props[i]);
+            if (!str)
+                return NULL;
+            ni->props_array[i].init(str);
+        }
     }
     return ni;
 }
 
 inline void
 NativeIterator::init(JSObject *obj, uintN flags, uint32_t slength, uint32_t key)
 {
     this->obj.init(obj);
@@ -913,46 +918,50 @@ js_CloseIterator(JSContext *cx, JSObject
  * We do not suppress enumeration of a property deleted along an object's
  * prototype chain. Only direct deletions on the object are handled.
  *
  * This function can suppress multiple properties at once. The |predicate|
  * argument is an object which can be called on an id and returns true or
  * false. It also must have a method |matchesAtMostOne| which allows us to
  * stop searching after the first deletion if true.
  */
-template<typename IdPredicate>
+template<typename StringPredicate>
 static bool
-SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicate)
+SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, StringPredicate predicate)
 {
     JSObject *iterobj = cx->enumerators;
     while (iterobj) {
       again:
         NativeIterator *ni = iterobj->getNativeIterator();
         /* This only works for identified surpressed keys, not values. */
         if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
             /* Check whether id is still to come. */
-            HeapId *props_cursor = ni->current();
-            HeapId *props_end = ni->end();
-            for (HeapId *idp = props_cursor; idp < props_end; ++idp) {
+            HeapPtr<JSFlatString> *props_cursor = ni->current();
+            HeapPtr<JSFlatString> *props_end = ni->end();
+            for (HeapPtr<JSFlatString> *idp = props_cursor; idp < props_end; ++idp) {
                 if (predicate(*idp)) {
                     /*
                      * Check whether another property along the prototype chain
                      * became visible as a result of this deletion.
                      */
                     if (obj->getProto()) {
                         JSObject *proto = obj->getProto();
                         JSObject *obj2;
                         JSProperty *prop;
-                        if (!proto->lookupGeneric(cx, *idp, &obj2, &prop))
+                        jsid id;
+                        if (!ValueToId(cx, StringValue(*idp), &id))
+                            return false;
+                        id = js_CheckForStringIndex(id);
+                        if (!proto->lookupGeneric(cx, id, &obj2, &prop))
                             return false;
                         if (prop) {
                             uintN attrs;
                             if (obj2->isNative())
                                 attrs = ((Shape *) prop)->attributes();
-                            else if (!obj2->getGenericAttributes(cx, *idp, &attrs))
+                            else if (!obj2->getGenericAttributes(cx, id, &attrs))
                                 return false;
 
                             if (attrs & JSPROP_ENUMERATE)
                                 continue;
                         }
                     }
 
                     /*
@@ -965,17 +974,17 @@ SuppressDeletedPropertyHelper(JSContext 
                     /*
                      * No property along the prototype chain stepped in to take the
                      * property's place, so go ahead and delete id from the list.
                      * If it is the next property to be enumerated, just skip it.
                      */
                     if (idp == props_cursor) {
                         ni->incCursor();
                     } else {
-                        for (HeapId *p = idp; p + 1 != props_end; p++)
+                        for (HeapPtr<JSFlatString> *p = idp; p + 1 != props_end; p++)
                             *p = *(p + 1);
                         ni->props_end = ni->end() - 1;
                     }
 
                     /* Don't reuse modified native iterators. */
                     ni->flags |= JSITER_UNREUSABLE;
 
                     if (predicate.matchesAtMostOne())
@@ -983,61 +992,52 @@ SuppressDeletedPropertyHelper(JSContext 
                 }
             }
         }
         iterobj = ni->next;
     }
     return true;
 }
 
-class SingleIdPredicate {
-    jsid id;
+class SingleStringPredicate {
+    JSFlatString *str;
 public:
-    SingleIdPredicate(jsid id) : id(id) {}
+    SingleStringPredicate(JSFlatString *str) : str(str) {}
 
-    bool operator()(jsid id) { return id == this->id; }
+    bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); }
     bool matchesAtMostOne() { return true; }
 };
 
 bool
 js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
 {
-    id = js_CheckForStringIndex(id);
-    return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id));
+    JSFlatString *str = IdToString(cx, id);
+    if (!str)
+        return false;
+    return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
 }
 
 bool
 js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32_t index)
 {
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
-    JS_ASSERT(id == js_CheckForStringIndex(id));
-    return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id));
+    return js_SuppressDeletedProperty(cx, obj, id);
 }
 
 class IndexRangePredicate {
     uint32_t begin, end;
 
   public:
     IndexRangePredicate(uint32_t begin, uint32_t end) : begin(begin), end(end) {}
 
-    bool operator()(jsid id) {
-        if (JSID_IS_INT(id)) {
-            jsint i = JSID_TO_INT(id);
-            return i > 0 && begin <= uint32_t(i) && uint32_t(i) < end;
-        }
-
-        if (JS_LIKELY(JSID_IS_ATOM(id))) {
-            JSAtom *atom = JSID_TO_ATOM(id);
-            uint32_t index;
-            return atom->isIndex(&index) && begin <= index && index < end;
-        }
-
-        return false;
+    bool operator()(JSFlatString *str) {
+        uint32_t index;
+        return str->isIndex(&index) && begin <= index && index < end;
     }
 
     bool matchesAtMostOne() { return false; }
 };
 
 bool
 js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_t end)
 {
@@ -1080,17 +1080,20 @@ js_IteratorMore(JSContext *cx, JSObject 
 
             cx->clearPendingException();
             cx->iterValue.setMagic(JS_NO_ITER_VALUE);
             rval->setBoolean(false);
             return true;
         }
     } else {
         JS_ASSERT(!ni->isKeyIter());
-        jsid id = *ni->current();
+        jsid id;
+        if (!ValueToId(cx, StringValue(*ni->current()), &id))
+            return false;
+        id = js_CheckForStringIndex(id);
         ni->incCursor();
         if (!ni->obj->getGeneric(cx, id, rval))
             return false;
         if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval))
             return false;
     }
 
     /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
@@ -1107,17 +1110,17 @@ js_IteratorNext(JSContext *cx, JSObject 
     if (iterobj->isIterator()) {
         /*
          * Implement next directly as all the methods of the native iterator are
          * read-only and permanent.
          */
         NativeIterator *ni = iterobj->getNativeIterator();
         if (ni->isKeyIter()) {
             JS_ASSERT(ni->props_cursor < ni->props_end);
-            *rval = IdToValue(*ni->current());
+            *rval = StringValue(*ni->current());
             ni->incCursor();
 
             if (rval->isString())
                 return true;
 
             JSString *str;
             jsint i;
             if (rval->isInt32() && StaticStrings::hasInt(i = rval->toInt32())) {
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -57,40 +57,40 @@
  */
 #define JSITER_ACTIVE       0x1000
 #define JSITER_UNREUSABLE   0x2000
 
 namespace js {
 
 struct NativeIterator {
     HeapPtrObject obj;
-    HeapId    *props_array;
-    HeapId    *props_cursor;
-    HeapId    *props_end;
+    HeapPtr<JSFlatString> *props_array;
+    HeapPtr<JSFlatString> *props_cursor;
+    HeapPtr<JSFlatString> *props_end;
     const Shape **shapes_array;
     uint32_t  shapes_length;
     uint32_t  shapes_key;
     uint32_t  flags;
     JSObject  *next;  /* Forms cx->enumerators list, garbage otherwise. */
 
     bool isKeyIter() const { return (flags & JSITER_FOREACH) == 0; }
 
-    inline HeapId *begin() const {
+    inline HeapPtr<JSFlatString> *begin() const {
         return props_array;
     }
 
-    inline HeapId *end() const {
+    inline HeapPtr<JSFlatString> *end() const {
         return props_end;
     }
 
     size_t numKeys() const {
         return end() - begin();
     }
 
-    HeapId *current() const {
+    HeapPtr<JSFlatString> *current() const {
         JS_ASSERT(props_cursor < props_end);
         return props_cursor;
     }
 
     void incCursor() {
         props_cursor = props_cursor + 1;
     }
 
--- a/js/src/jslock.cpp
+++ b/js/src/jslock.cpp
@@ -735,35 +735,9 @@ js_Lock(JSContext *cx, JSThinLock *tl)
 }
 
 void
 js_Unlock(JSContext *cx, JSThinLock *tl)
 {
     ThinUnlock(tl, CX_THINLOCK_ID(cx));
 }
 
-void
-js_LockRuntime(JSRuntime *rt)
-{
-    PR_Lock(rt->rtLock);
-#ifdef DEBUG
-    rt->rtLockOwner = js_CurrentThreadId();
-#endif
-}
-
-void
-js_UnlockRuntime(JSRuntime *rt)
-{
-#ifdef DEBUG
-    rt->rtLockOwner = NULL;
-#endif
-    PR_Unlock(rt->rtLock);
-}
-
-#ifdef DEBUG
-JSBool
-js_IsRuntimeLocked(JSRuntime *rt)
-{
-    return js_CurrentThreadId() == rt->rtLockOwner;
-}
-#endif /* DEBUG */
-
 #endif /* JS_THREADSAFE */
--- a/js/src/jslock.h
+++ b/js/src/jslock.h
@@ -108,40 +108,23 @@ typedef PRLock JSLock;
 #define JS_WAIT_CONDVAR(cv,to)      PR_WaitCondVar(cv,to)
 #define JS_NO_TIMEOUT               PR_INTERVAL_NO_TIMEOUT
 #define JS_NOTIFY_CONDVAR(cv)       PR_NotifyCondVar(cv)
 #define JS_NOTIFY_ALL_CONDVAR(cv)   PR_NotifyAllCondVar(cv)
 
 #define JS_LOCK(cx, tl)             js_Lock(cx, tl)
 #define JS_UNLOCK(cx, tl)           js_Unlock(cx, tl)
 
-#define JS_LOCK_RUNTIME(rt)         js_LockRuntime(rt)
-#define JS_UNLOCK_RUNTIME(rt)       js_UnlockRuntime(rt)
-
 extern void js_Lock(JSContext *cx, JSThinLock *tl);
 extern void js_Unlock(JSContext *cx, JSThinLock *tl);
-extern void js_LockRuntime(JSRuntime *rt);
-extern void js_UnlockRuntime(JSRuntime *rt);
 extern int js_SetupLocks(int,int);
 extern void js_CleanupLocks();
 extern void js_InitLock(JSThinLock *);
 extern void js_FinishLock(JSThinLock *);
 
-#ifdef DEBUG
-
-#define JS_IS_RUNTIME_LOCKED(rt)        js_IsRuntimeLocked(rt)
-
-extern JSBool js_IsRuntimeLocked(JSRuntime *rt);
-
-#else
-
-#define JS_IS_RUNTIME_LOCKED(rt)        0
-
-#endif /* DEBUG */
-
 #else  /* !JS_THREADSAFE */
 
 #define JS_ATOMIC_INCREMENT(p)      (++*(p))
 #define JS_ATOMIC_DECREMENT(p)      (--*(p))
 #define JS_ATOMIC_ADD(p,v)          (*(p) += (v))
 #define JS_ATOMIC_SET(p,v)          (*(p) = (v))
 
 #define js_CurrentThreadId()        ((void*)NULL)
@@ -153,21 +136,16 @@ extern JSBool js_IsRuntimeLocked(JSRunti
 #define JS_UNLOCK(cx, tl)           ((void)0)
 
 #define JS_NEW_CONDVAR(l)           NULL
 #define JS_DESTROY_CONDVAR(cv)      ((void)0)
 #define JS_WAIT_CONDVAR(cv,to)      ((void)0)
 #define JS_NOTIFY_CONDVAR(cv)       ((void)0)
 #define JS_NOTIFY_ALL_CONDVAR(cv)   ((void)0)
 
-#define JS_LOCK_RUNTIME(rt)         ((void)0)
-#define JS_UNLOCK_RUNTIME(rt)       ((void)0)
-
-#define JS_IS_RUNTIME_LOCKED(rt)        1
-
 #endif /* !JS_THREADSAFE */
 
 #define JS_LOCK_GC(rt)              JS_ACQUIRE_LOCK((rt)->gcLock)
 #define JS_UNLOCK_GC(rt)            JS_RELEASE_LOCK((rt)->gcLock)
 #define JS_AWAIT_GC_DONE(rt)        JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT)
 #define JS_NOTIFY_GC_DONE(rt)       JS_NOTIFY_ALL_CONDVAR((rt)->gcDone)
 #define JS_AWAIT_REQUEST_DONE(rt)   JS_WAIT_CONDVAR((rt)->requestDone,        \
                                                     JS_NO_TIMEOUT)
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -731,17 +731,17 @@ num_toLocaleString(JSContext *cx, uintN 
         JS_ASSERT(tmpDest - buf < buflen);
         *tmpDest++ = *tmpSrc++;
     }
     while (tmpSrc < end) {
         JS_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
         strcpy(tmpDest, rt->thousandsSeparator);
         tmpDest += thousandsLength;
         JS_ASSERT(tmpDest - buf + *tmpGroup <= buflen);
-        memcpy(tmpDest, tmpSrc, *tmpGroup);
+        js_memcpy(tmpDest, tmpSrc, *tmpGroup);
         tmpDest += *tmpGroup;
         tmpSrc += *tmpGroup;
         if (--nrepeat < 0)
             tmpGroup--;
     }
 
     if (*nint == '.') {
         JS_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen);
@@ -973,25 +973,25 @@ InitRuntimeNumberState(JSRuntime *rt)
     size_t groupingSize = strlen(grouping) + 1;
 
     char *storage = static_cast<char *>(OffTheBooks::malloc_(thousandsSeparatorSize +
                                                              decimalPointSize +
                                                              groupingSize));
     if (!storage)
         return false;
 
-    memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
+    js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
     rt->thousandsSeparator = storage;
     storage += thousandsSeparatorSize;
 
-    memcpy(storage, decimalPoint, decimalPointSize);
+    js_memcpy(storage, decimalPoint, decimalPointSize);
     rt->decimalSeparator = storage;
     storage += decimalPointSize;
 
-    memcpy(storage, grouping, groupingSize);
+    js_memcpy(storage, grouping, groupingSize);
     rt->numGrouping = grouping;
     return true;
 }
 
 void
 FinishRuntimeNumberState(JSRuntime *rt)
 {
     /*
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3619,19 +3619,19 @@ JSObject::TradeGuts(JSContext *cx, JSObj
         /*
          * 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.
          */
         char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::result];
         JS_ASSERT(size <= sizeof(tmp));
 
-        memcpy(tmp, a, size);
-        memcpy(a, b, size);
-        memcpy(b, tmp, size);
+        js_memcpy(tmp, a, size);
+        js_memcpy(a, b, size);
+        js_memcpy(b, tmp, size);
     } else {
         /*
          * If the objects are of differing sizes, use the space we reserved
          * earlier to save the slots from each object and then copy them into
          * the new layout for the other object.
          */
 
         unsigned acap = a->slotSpan();
@@ -3648,19 +3648,19 @@ JSObject::TradeGuts(JSContext *cx, JSObj
             cx->free_(a->slots);
         if (b->hasDynamicSlots())
             cx->free_(b->slots);
 
         void *apriv = a->hasPrivate() ? a->getPrivate() : NULL;
         void *bpriv = b->hasPrivate() ? b->getPrivate() : NULL;
 
         char tmp[sizeof(JSObject)];
-        memcpy(&tmp, a, sizeof tmp);
-        memcpy(a, b, sizeof tmp);
-        memcpy(b, &tmp, sizeof tmp);
+        js_memcpy(&tmp, a, sizeof tmp);
+        js_memcpy(a, b, sizeof tmp);
+        js_memcpy(b, &tmp, sizeof tmp);
 
         if (a->isNative())
             a->shape_->setNumFixedSlots(reserved.newafixed);
         else
             a->shape_ = reserved.newashape;
 
         a->slots = reserved.newaslots;
         a->initSlotRange(0, reserved.bvals.begin(), bcap);
@@ -4313,18 +4313,18 @@ JSObject::growElements(JSContext *cx, ui
             cx->realloc_(getElementsHeader(), oldAllocated * sizeof(Value),
                          newAllocated * sizeof(Value));
         if (!newheader)
             return false;  /* Leave elements as its old size. */
     } else {
         newheader = (ObjectElements *) cx->malloc_(newAllocated * sizeof(Value));
         if (!newheader)
             return false;  /* Ditto. */
-        memcpy(newheader, getElementsHeader(),
-               (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
+        js_memcpy(newheader, getElementsHeader(),
+                  (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
     }
 
     newheader->capacity = actualCapacity;
     elements = newheader->elements();
 
     Debug_SetValueRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen);
 
     if (Probes::objectResizeActive())
@@ -5091,39 +5091,24 @@ js::LookupPropertyWithFlags(JSContext *c
 {
     /* Convert string indices to integers if appropriate. */
     id = js_CheckForStringIndex(id);
 
     return LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp);
 }
 
 bool
-js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, bool global,
+js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, JSObject *scopeChain,
                        JSObject **objp, JSObject **pobjp, JSProperty **propp)
 {
     jsid id = ATOM_TO_JSID(name);
-    JSObject *scopeChain, *obj, *parent, *pobj;
+    JSObject *obj, *parent, *pobj;
     int scopeIndex;
     JSProperty *prop;
 
-    scopeChain = cx->stack.currentScriptedScopeChain();
-
-    if (global) {
-        /*
-         * Skip along the scope chain to the enclosing global object. This is
-         * used for GNAME opcodes where the bytecode emitter has determined a
-         * name access must be on the global. It also insulates us from bugs
-         * in the emitter: type inference will assume that GNAME opcodes are
-         * accessing the global object, and the inferred behavior should match
-         * the actual behavior even if the id could be found on the scope chain
-         * before the global object.
-         */
-        scopeChain = &scopeChain->global();
-    }
-
     /* Scan entries on the scope chain that we can cache across. */
     obj = scopeChain;
     parent = obj->enclosingScope();
     for (scopeIndex = 0;
          parent
          ? IsCacheableNonGlobalScope(obj)
          : !obj->getOps()->lookupProperty;
          ++scopeIndex) {
@@ -5198,20 +5183,20 @@ js::FindPropertyHelper(JSContext *cx, Pr
     return true;
 }
 
 /*
  * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|.
  * Otherwise, its type and meaning depends on the host object's implementation.
  */
 bool
-js::FindProperty(JSContext *cx, PropertyName *name, bool global,
+js::FindProperty(JSContext *cx, PropertyName *name, JSObject *scopeChain,
                  JSObject **objp, JSObject **pobjp, JSProperty **propp)
 {
-    return !!FindPropertyHelper(cx, name, false, global, objp, pobjp, propp);
+    return !!FindPropertyHelper(cx, name, false, scopeChain, objp, pobjp, propp);
 }
 
 JSObject *
 js::FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name)
 {
     /*
      * This function should not be called for a global object or from the
      * trace and should have a valid cache entry for native scopeChain.
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1824,25 +1824,25 @@ ReadPropertyDescriptors(JSContext *cx, J
  * bytecode.
  */
 static const uintN RESOLVE_INFER = 0xffff;
 
 /*
  * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success.
  */
 extern bool
-FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, bool global,
+FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, JSObject *scopeChain,
                    JSObject **objp, JSObject **pobjp, JSProperty **propp);
 
 /*
  * Search for name either on the current scope chain or on the scope chain's
  * global object, per the global parameter.
  */
 extern bool
-FindProperty(JSContext *cx, PropertyName *name, bool global,
+FindProperty(JSContext *cx, PropertyName *name, JSObject *scopeChain,
              JSObject **objp, JSObject **pobjp, JSProperty **propp);
 
 extern JSObject *
 FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name);
 
 }
 
 extern JSObject *
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -1560,17 +1560,17 @@ NewObjectCache::fill(EntryIndex entry_, 
 
     JS_ASSERT(!obj->hasDynamicSlots() && !obj->hasDynamicElements());
 
     entry->clasp = clasp;
     entry->key = key;
     entry->kind = kind;
 
     entry->nbytes = obj->structSize();
-    memcpy(&entry->templateObject, obj, entry->nbytes);
+    js_memcpy(&entry->templateObject, obj, entry->nbytes);
 }
 
 inline void
 NewObjectCache::fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj)
 {
     JS_ASSERT(!proto->isGlobal());
     JS_ASSERT(obj->getProto() == proto);
     return fill(entry, clasp, proto, kind, obj);
@@ -1593,34 +1593,34 @@ NewObjectCache::fillType(EntryIndex entr
 inline JSObject *
 NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_)
 {
     JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
     Entry *entry = &entries[entry_];
 
     JSObject *obj = js_TryNewGCObject(cx, entry->kind);
     if (obj) {
-        memcpy(obj, &entry->templateObject, entry->nbytes);
+        js_memcpy(obj, &entry->templateObject, entry->nbytes);
         Probes::createObject(cx, obj);
         return obj;
     }
 
     /* Copy the entry to the stack first in case it is purged by a GC. */
     size_t nbytes = entry->nbytes;
     char stackObject[sizeof(JSObject_Slots16)];
     JS_ASSERT(nbytes <= sizeof(stackObject));
-    memcpy(&stackObject, &entry->templateObject, nbytes);
+    js_memcpy(&stackObject, &entry->templateObject, nbytes);
 
     JSObject *baseobj = (JSObject *) stackObject;
     RootShape shapeRoot(cx, (Shape **) baseobj->addressOfShape());
     RootTypeObject typeRoot(cx, (types::TypeObject **) baseobj->addressOfType());
 
     obj = js_NewGCObject(cx, entry->kind);
     if (obj) {
-        memcpy(obj, baseobj, nbytes);
+        js_memcpy(obj, baseobj, nbytes);
         Probes::createObject(cx, obj);
         return obj;
     }
 
     return NULL;
 }
 
 static inline bool
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1143,37 +1143,37 @@ static inline bool
 UpdateDecompiledText(SprintStack *ss, jsbytecode *pc, ptrdiff_t todo)
 {
     JSPrinter *jp = ss->printer;
 
     if (jp->decompiledOpcodes && jp->decompiled(pc).text == NULL) {
         const char *text = OFF2STR(&ss->sprinter, todo);
         size_t len = strlen(text) + 1;
 
-        const char *ntext = ss->printer->pool.newArrayUninitialized<char>(len);
+        char *ntext = ss->printer->pool.newArrayUninitialized<char>(len);
         if (!ntext) {
             js_ReportOutOfMemory(ss->sprinter.context);
             return false;
         }
 
-        memcpy((char *) ntext, text, len);
-        jp->decompiled(pc).text = ntext;
+        js_memcpy(ntext, text, len);
+        jp->decompiled(pc).text = const_cast<const char *>(ntext);
     }
 
     return true;
 }
 
 static inline const char *
 SprintDupeStr(SprintStack *ss, const char *str)
 {
     size_t len = strlen(str) + 1;
 
     const char *nstr = ss->printer->pool.newArrayUninitialized<char>(len);
     if (nstr) {
-        memcpy((char *) nstr, str, len);
+        js_memcpy((char *) nstr, str, len);
     } else {
         js_ReportOutOfMemory(ss->sprinter.context);
         nstr = "";
     }
 
     return nstr;
 }
 
--- a/js/src/jsprf.cpp
+++ b/js/src/jsprf.cpp
@@ -339,17 +339,17 @@ static int cvt_f(SprintfState *ss, doubl
     char fout[300];
     int amount = fmt1 - fmt0;
 
     JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
     if (amount >= (int)sizeof(fin)) {
         /* Totally bogus % command to sprintf. Just ignore it */
         return 0;
     }
-    memcpy(fin, fmt0, (size_t)amount);
+    js_memcpy(fin, fmt0, (size_t)amount);
     fin[amount] = 0;
 
     /* Convert floating point using the native sprintf code */
 #ifdef DEBUG
     {
         const char *p = fin;
         while (*p) {
             JS_ASSERT(*p != 'L');
@@ -913,17 +913,17 @@ static int dosprintf(SprintfState *ss, c
           case 'E':
           case 'f':
           case 'g':
             u.d = va_arg(ap, double);
             if( nas != NULL ){
                 i = fmt - dolPt;
                 if( i < (int)sizeof( pattern ) ){
                     pattern[0] = '%';
-                    memcpy( &pattern[1], dolPt, (size_t)i );
+                    js_memcpy( &pattern[1], dolPt, (size_t)i );
                     rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
                 }
             } else
                 rv = cvt_f(ss, u.d, fmt0, fmt);
 
             if (rv < 0) {
                 return rv;
             }
--- a/js/src/jsprobes.cpp
+++ b/js/src/jsprobes.cpp
@@ -123,17 +123,17 @@ Probes::JITGranularityRequested()
  * ActiveFrame. (Note that some of these regions may be zero-length, for
  * example if two ActiveFrames end at the same place.)
  */
 typedef mjit::Compiler::ActiveFrame ActiveFrame;
 
 bool
 Probes::JITWatcher::CollectNativeRegions(RegionVector &regions,
                                          JSRuntime *rt,
-                                         mjit::JITScript *jit,
+                                         mjit::JITChunk *jit,
                                          mjit::JSActiveFrame *outerFrame,
                                          mjit::JSActiveFrame **inlineFrames)
 {
     regions.resize(jit->nInlineFrames * 2 + 2);
 
     mjit::JSActiveFrame **stack =
         rt->array_new<mjit::JSActiveFrame*>(jit->nInlineFrames+2);
     if (!stack)
--- a/js/src/jsprobes.h
+++ b/js/src/jsprobes.h
@@ -43,16 +43,20 @@
 #ifdef INCLUDE_MOZILLA_DTRACE
 #include "javascript-trace.h"
 #endif
 #include "jspubtd.h"
 #include "jsprvtd.h"
 #include "jsscript.h"
 #include "jsobj.h"
 
+#ifdef JS_METHODJIT
+#include "methodjit/MethodJIT.h"
+#endif
+
 namespace js {
 
 namespace mjit {
 struct NativeAddressInfo;
 struct JSActiveFrame;
 }
 
 namespace Probes {
@@ -243,17 +247,17 @@ public:
 
     typedef Vector<NativeRegion, 0, RuntimeAllocPolicy> RegionVector;
 
     virtual JITReportGranularity granularityRequested() = 0;
 
 #ifdef JS_METHODJIT
     static bool CollectNativeRegions(RegionVector &regions,
                                      JSRuntime *rt,
-                                     mjit::JITScript *jit,
+                                     mjit::JITChunk *jit,
                                      mjit::JSActiveFrame *outerFrame,
                                      mjit::JSActiveFrame **inlineFrames);
 
     virtual void registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr,
                                   mjit::JSActiveFrame *outerFrame,
                                   mjit::JSActiveFrame **inlineFrames,
                                   void *mainCodeAddress, size_t mainCodeSize,
                                   void *stubCodeAddress, size_t stubCodeSize) = 0;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1136,18 +1136,18 @@ JSScript::NewScriptFromEmitter(JSContext
                        bce->typesetCount, bce->version());
     if (!script)
         return NULL;
 
     bce->bindings.makeImmutable();
 
     JS_ASSERT(script->mainOffset == 0);
     script->mainOffset = prologLength;
-    memcpy(script->code, bce->prologBase(), prologLength * sizeof(jsbytecode));
-    memcpy(script->main(), bce->base(), mainLength * sizeof(jsbytecode));
+    PodCopy<jsbytecode>(script->code, bce->prologBase(), prologLength);
+    PodCopy<jsbytecode>(script->main(), bce->base(), mainLength);
     nfixed = bce->inFunction()
              ? bce->bindings.countVars()
              : bce->sharpSlots();
     JS_ASSERT(nfixed < SLOTNO_LIMIT);
     script->nfixed = uint16_t(nfixed);
     js_InitAtomMap(cx, bce->atomIndices.getMap(), script->atoms);
 
     filename = bce->parser->tokenStream.getFilename();
@@ -1204,32 +1204,32 @@ JSScript::NewScriptFromEmitter(JSContext
         script->usesEval = true;
     if (bce->flags & TCF_FUN_USES_ARGUMENTS)
         script->usesArguments = true;
     if (bce->flags & TCF_HAS_SINGLETONS)
         script->hasSingletons = true;
 
     if (bce->hasUpvarIndices()) {
         JS_ASSERT(bce->upvarIndices->count() <= bce->upvarMap.length());
-        memcpy(script->upvars()->vector, bce->upvarMap.begin(),
-               bce->upvarIndices->count() * sizeof(bce->upvarMap[0]));
+        PodCopy<UpvarCookie>(script->upvars()->vector, bce->upvarMap.begin(),
+                             bce->upvarIndices->count());
         bce->upvarIndices->clear();
         bce->upvarMap.clear();
     }
 
     if (bce->globalUses.length()) {
-        memcpy(script->globals()->vector, &bce->globalUses[0],
-               bce->globalUses.length() * sizeof(GlobalSlotArray::Entry));
+        PodCopy<GlobalSlotArray::Entry>(script->globals()->vector, &bce->globalUses[0],
+                                        bce->globalUses.length());
     }
 
     if (script->nClosedArgs)
-        memcpy(script->closedSlots, &bce->closedArgs[0], script->nClosedArgs * sizeof(uint32_t));
+        PodCopy<uint32_t>(script->closedSlots, &bce->closedArgs[0], script->nClosedArgs);
     if (script->nClosedVars) {
-        memcpy(&script->closedSlots[script->nClosedArgs], &bce->closedVars[0],
-               script->nClosedVars * sizeof(uint32_t));
+        PodCopy<uint32_t>(&script->closedSlots[script->nClosedArgs], &bce->closedVars[0],
+                          script->nClosedVars);
     }
 
     script->bindings.transfer(cx, &bce->bindings);
 
     fun = NULL;
     if (bce->inFunction()) {
         /*
          * We initialize fun->script() to be the script constructed above
@@ -1710,17 +1710,17 @@ js_CloneScript(JSContext *cx, JSScript *
     }
 
     return newScript;
 }
 
 void
 JSScript::copyClosedSlotsTo(JSScript *other)
 {
-    memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars);
+    js_memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars);
 }
 
 bool
 JSScript::ensureHasDebug(JSContext *cx)
 {
     if (debug)
         return true;
 
@@ -1740,20 +1740,19 @@ JSScript::ensureHasDebug(JSContext *cx)
 
     return true;
 }
 
 bool
 JSScript::recompileForStepMode(JSContext *cx)
 {
 #ifdef JS_METHODJIT
-    js::mjit::JITScript *jit = jitNormal ? jitNormal : jitCtor;
-    if (jit && stepModeEnabled() != jit->singleStepMode) {
-        js::mjit::Recompiler recompiler(cx, this);
-        recompiler.recompile();
+    if (jitNormal || jitCtor) {
+        mjit::ClearAllFrames(cx->compartment);
+        mjit::ReleaseScriptCode(cx, this);
     }
 #endif
     return true;
 }
 
 bool
 JSScript::tryNewStepMode(JSContext *cx, uint32_t newValue)
 {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -330,22 +330,16 @@ class Bindings {
 
 #ifdef JS_METHODJIT
 namespace JSC {
     class ExecutablePool;
 }
 
 #define JS_UNJITTABLE_SCRIPT (reinterpret_cast<void*>(1))
 
-enum JITScriptStatus {
-    JITScript_None,
-    JITScript_Invalid,
-    JITScript_Valid
-};
-
 namespace js { namespace mjit { struct JITScript; } }
 #endif
 
 namespace js {
 
 namespace analyze { class ScriptAnalysis; }
 
 class ScriptOpcodeCounts
@@ -634,37 +628,27 @@ struct JSScript : public js::gc::Cell {
 
 #ifdef JS_METHODJIT
     bool hasJITCode() {
         return jitNormal || jitCtor;
     }
 
     // These methods are implemented in MethodJIT.h.
     inline void **nativeMap(bool constructing);
-    inline void *maybeNativeCodeForPC(bool constructing, jsbytecode *pc);
     inline void *nativeCodeForPC(bool constructing, jsbytecode *pc);
 
     js::mjit::JITScript *getJIT(bool constructing) {
         return constructing ? jitCtor : jitNormal;
     }
 
     size_t getUseCount() const  { return useCount; }
     size_t incUseCount() { return ++useCount; }
     size_t *addressOfUseCount() { return &useCount; }
     void resetUseCount() { useCount = 0; }
 
-    JITScriptStatus getJITStatus(bool constructing) {
-        void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal;
-        if (addr == NULL)
-            return JITScript_None;
-        if (addr == JS_UNJITTABLE_SCRIPT)
-            return JITScript_Invalid;
-        return JITScript_Valid;
-    }
-
     /* Size of the JITScript and all sections.  (This method is implemented in MethodJIT.cpp.) */
     size_t jitDataSize(JSMallocSizeOfFun mallocSizeOf);
 
 #endif
 
     /* Counter accessors. */
     js::OpcodeCounts getCounts(jsbytecode *pc) {
         JS_ASSERT(size_t(pc - code) < length);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3263,17 +3263,17 @@ js_NewStringCopyZ(JSContext *cx, const j
     size_t n = js_strlen(s);
     if (JSShortString::lengthFits(n))
         return NewShortString(cx, s, n);
 
     size_t m = (n + 1) * sizeof(jschar);
     jschar *news = (jschar *) cx->malloc_(m);
     if (!news)
         return NULL;
-    memcpy(news, s, m);
+    js_memcpy(news, s, m);
     JSFixedString *str = js_NewString(cx, news, n);
     if (!str)
         cx->free_(news);
     return str;
 }
 
 JSFixedString *
 js_NewStringCopyZ(JSContext *cx, const char *s)
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -41,16 +41,17 @@
 #define jsstr_h___
 
 #include <ctype.h>
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jsprvtd.h"
 #include "jslock.h"
 #include "jscell.h"
+#include "jsutil.h"
 
 #include "js/HashTable.h"
 #include "vm/Unicode.h"
 
 namespace js {
 
 /* Implemented in jsstrinlines.h */
 class StringBuffer;
@@ -210,17 +211,21 @@ extern size_t
 js_strlen(const jschar *s);
 
 extern jschar *
 js_strchr(const jschar *s, jschar c);
 
 extern jschar *
 js_strchr_limit(const jschar *s, jschar c, const jschar *limit);
 
-#define js_strncpy(t, s, n)     memcpy((t), (s), (n) * sizeof(jschar))
+static JS_ALWAYS_INLINE void
+js_strncpy(jschar *dst, const jschar *src, size_t nelem)
+{
+    return js::PodCopy(dst, src, nelem);
+}
 
 namespace js {
 
 /*
  * Inflate bytes to jschars. Return null on error, otherwise return the jschar
  * or byte vector that was malloc'ed. length is updated to the length of the
  * new string (in jschars).
  */
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -1794,17 +1794,17 @@ class TypedArrayTemplate
         JS_ASSERT(offset <= getLength(thisTypedArrayObj));
         JS_ASSERT(getLength(tarray) <= getLength(thisTypedArrayObj) - offset);
         if (getBuffer(tarray) == getBuffer(thisTypedArrayObj))
             return copyFromWithOverlap(cx, thisTypedArrayObj, tarray, offset);
 
         NativeType *dest = static_cast<NativeType*>((void*)getDataOffset(thisTypedArrayObj)) + offset;
 
         if (getType(tarray) == getType(thisTypedArrayObj)) {
-            memcpy(dest, getDataOffset(tarray), getByteLength(tarray));
+            js_memcpy(dest, getDataOffset(tarray), getByteLength(tarray));
             return true;
         }
 
         uintN srclen = getLength(tarray);
         switch (getType(tarray)) {
           case TypedArray::TYPE_INT8: {
             int8_t *src = static_cast<int8_t*>(getDataOffset(tarray));
             for (uintN i = 0; i < srclen; ++i)
@@ -1874,17 +1874,17 @@ class TypedArrayTemplate
             return true;
         }
 
         // We have to make a copy of the source array here, since
         // there's overlap, and we have to convert types.
         void *srcbuf = cx->malloc_(getLength(tarray));
         if (!srcbuf)
             return false;
-        memcpy(srcbuf, getDataOffset(tarray), getByteLength(tarray));
+        js_memcpy(srcbuf, getDataOffset(tarray), getByteLength(tarray));
 
         switch (getType(tarray)) {
           case TypedArray::TYPE_INT8: {
             int8_t *src = (int8_t*) srcbuf;
             for (uintN i = 0; i < getLength(tarray); ++i)
                 *dest++ = NativeType(*src++);
             break;
           }
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -178,17 +178,17 @@ JS_BasicStatsAccum(JSBasicStats *bs, uin
             if (newscale != oldscale) {
                 uint32_t newhist[11], newbin;
 
                 PodArrayZero(newhist);
                 for (bin = 0; bin <= 10; bin++) {
                     newbin = ValToBin(newscale, BinToVal(oldscale, bin));
                     newhist[newbin] += bs->hist[bin];
                 }
-                memcpy(bs->hist, newhist, sizeof bs->hist);
+                js_memcpy(bs->hist, newhist, sizeof bs->hist);
                 bs->logscale = newscale;
             }
         }
     }
 
     bin = ValToBin(bs->logscale, val);
     ++bs->hist[bin];
 }
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -46,16 +46,27 @@
 
 #include "mozilla/Attributes.h"
 
 #include "js/Utility.h"
 
 /* Forward declarations. */
 struct JSContext;
 
+static JS_ALWAYS_INLINE void *
+js_memcpy(void *dst_, const void *src_, size_t len)
+{
+    char *dst = (char *) dst_;
+    const char *src = (const char *) src_;
+    JS_ASSERT_IF(dst >= src, (size_t) (dst - src) >= len);
+    JS_ASSERT_IF(src >= dst, (size_t) (src - dst) >= len);
+
+    return memcpy(dst, src, len);
+}
+
 #ifdef __cplusplus
 namespace js {
 
 template <class T>
 class AlignedPtrAndFlag
 {
     uintptr_t bits;
 
@@ -283,17 +294,17 @@ JS_ALWAYS_INLINE static void
 PodZero(T *t, size_t nelem)
 {
     /*
      * This function is often called with 'nelem' small; we use an
      * inline loop instead of calling 'memset' with a non-constant
      * length.  The compiler should inline the memset call with constant
      * size, though.
      */
-    for (size_t i = 0; i < nelem; ++i, ++t)
+    for (T *end = t + nelem; t != end; ++t)
         memset(t, 0, sizeof(T));
 }
 
 /*
  * Arrays implicitly convert to pointers to their first element, which is
  * dangerous when combined with the above PodZero definitions. Adding an
  * overload for arrays is ambiguous, so we need another identifier. The
  * ambiguous overload is left to catch mistaken uses of PodZero; if you get a
@@ -306,25 +317,36 @@ template <class T, size_t N>
 JS_ALWAYS_INLINE static void
 PodArrayZero(T (&t)[N])
 {
     memset(t, 0, N * sizeof(T));
 }
 
 template <class T>
 JS_ALWAYS_INLINE static void
+PodAssign(T *dst, const T *src)
+{
+    js_memcpy((char *) dst, (const char *) src, sizeof(T));
+}
+
+template <class T>
+JS_ALWAYS_INLINE static void
 PodCopy(T *dst, const T *src, size_t nelem)
 {
     /* Cannot find portable word-sized abs(). */
     JS_ASSERT_IF(dst >= src, size_t(dst - src) >= nelem);
     JS_ASSERT_IF(src >= dst, size_t(src - dst) >= nelem);
 
     if (nelem < 128) {
+        /*
+         * Avoid using operator= in this loop, as it may have been
+         * intentionally deleted by the POD type.
+         */
         for (const T *srcend = src + nelem; src != srcend; ++src, ++dst)
-            *dst = *src;
+            PodAssign(dst, src);
     } else {
         memcpy(dst, src, nelem * sizeof(T));
     }
 }
 
 template <class T>
 JS_ALWAYS_INLINE static bool
 PodEqual(T *one, T *two, size_t len)
--- a/js/src/jsval.h
+++ b/js/src/jsval.h
@@ -132,19 +132,19 @@ JS_ENUM_HEADER(JSValueType, uint8_t)
     JSVAL_TYPE_INT32               = 0x01,
     JSVAL_TYPE_UNDEFINED           = 0x02,
     JSVAL_TYPE_BOOLEAN             = 0x03,
     JSVAL_TYPE_MAGIC               = 0x04,
     JSVAL_TYPE_STRING              = 0x05,
     JSVAL_TYPE_NULL                = 0x06,
     JSVAL_TYPE_OBJECT              = 0x07,
 
-    /* This never appears in a jsval; it is only provided as an out-of-band value. */
-    JSVAL_TYPE_UNKNOWN             = 0x20
-
+    /* These never appear in a jsval; they are only provided as an out-of-band value. */
+    JSVAL_TYPE_UNKNOWN             = 0x20,
+    JSVAL_TYPE_MISSING             = 0x21
 } JS_ENUM_FOOTER(JSValueType);
 
 JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
 
 #if JS_BITS_PER_WORD == 32
 
 /* Remember to propagate changes to the C defines below. */
 JS_ENUM_HEADER(JSValueTag, uint32_t)
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -668,17 +668,21 @@ Reify(JSContext *cx, JSCompartment *orig
      */
     size_t length = ni->numKeys();
     bool isKeyIter = ni->isKeyIter();
     AutoIdVector keys(cx);
     if (length > 0) {
         if (!keys.resize(length))
             return false;
         for (size_t i = 0; i < length; ++i) {
-            keys[i] = ni->begin()[i];
+            jsid id;
+            if (!ValueToId(cx, StringValue(ni->begin()[i]), &id))
+                return false;
+            id = js_CheckForStringIndex(id);
+            keys[i] = id;
             if (!origin->wrapId(cx, &keys[i]))
                 return false;
         }
     }
 
     close.clear();
     if (!js_CloseIterator(cx, iterObj))
         return false;
--- a/js/src/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -130,26 +130,26 @@ mem_set32(JSXDRState *xdr, uint32_t *lp)
     MEM_INCR(xdr, 4);
     return JS_TRUE;
 }
 
 static JSBool
 mem_getbytes(JSXDRState *xdr, char *bytes, uint32_t len)
 {
     MEM_LEFT(xdr, len);
-    memcpy(bytes, MEM_DATA(xdr), len);
+    js_memcpy(bytes, MEM_DATA(xdr), len);
     MEM_INCR(xdr, len);
     return JS_TRUE;
 }
 
 static JSBool
 mem_setbytes(JSXDRState *xdr, char *bytes, uint32_t len)
 {
     MEM_NEED(xdr, len);
-    memcpy(MEM_DATA(xdr), bytes, len);
+    js_memcpy(MEM_DATA(xdr), bytes, len);
     MEM_INCR(xdr, len);
     return JS_TRUE;
 }
 
 static void *
 mem_raw(JSXDRState *xdr, uint32_t len)
 {
     void *data;
--- a/js/src/methodjit/BaseCompiler.h
+++ b/js/src/methodjit/BaseCompiler.h
@@ -135,18 +135,19 @@ class LinkerHelper : public JSC::LinkBuf
         uintptr_t highest = JS_MAX(myEnd, otherEnd);
 
         return (highest - lowest < INT_MAX);
 #else
         return true;
 #endif
     }
 
-    bool verifyRange(JITScript *jit) {
-        return verifyRange(JSC::JITCode(jit->code.m_code.executableAddress(), jit->code.m_size));
+    bool verifyRange(JITChunk *chunk) {
+        return verifyRange(JSC::JITCode(chunk->code.m_code.executableAddress(),
+                                        chunk->code.m_size));
     }
 
     JSC::ExecutablePool *init(JSContext *cx) {
         // The pool is incref'd after this call, so it's necessary to release()
         // on any failure.
         JSScript *script = cx->fp()->script();
         JSC::ExecutableAllocator *allocator = script->compartment()->jaegerCompartment()->execAlloc();
         allocator->setDestroyCallback(Probes::discardExecutableRegion);
@@ -183,32 +184,32 @@ class NativeStubLinker : public LinkerHe
 {
   public:
 #ifdef JS_CPU_X64
     typedef JSC::MacroAssembler::DataLabelPtr FinalJump;
 #else
     typedef JSC::MacroAssembler::Jump FinalJump;
 #endif
 
-    NativeStubLinker(Assembler &masm, JITScript *jit, jsbytecode *pc, FinalJump done)
-        : LinkerHelper(masm, JSC::METHOD_CODE), jit(jit), pc(pc), done(done)
+    NativeStubLinker(Assembler &masm, JITChunk *chunk, jsbytecode *pc, FinalJump done)
+        : LinkerHelper(masm, JSC::METHOD_CODE), chunk(chunk), pc(pc), done(done)
     {}
 
     bool init(JSContext *cx);
 
     void patchJump(JSC::CodeLocationLabel target) {
 #ifdef JS_CPU_X64
         patch(done, target);
 #else
         link(done, target);
 #endif
     }
 
   private:
-    JITScript *jit;
+    JITChunk *chunk;
     jsbytecode *pc;
     FinalJump done;
 };
 
 bool
 NativeStubEpilogue(VMFrame &f, Assembler &masm, NativeStubLinker::FinalJump *result,
                    int32_t initialFrameDepth, int32_t vpOffset,
                    MaybeRegisterID typeReg, MaybeRegisterID dataReg);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -90,20 +90,23 @@ static const char *OpcodeNames[] = {
 #endif
 
 /*
  * Number of times a script must be called or had a backedge before we try to
  * inline its calls.
  */
 static const size_t USES_BEFORE_INLINING = 10000;
 
-mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, bool isConstructing)
+mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript,
+                         unsigned chunkIndex, bool isConstructing)
   : BaseCompiler(cx),
     outerScript(outerScript),
+    chunkIndex(chunkIndex),
     isConstructing(isConstructing),
+    outerChunk(outerJIT()->chunkDescriptor(chunkIndex)),
     ssa(cx, outerScript),
     globalObj(outerScript->hasGlobal() ? outerScript->global() : NULL),
     globalSlots(globalObj ? globalObj->getRawSlots() : NULL),
     frame(cx, *thisFromCtor(), masm, stubcc),
     a(NULL), outer(NULL), script(NULL), PC(NULL), loop(NULL),
     inlineFrames(CompilerAllocPolicy(cx, *thisFromCtor())),
     branchPatches(CompilerAllocPolicy(cx, *thisFromCtor())),
 #if defined JS_MONOIC
@@ -118,18 +121,19 @@ mjit::Compiler::Compiler(JSContext *cx, 
     setElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
 #endif
     callPatches(CompilerAllocPolicy(cx, *thisFromCtor())),
     callSites(CompilerAllocPolicy(cx, *thisFromCtor())),
     doubleList(CompilerAllocPolicy(cx, *thisFromCtor())),
     fixedIntToDoubleEntries(CompilerAllocPolicy(cx, *thisFromCtor())),
     fixedDoubleToAnyEntries(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTables(CompilerAllocPolicy(cx, *thisFromCtor())),
-    jumpTableOffsets(CompilerAllocPolicy(cx, *thisFromCtor())),
+    jumpTableEdges(CompilerAllocPolicy(cx, *thisFromCtor())),
     loopEntries(CompilerAllocPolicy(cx, *thisFromCtor())),
+    chunkEdges(CompilerAllocPolicy(cx, *thisFromCtor())),
     stubcc(cx, *thisFromCtor(), frame),
     debugMode_(cx->compartment->debugMode()),
     inlining_(false),
     hasGlobalReallocation(false),
     oomInVector(false),
     overflowICSpace(false),
     gcNumber(cx->runtime->gcNumber),
     applyTricks(NoApplyTricks),
@@ -141,34 +145,24 @@ mjit::Compiler::Compiler(JSContext *cx, 
          cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))) {
         inlining_ = true;
     }
 }
 
 CompileStatus
 mjit::Compiler::compile()
 {
-    JS_ASSERT_IF(isConstructing, !outerScript->jitCtor);
-    JS_ASSERT_IF(!isConstructing, !outerScript->jitNormal);
-
-    JITScript **jit = isConstructing ? &outerScript->jitCtor : &outerScript->jitNormal;
+    JS_ASSERT(!outerChunk.chunk);
+
     void **checkAddr = isConstructing
                        ? &outerScript->jitArityCheckCtor
                        : &outerScript->jitArityCheckNormal;
 
-    CompileStatus status = performCompilation(jit);
-    if (status == Compile_Okay) {
-        // Global scripts don't have an arity check entry. That's okay, we
-        // just need a pointer so the VM can quickly decide whether this
-        // method can be JIT'd or not. Global scripts cannot be IC'd, since
-        // they have no functions, so there is no danger.
-        *checkAddr = (*jit)->arityCheckEntry
-                     ? (*jit)->arityCheckEntry
-                     : (*jit)->invokeEntry;
-    } else if (status != Compile_Retry) {
+    CompileStatus status = performCompilation();
+    if (status != Compile_Okay && status != Compile_Retry) {
         *checkAddr = JS_UNJITTABLE_SCRIPT;
         if (outerScript->function()) {
             outerScript->uninlineable = true;
             types::MarkTypeObjectFlags(cx, outerScript->function(),
                                        types::OBJECT_FLAG_UNINLINEABLE);
         }
     }
 
@@ -235,17 +229,24 @@ mjit::Compiler::scanInlineCalls(uint32_t
         script->global() != globalObj ||
         (script->function() && script->function()->getParent() != globalObj) ||
         (script->function() && script->function()->isHeavyweight()) ||
         script->isActiveEval) {
         return Compile_Okay;
     }
 
     uint32_t nextOffset = 0;
-    while (nextOffset < script->length) {
+    uint32_t lastOffset = script->length;
+
+    if (index == CrossScriptSSA::OUTER_FRAME) {
+        nextOffset = outerChunk.begin;
+        lastOffset = outerChunk.end;
+    }
+
+    while (nextOffset < lastOffset) {
         uint32_t offset = nextOffset;
         jsbytecode *pc = script->code + offset;
         nextOffset = offset + GetBytecodeLength(pc);
 
         Bytecode *code = analysis->maybeCode(pc);
         if (!code)
             continue;
 
@@ -502,57 +503,63 @@ mjit::Compiler::popActiveFrame()
         if (status_ != Compile_Okay) {                               \
             if (oomInVector || masm.oom() || stubcc.masm.oom())      \
                 js_ReportOutOfMemory(cx);                            \
             return status_;                                          \
         }                                                            \
     JS_END_MACRO
 
 CompileStatus
-mjit::Compiler::performCompilation(JITScript **jitp)
+mjit::Compiler::performCompilation()
 {
-    JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n",
-               outerScript->filename, outerScript->lineno, outerScript->length);
+    JaegerSpew(JSpew_Scripts,
+               "compiling script (file \"%s\") (line \"%d\") (length \"%d\") (chunk \"%d\")\n",
+               outerScript->filename, outerScript->lineno, outerScript->length, chunkIndex);
 
     if (inlining()) {
-        JaegerSpew(JSpew_Inlining, "inlining calls in script (file \"%s\") (line \"%d\")\n",
+        JaegerSpew(JSpew_Inlining,
+                   "inlining calls in script (file \"%s\") (line \"%d\")\n",
                    outerScript->filename, outerScript->lineno);
     }
 
 #ifdef JS_METHODJIT_SPEW
     Profiler prof;
     prof.start();
 #endif
 
 #ifdef JS_METHODJIT
     outerScript->debugMode = debugMode();
 #endif
 
     JS_ASSERT(cx->compartment->activeInference);
 
     {
-        types::AutoEnterCompilation enter(cx, outerScript);
+        types::AutoEnterCompilation enter(cx, outerScript, isConstructing, chunkIndex);
 
         CHECK_STATUS(checkAnalysis(outerScript));
         if (inlining())
             CHECK_STATUS(scanInlineCalls(CrossScriptSSA::OUTER_FRAME, 0));
         CHECK_STATUS(pushActiveFrame(outerScript, 0));
-        CHECK_STATUS(generatePrologue());
+        if (chunkIndex == 0)
+            CHECK_STATUS(generatePrologue());
         CHECK_STATUS(generateMethod());
-        CHECK_STATUS(generateEpilogue());
-        CHECK_STATUS(finishThisUp(jitp));
+        if (outerJIT() && chunkIndex == outerJIT()->nchunks - 1)
+            CHECK_STATUS(generateEpilogue());
+        CHECK_STATUS(finishThisUp());
     }
 
 #ifdef JS_METHODJIT_SPEW
     prof.stop();
     JaegerSpew(JSpew_Prof, "compilation took %d us\n", prof.time_us());
 #endif
 
     JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%u\")\n",
-               (*jitp)->code.m_code.executableAddress(), unsigned((*jitp)->code.m_size));
+               outerChunk.chunk->code.m_code.executableAddress(),
+               unsigned(outerChunk.chunk->code.m_size));
+
     return Compile_Okay;
 }
 
 #undef CHECK_STATUS
 
 mjit::JSActiveFrame::JSActiveFrame()
     : parent(NULL), parentPC(NULL), script(NULL), inlineIndex(UINT32_MAX)
 {
@@ -612,55 +619,378 @@ mjit::Compiler::prepareInferenceTypes(JS
 
     a->varTypes = (VarType *)
         cx->calloc_(TotalSlots(script) * sizeof(VarType));
     if (!a->varTypes)
         return Compile_Error;
 
     for (uint32_t slot = ArgSlot(0); slot < TotalSlots(script); slot++) {
         VarType &vt = a->varTypes[slot];
-        vt.types = types::TypeScript::SlotTypes(script, slot);
-        vt.type = vt.types->getKnownTypeTag(cx);
+        vt.setTypes(types::TypeScript::SlotTypes(script, slot));
     }
 
     return Compile_Okay;
 }
 
-CompileStatus JS_NEVER_INLINE
-mjit::TryCompile(JSContext *cx, JSScript *script, bool construct)
+/*
+ * Number of times a script must be called or have back edges taken before we
+ * run it in the methodjit. We wait longer if type inference is enabled, to
+ * allow more gathering of type information and less recompilation.
+ */
+static const size_t USES_BEFORE_COMPILE       = 16;
+static const size_t INFER_USES_BEFORE_COMPILE = 40;
+
+/* Target maximum size, in bytecode length, for a compiled chunk of a script. */
+static uint32_t CHUNK_LIMIT = 1500;
+
+void
+mjit::SetChunkLimit(uint32_t limit)
+{
+    if (limit)
+        CHUNK_LIMIT = limit;
+}
+
+JITScript *
+MakeJITScript(JSContext *cx, JSScript *script, bool construct)
 {
+    if (!script->ensureRanAnalysis(cx, NULL))
+        return NULL;
+
+    ScriptAnalysis *analysis = script->analysis();
+
+    JITScript *&location = construct ? script->jitCtor : script->jitNormal;
+
+    Vector<ChunkDescriptor> chunks(cx);
+    Vector<CrossChunkEdge> edges(cx);
+
+    /*
+     * Chunk compilation is not supported on x64, since there is no guarantee
+     * that cross chunk jumps will be patchable even to go to the default shim.
+     */
+#ifndef JS_CPU_X64
+    if (script->length < CHUNK_LIMIT || !cx->typeInferenceEnabled()) {
+#endif
+        ChunkDescriptor desc;
+        desc.begin = 0;
+        desc.end = script->length;
+        if (!chunks.append(desc))
+            return NULL;
+#ifndef JS_CPU_X64
+    } else {
+        if (!script->ensureRanInference(cx))
+            return NULL;
+
+        /* Outgoing edges within the current chunk. */
+        Vector<CrossChunkEdge> currentEdges(cx);
+        uint32_t chunkStart = 0;
+
+        unsigned offset, nextOffset = 0;
+        while (nextOffset < script->length) {
+            offset = nextOffset;
+
+            jsbytecode *pc = script->code + offset;
+            JSOp op = JSOp(*pc);
+
+            nextOffset = offset + GetBytecodeLength(pc);
+
+            Bytecode *code = analysis->maybeCode(offset);
+            if (!code)
+                continue;
+
+            /* Whether this should be the last opcode in the chunk. */
+            bool finishChunk = false;
+
+            /* Keep going, override finishChunk. */
+            bool preserveChunk = false;
+
+            /*
+             * Add an edge for opcodes which perform a branch. Skip LABEL ops,
+             * which do not actually branch. XXX LABEL should not be JOF_JUMP.
+             */
+            uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
+            if (type == JOF_JUMP && op != JSOP_LABEL) {
+                CrossChunkEdge edge;
+                edge.source = offset;
+                edge.target = FollowBranch(cx, script, pc - script->code);
+                if (edge.target < offset) {
+                    /* Always end chunks after loop back edges. */
+                    finishChunk = true;
+                    if (edge.target < chunkStart) {
+                        analysis->getCode(edge.target).safePoint = true;
+                        if (!edges.append(edge))
+                            return NULL;
+                    }
+                } else if (edge.target == nextOffset) {
+                    /*
+                     * Override finishChunk for bytecodes which directly
+                     * jump to their fallthrough opcode ('if (x) {}'). This
+                     * creates two CFG edges with the same source/target, which
+                     * will confuse the compiler's edge patching code.
+                     */
+                    preserveChunk = true;
+                } else {
+                    if (!currentEdges.append(edge))
+                        return NULL;
+                }
+            }
+
+            /*
+             * Watch for cross-chunk edges in a table switch. Don't handle
+             * lookup switches, as these are always stubbed.
+             */
+            if (op == JSOP_TABLESWITCH) {
+                jsbytecode *pc2 = pc;
+                unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
+                pc2 += JUMP_OFFSET_LEN;
+                jsint low = GET_JUMP_OFFSET(pc2);
+                pc2 += JUMP_OFFSET_LEN;
+                jsint high = GET_JUMP_OFFSET(pc2);
+                pc2 += JUMP_OFFSET_LEN;
+
+                CrossChunkEdge edge;
+                edge.source = offset;
+                edge.target = defaultOffset;
+                if (!currentEdges.append(edge))
+                    return NULL;
+
+                for (jsint i = low; i <= high; i++) {
+                    unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
+                    if (targetOffset != offset) {
+                        /*
+                         * This can end up inserting duplicate edges, all but
+                         * the first of which will be ignored.
+                         */
+                        CrossChunkEdge edge;
+                        edge.source = offset;
+                        edge.target = targetOffset;
+                        if (!currentEdges.append(edge))
+                            return NULL;
+                    }
+                    pc2 += JUMP_OFFSET_LEN;
+                }
+            }
+
+            if (unsigned(offset - chunkStart) > CHUNK_LIMIT)
+                finishChunk = true;
+
+            if (nextOffset >= script->length || !analysis->maybeCode(nextOffset)) {
+                /* Ensure that chunks do not start on unreachable opcodes. */
+                preserveChunk = true;
+            } else {
+                /*
+                 * Start new chunks at the opcode before each loop head.
+                 * This ensures that the initial goto for loops is included in
+                 * the same chunk as the loop itself.
+                 */
+                jsbytecode *nextpc = script->code + nextOffset;
+
+                /*
+                 * Don't insert a chunk boundary in the middle of two opcodes
+                 * which may be fused together.
+                 */
+                switch (JSOp(*nextpc)) {
+                  case JSOP_POP:
+                  case JSOP_IFNE:
+                  case JSOP_IFEQ:
+                    preserveChunk = true;
+                    break;
+                  default:
+                    break;
+                }
+
+                uint32_t afterOffset = nextOffset + GetBytecodeLength(nextpc);
+                if (afterOffset < script->length) {
+                    if (analysis->maybeCode(afterOffset) &&
+                        JSOp(script->code[afterOffset]) == JSOP_LOOPHEAD &&
+                        analysis->getLoop(afterOffset))
+                    {
+                        finishChunk = true;
+                    }
+                }
+            }
+
+            if (finishChunk && !preserveChunk) {
+                ChunkDescriptor desc;
+                desc.begin = chunkStart;
+                desc.end = nextOffset;
+                if (!chunks.append(desc))
+                    return NULL;
+
+                /* Add an edge for fallthrough from this chunk to the next one. */
+                if (!BytecodeNoFallThrough(op)) {
+                    CrossChunkEdge edge;
+                    edge.source = offset;
+                    edge.target = nextOffset;
+                    analysis->getCode(edge.target).safePoint = true;
+                    if (!edges.append(edge))
+                        return NULL;
+                }
+
+                chunkStart = nextOffset;
+                for (unsigned i = 0; i < currentEdges.length(); i++) {
+                    const CrossChunkEdge &edge = currentEdges[i];
+                    if (edge.target >= nextOffset) {
+                        analysis->getCode(edge.target).safePoint = true;
+                        if (!edges.append(edge))
+                            return NULL;
+                    }
+                }
+                currentEdges.clear();
+            }
+        }
+
+        if (chunkStart != script->length) {
+            ChunkDescriptor desc;
+            desc.begin = chunkStart;
+            desc.end = script->length;
+            if (!chunks.append(desc))
+                return NULL;
+        }
+    }
+#endif /* !JS_CPU_X64 */
+
+    size_t dataSize = sizeof(JITScript)
+        + (chunks.length() * sizeof(ChunkDescriptor))
+        + (edges.length() * sizeof(CrossChunkEdge));
+    uint8_t *cursor = (uint8_t *) cx->calloc_(dataSize);
+    if (!cursor)
+        return NULL;
+
+    JITScript *jit = (JITScript *) cursor;
+    cursor += sizeof(JITScript);
+
+    jit->script = script;
+    JS_INIT_CLIST(&jit->callers);
+
+    jit->nchunks = chunks.length();
+    for (unsigned i = 0; i < chunks.length(); i++) {
+        const ChunkDescriptor &a = chunks[i];
+        ChunkDescriptor &b = jit->chunkDescriptor(i);
+        b.begin = a.begin;
+        b.end = a.end;
+
+        if (chunks.length() == 1) {
+            /* Seed the chunk's count so it is immediately compiled. */
+            b.counter = INFER_USES_BEFORE_COMPILE;
+        }
+    }
+
+    if (edges.empty()) {
+        location = jit;
+        return jit;
+    }
+
+    jit->nedges = edges.length();
+    CrossChunkEdge *jitEdges = jit->edges();
+    for (unsigned i = 0; i < edges.length(); i++) {
+        const CrossChunkEdge &a = edges[i];
+        CrossChunkEdge &b = jitEdges[i];
+        b.source = a.source;
+        b.target = a.target;
+    }
+
+    /* Generate a pool with all cross chunk shims, and set shimLabel for each edge. */
+    Assembler masm;
+    for (unsigned i = 0; i < jit->nedges; i++) {
+        jsbytecode *pc = script->code + jitEdges[i].target;
+        jitEdges[i].shimLabel = (void *) masm.distanceOf(masm.label());
+        masm.move(JSC::MacroAssembler::ImmPtr(&jitEdges[i]), Registers::ArgReg1);
+        masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::CrossChunkShim),
+                            pc, NULL, script->nfixed + analysis->getCode(pc).stackDepth);
+    }
+    LinkerHelper linker(masm, JSC::METHOD_CODE);
+    JSC::ExecutablePool *ep = linker.init(cx);
+    if (!ep)
+        return NULL;
+    jit->shimPool = ep;
+
+    masm.finalize(linker);
+    uint8_t *shimCode = (uint8_t *) linker.finalizeCodeAddendum().executableAddress();
+
+    JS_ALWAYS_TRUE(linker.verifyRange(JSC::JITCode(shimCode, masm.size())));
+
+    JaegerSpew(JSpew_PICs, "generated SHIM POOL stub %p (%lu bytes)\n",
+               shimCode, (unsigned long)masm.size());
+
+    for (unsigned i = 0; i < jit->nedges; i++) {
+        CrossChunkEdge &edge = jitEdges[i];
+        edge.shimLabel = shimCode + (size_t) edge.shimLabel;
+    }
+
+    location = jit;
+    return jit;
+}
+
+CompileStatus
+mjit::CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
+                   bool construct, CompileRequest request)
+{
+  restart:
+    if (!cx->methodJitEnabled)
+        return Compile_Abort;
+
+    void *addr = construct ? script->jitArityCheckCtor : script->jitArityCheckNormal;
+    if (addr == JS_UNJITTABLE_SCRIPT)
+        return Compile_Abort;
+
+    JITScript *jit = script->getJIT(construct);
+
+    if (request == CompileRequest_Interpreter &&
+        !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
+        (cx->typeInferenceEnabled()
+         ? script->incUseCount() <= INFER_USES_BEFORE_COMPILE
+         : script->incUseCount() <= USES_BEFORE_COMPILE))
+    {
+        return Compile_Skipped;
+    }
+
 #if JS_HAS_SHARP_VARS
     if (script->hasSharps)
         return Compile_Abort;
 #endif
-    bool ok = cx->compartment->ensureJaegerCompartmentExists(cx);
-    if (!ok)
-        return Compile_Abort;
+
+    if (!cx->compartment->ensureJaegerCompartmentExists(cx))
+        return Compile_Error;
 
     // Ensure that constructors have at least one slot.
     if (construct && !script->nslots)
         script->nslots++;
 
+    if (!jit) {
+        jit = MakeJITScript(cx, script, construct);
+        if (!jit)
+            return Compile_Error;
+    }
+    unsigned chunkIndex = jit->chunkIndex(pc);
+    ChunkDescriptor &desc = jit->chunkDescriptor(chunkIndex);
+
+    if (desc.chunk)
+        return Compile_Okay;
+
+    if (request == CompileRequest_Interpreter &&
+        !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
+        ++desc.counter <= INFER_USES_BEFORE_COMPILE)
+    {
+        return Compile_Skipped;
+    }
+
     CompileStatus status;
     {
         types::AutoEnterTypeInference enter(cx, true);
 
-        Compiler cc(cx, script, construct);
+        Compiler cc(cx, script, chunkIndex, construct);
         status = cc.compile();
     }
 
     if (status == Compile_Okay) {
         /*
-         * Compiling a script can occasionally trigger its own recompilation.
-         * Treat this the same way as a static overflow and wait for another
-         * attempt to compile the script.
+         * Compiling a script can occasionally trigger its own recompilation,
+         * so go back through the compilation logic.
          */
-        JITScriptStatus status = script->getJITStatus(construct);
-        JS_ASSERT(status != JITScript_Invalid);
-        return (status == JITScript_Valid) ? Compile_Okay : Compile_Retry;
+        goto restart;
     }
 
     /* Non-OOM errors should have an associated exception. */
     JS_ASSERT_IF(status == Compile_Error,
                  cx->isExceptionPending() || cx->runtime->hadOutOfMemory);
 
     return status;
 }
@@ -867,17 +1197,17 @@ mjit::Compiler::generatePrologue()
 }
 
 void
 mjit::Compiler::ensureDoubleArguments()
 {
     /* Convert integer arguments which were inferred as (int|double) to doubles. */
     for (uint32_t i = 0; script->function() && i < script->function()->nargs; i++) {
         uint32_t slot = ArgSlot(i);
-        if (a->varTypes[slot].type == JSVAL_TYPE_DOUBLE && analysis->trackSlot(slot))
+        if (a->varTypes[slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE && analysis->trackSlot(slot))
             frame.ensureDouble(frame.getArg(i));
     }
 }
 
 void
 mjit::Compiler::markUndefinedLocals()
 {
     uint32_t depth = ssa.getFrame(a->inlineIndex).depth;
@@ -901,17 +1231,17 @@ mjit::Compiler::markUndefinedLocals()
 
 CompileStatus
 mjit::Compiler::generateEpilogue()
 {
     return Compile_Okay;
 }
 
 CompileStatus
-mjit::Compiler::finishThisUp(JITScript **jitp)
+mjit::Compiler::finishThisUp()
 {
     RETURN_IF_OOM(Compile_Error);
 
     /*
      * 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)
@@ -919,16 +1249,20 @@ mjit::Compiler::finishThisUp(JITScript *
 
     /*
      * Watch for GCs which occurred during compilation. These may have
      * renumbered shapes baked into the jitcode.
      */
     if (cx->runtime->gcNumber != gcNumber)
         return Compile_Retry;
 
+    /* The JIT will not have been cleared if no GC has occurred. */
+    JITScript *jit = outerJIT();
+    JS_ASSERT(jit != NULL);
+
     if (overflowICSpace) {
         JaegerSpew(JSpew_Scripts, "dumped a constant pool while generating an IC\n");
         return Compile_Abort;
     }
 
     a->mainCodeEnd = masm.size();
     a->stubCodeEnd = stubcc.size();
 
@@ -949,42 +1283,48 @@ mjit::Compiler::finishThisUp(JITScript *
     size_t codeSize = masm.size() +
 #if defined(JS_CPU_MIPS) 
                       stubcc.size() + sizeof(double) +
 #else
                       stubcc.size() +
 #endif
                       (masm.numDoubles() * sizeof(double)) +
                       (stubcc.masm.numDoubles() * sizeof(double)) +
-                      jumpTableOffsets.length() * sizeof(void *);
+                      jumpTableEdges.length() * sizeof(void *);
+
+    Vector<ChunkJumpTableEdge> chunkJumps(cx);
+    if (!chunkJumps.reserve(jumpTableEdges.length()))
+        return Compile_Error;
 
     JSC::ExecutablePool *execPool;
     uint8_t *result = (uint8_t *)script->compartment()->jaegerCompartment()->execAlloc()->
                     alloc(codeSize, &execPool, JSC::METHOD_CODE);
     if (!result) {
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
     JS_ASSERT(execPool);
     JSC::ExecutableAllocator::makeWritable(result, codeSize);
     masm.executableCopy(result);
     stubcc.masm.executableCopy(result + masm.size());
 
     JSC::LinkBuffer fullCode(result, codeSize, JSC::METHOD_CODE);
     JSC::LinkBuffer stubCode(result + masm.size(), stubcc.size(), JSC::METHOD_CODE);
 
+    JS_ASSERT(!loop);
+
     size_t nNmapLive = loopEntries.length();
-    for (size_t i = 0; i < script->length; i++) {
+    for (size_t i = outerChunk.begin; i < outerChunk.end; i++) {
         Bytecode *opinfo = analysis->maybeCode(i);
         if (opinfo && opinfo->safePoint)
             nNmapLive++;
     }
 
-    /* Please keep in sync with JITScript::scriptDataSize! */
-    size_t dataSize = sizeof(JITScript) +
+    /* Please keep in sync with JITChunk::scriptDataSize! */
+    size_t dataSize = sizeof(JITChunk) +
                       sizeof(NativeMapEntry) * nNmapLive +
                       sizeof(InlineFrame) * inlineFrames.length() +
                       sizeof(CallSite) * callSites.length() +
 #if defined JS_MONOIC
                       sizeof(ic::GetGlobalNameIC) * getGlobalNames.length() +
                       sizeof(ic::SetGlobalNameIC) * setGlobalNames.length() +
                       sizeof(ic::CallICInfo) * callICs.length() +
                       sizeof(ic::EqualityICInfo) * equalityICs.length() +
@@ -998,47 +1338,50 @@ mjit::Compiler::finishThisUp(JITScript *
 
     uint8_t *cursor = (uint8_t *)cx->calloc_(dataSize);
     if (!cursor) {
         execPool->release();
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
 
-    JITScript *jit = new(cursor) JITScript;
-    cursor += sizeof(JITScript);
+    JITChunk *chunk = new(cursor) JITChunk;
+    cursor += sizeof(JITChunk);
 
     JS_ASSERT(outerScript == script);
 
-    jit->script = script;
-    jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size());
-    jit->invokeEntry = result;
-    jit->singleStepMode = script->stepModeEnabled();
-    if (script->function()) {
-        jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress();
-        jit->argsCheckEntry = stubCode.locationOf(argsCheckLabel).executableAddress();
-        jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress();
-    }
-    jit->pcLengths = pcLengths;
+    chunk->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size());
+    chunk->pcLengths = pcLengths;
+
+    if (chunkIndex == 0) {
+        jit->invokeEntry = result;
+        if (script->function()) {
+            jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress();
+            jit->argsCheckEntry = stubCode.locationOf(argsCheckLabel).executableAddress();
+            jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress();
+            void *&addr = isConstructing ? script->jitArityCheckCtor : script->jitArityCheckNormal;
+            addr = jit->arityCheckEntry;
+        }
+    }
 
     /*
      * WARNING: mics(), callICs() et al depend on the ordering of these
-     * variable-length sections.  See JITScript's declaration for details.
+     * variable-length sections.  See JITChunk's declaration for details.
      */
 
     /* ICs can only refer to bytecodes in the outermost script, not inlined calls. */
     Label *jumpMap = a->jumpMap;
 
     /* Build the pc -> ncode mapping. */
     NativeMapEntry *jitNmap = (NativeMapEntry *)cursor;
-    jit->nNmapPairs = nNmapLive;
-    cursor += sizeof(NativeMapEntry) * jit->nNmapPairs;
+    chunk->nNmapPairs = nNmapLive;
+    cursor += sizeof(NativeMapEntry) * chunk->nNmapPairs;
     size_t ix = 0;
-    if (jit->nNmapPairs > 0) {
-        for (size_t i = 0; i < script->length; i++) {
+    if (chunk->nNmapPairs > 0) {
+        for (size_t i = outerChunk.begin; i < outerChunk.end; i++) {
             Bytecode *opinfo = analysis->maybeCode(i);
             if (opinfo && opinfo->safePoint) {
                 Label L = jumpMap[i];
                 JS_ASSERT(L.isSet());
                 jitNmap[ix].bcOff = i;
                 jitNmap[ix].ncode = (uint8_t *)(result + masm.distanceOf(L));
                 ix++;
             }
@@ -1053,39 +1396,39 @@ mjit::Compiler::finishThisUp(JITScript *
                     break;
                 }
             }
             jitNmap[j].bcOff = entry.pcOffset;
             jitNmap[j].ncode = (uint8_t *) stubCode.locationOf(entry.label).executableAddress();
             ix++;
         }
     }
-    JS_ASSERT(ix == jit->nNmapPairs);
+    JS_ASSERT(ix == chunk->nNmapPairs);
 
     /* Build the table of inlined frames. */
     InlineFrame *jitInlineFrames = (InlineFrame *)cursor;
-    jit->nInlineFrames = inlineFrames.length();
-    cursor += sizeof(InlineFrame) * jit->nInlineFrames;
-    for (size_t i = 0; i < jit->nInlineFrames; i++) {
+    chunk->nInlineFrames = inlineFrames.length();
+    cursor += sizeof(InlineFrame) * chunk->nInlineFrames;
+    for (size_t i = 0; i < chunk->nInlineFrames; i++) {
         InlineFrame &to = jitInlineFrames[i];
         ActiveFrame *from = inlineFrames[i];
         if (from->parent != outer)
             to.parent = &jitInlineFrames[from->parent->inlineIndex];
         else
             to.parent = NULL;
         to.parentpc = from->parentPC;
         to.fun = from->script->function();
         to.depth = ssa.getFrame(from->inlineIndex).depth;
     }
 
     /* Build the table of call sites. */
     CallSite *jitCallSites = (CallSite *)cursor;
-    jit->nCallSites = callSites.length();
-    cursor += sizeof(CallSite) * jit->nCallSites;
-    for (size_t i = 0; i < jit->nCallSites; i++) {
+    chunk->nCallSites = callSites.length();
+    cursor += sizeof(CallSite) * chunk->nCallSites;
+    for (size_t i = 0; i < chunk->nCallSites; i++) {
         CallSite &to = jitCallSites[i];
         InternalCallSite &from = callSites[i];
 
         /* Patch stores of f.regs.inlined for stubs called from within inline frames. */
         if (cx->typeInferenceEnabled() &&
             from.rejoin != REJOIN_TRAP &&
             from.rejoin != REJOIN_SCRIPTED &&
             from.inlineIndex != UINT32_MAX) {
@@ -1107,44 +1450,44 @@ mjit::Compiler::finishThisUp(JITScript *
          * calls. InvariantFailure will patch its own return address to this
          * pointer before triggering recompilation.
          */
         if (from.loopPatch.hasPatch)
             stubCode.patch(from.loopPatch.codePatch, result + codeOffset);
     }
 
 #if defined JS_MONOIC
-    JS_INIT_CLIST(&jit->callers);
-
-    if (script->function() && cx->typeInferenceEnabled()) {
-        jit->argsCheckStub = stubCode.locationOf(argsCheckStub);
-        jit->argsCheckFallthrough = stubCode.locationOf(argsCheckFallthrough);
-        jit->argsCheckJump = stubCode.locationOf(argsCheckJump);
-        jit->argsCheckPool = NULL;
+    if (chunkIndex == 0 && script->function()) {
+        JS_ASSERT(jit->argsCheckPool == NULL);
+        if (cx->typeInferenceEnabled()) {
+            jit->argsCheckStub = stubCode.locationOf(argsCheckStub);
+            jit->argsCheckFallthrough = stubCode.locationOf(argsCheckFallthrough);
+            jit->argsCheckJump = stubCode.locationOf(argsCheckJump);
+        }
     }
 
     ic::GetGlobalNameIC *getGlobalNames_ = (ic::GetGlobalNameIC *)cursor;
-    jit->nGetGlobalNames = getGlobalNames.length();
-    cursor += sizeof(ic::GetGlobalNameIC) * jit->nGetGlobalNames;
-    for (size_t i = 0; i < jit->nGetGlobalNames; i++) {
+    chunk->nGetGlobalNames = getGlobalNames.length();
+    cursor += sizeof(ic::GetGlobalNameIC) * chunk->nGetGlobalNames;
+    for (size_t i = 0; i < chunk->nGetGlobalNames; i++) {
         ic::GetGlobalNameIC &to = getGlobalNames_[i];
         GetGlobalNameICInfo &from = getGlobalNames[i];
         from.copyTo(to, fullCode, stubCode);
 
         int offset = fullCode.locationOf(from.load) - to.fastPathStart;
         to.loadStoreOffset = offset;
         JS_ASSERT(to.loadStoreOffset == offset);
 
         stubCode.patch(from.addrLabel, &to);
     }
 
     ic::SetGlobalNameIC *setGlobalNames_ = (ic::SetGlobalNameIC *)cursor;
-    jit->nSetGlobalNames = setGlobalNames.length();
-    cursor += sizeof(ic::SetGlobalNameIC) * jit->nSetGlobalNames;
-    for (size_t i = 0; i < jit->nSetGlobalNames; i++) {
+    chunk->nSetGlobalNames = setGlobalNames.length();
+    cursor += sizeof(ic::SetGlobalNameIC) * chunk->nSetGlobalNames;
+    for (size_t i = 0; i < chunk->nSetGlobalNames; i++) {
         ic::SetGlobalNameIC &to = setGlobalNames_[i];
         SetGlobalNameICInfo &from = setGlobalNames[i];
         from.copyTo(to, fullCode, stubCode);
         to.slowPathStart = stubCode.locationOf(from.slowPathStart);
 
         int offset = fullCode.locationOf(from.store).labelAtOffset(0) -
                      to.fastPathStart;
         to.loadStoreOffset = offset;
@@ -1165,19 +1508,19 @@ mjit::Compiler::finishThisUp(JITScript *
                  to.fastPathStart;
         to.fastRejoinOffset = offset;
         JS_ASSERT(to.fastRejoinOffset == offset);
 
         stubCode.patch(from.addrLabel, &to);
     }
 
     ic::CallICInfo *jitCallICs = (ic::CallICInfo *)cursor;
-    jit->nCallICs = callICs.length();
-    cursor += sizeof(ic::CallICInfo) * jit->nCallICs;
-    for (size_t i = 0; i < jit->nCallICs; i++) {
+    chunk->nCallICs = callICs.length();
+    cursor += sizeof(ic::CallICInfo) * chunk->nCallICs;
+    for (size_t i = 0; i < chunk->nCallICs; i++) {
         jitCallICs[i].reset();
         jitCallICs[i].funGuard = fullCode.locationOf(callICs[i].funGuard);
         jitCallICs[i].funJump = fullCode.locationOf(callICs[i].funJump);
         jitCallICs[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart);
         jitCallICs[i].typeMonitored = callICs[i].typeMonitored;
 
         /* Compute the hot call offset. */
         uint32_t offset = fullCode.locationOf(callICs[i].hotJump) -
@@ -1224,19 +1567,19 @@ mjit::Compiler::finishThisUp(JITScript *
         jitCallICs[i].call = &jitCallSites[callICs[i].callIndex];
         jitCallICs[i].frameSize = callICs[i].frameSize;
         jitCallICs[i].funObjReg = callICs[i].funObjReg;
         stubCode.patch(callICs[i].addrLabel1, &jitCallICs[i]);
         stubCode.patch(callICs[i].addrLabel2, &jitCallICs[i]);
     }
 
     ic::EqualityICInfo *jitEqualityICs = (ic::EqualityICInfo *)cursor;
-    jit->nEqualityICs = equalityICs.length();
-    cursor += sizeof(ic::EqualityICInfo) * jit->nEqualityICs;
-    for (size_t i = 0; i < jit->nEqualityICs; i++) {
+    chunk->nEqualityICs = equalityICs.length();
+    cursor += sizeof(ic::EqualityICInfo) * chunk->nEqualityICs;
+    for (size_t i = 0; i < chunk->nEqualityICs; i++) {
         if (equalityICs[i].trampoline) {
             jitEqualityICs[i].target = stubCode.locationOf(equalityICs[i].trampolineStart);
         } else {
             uint32_t offs = uint32_t(equalityICs[i].jumpTarget - script->code);
             JS_ASSERT(jumpMap[offs].isSet());
             jitEqualityICs[i].target = fullCode.locationOf(jumpMap[offs]);
         }
         jitEqualityICs[i].stubEntry = stubCode.locationOf(equalityICs[i].stubEntry);
@@ -1264,19 +1607,19 @@ mjit::Compiler::finishThisUp(JITScript *
         if (patch.hasFastNcode)
             fullCode.patch(patch.fastNcodePatch, joinPoint);
         if (patch.hasSlowNcode)
             stubCode.patch(patch.slowNcodePatch, joinPoint);
     }
 
 #ifdef JS_POLYIC
     ic::GetElementIC *jitGetElems = (ic::GetElementIC *)cursor;
-    jit->nGetElems = getElemICs.length();
-    cursor += sizeof(ic::GetElementIC) * jit->nGetElems;
-    for (size_t i = 0; i < jit->nGetElems; i++) {
+    chunk->nGetElems = getElemICs.length();
+    cursor += sizeof(ic::GetElementIC) * chunk->nGetElems;
+    for (size_t i = 0; i < chunk->nGetElems; i++) {
         ic::GetElementIC &to = jitGetElems[i];
         GetElementICInfo &from = getElemICs[i];
 
         new (&to) ic::GetElementIC();
         from.copyTo(to, fullCode, stubCode);
 
         to.typeReg = from.typeReg;
         to.objReg = from.objReg;
@@ -1292,19 +1635,19 @@ mjit::Compiler::finishThisUp(JITScript *
                                fullCode.locationOf(from.fastPathStart);
         to.inlineShapeGuard = inlineShapeGuard;
         JS_ASSERT(to.inlineShapeGuard == inlineShapeGuard);
 
         stubCode.patch(from.paramAddr, &to);
     }
 
     ic::SetElementIC *jitSetElems = (ic::SetElementIC *)cursor;
-    jit->nSetElems = setElemICs.length();
-    cursor += sizeof(ic::SetElementIC) * jit->nSetElems;
-    for (size_t i = 0; i < jit->nSetElems; i++) {
+    chunk->nSetElems = setElemICs.length();
+    cursor += sizeof(ic::SetElementIC) * chunk->nSetElems;
+    for (size_t i = 0; i < chunk->nSetElems; i++) {
         ic::SetElementIC &to = jitSetElems[i];
         SetElementICInfo &from = setElemICs[i];
 
         new (&to) ic::SetElementIC();
         from.copyTo(to, fullCode, stubCode);
 
         to.strictMode = script->strictModeCode;
         to.vr = from.vr;
@@ -1332,19 +1675,19 @@ mjit::Compiler::finishThisUp(JITScript *
 
         to.volatileMask = from.volatileMask;
         JS_ASSERT(to.volatileMask == from.volatileMask);
 
         stubCode.patch(from.paramAddr, &to);
     }
 
     ic::PICInfo *jitPics = (ic::PICInfo *)cursor;
-    jit->nPICs = pics.length();
-    cursor += sizeof(ic::PICInfo) * jit->nPICs;
-    for (size_t i = 0; i < jit->nPICs; i++) {
+    chunk->nPICs = pics.length();
+    cursor += sizeof(ic::PICInfo) * chunk->nPICs;
+    for (size_t i = 0; i < chunk->nPICs; i++) {
         new (&jitPics[i]) ic::PICInfo();
         pics[i].copyTo(jitPics[i], fullCode, stubCode);
         pics[i].copySimpleMembersTo(jitPics[i]);
 
         jitPics[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) -
                                 masm.distanceOf(pics[i].fastPathStart);
         JS_ASSERT(jitPics[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) -
                                            masm.distanceOf(pics[i].fastPathStart));
@@ -1361,19 +1704,19 @@ mjit::Compiler::finishThisUp(JITScript *
                 JS_ASSERT(distance <= 0);
                 jitPics[i].u.get.typeCheckOffset = distance;
             }
         }
         stubCode.patch(pics[i].paramAddr, &jitPics[i]);
     }
 #endif
 
-    JS_ASSERT(size_t(cursor - (uint8_t*)jit) == dataSize);
+    JS_ASSERT(size_t(cursor - (uint8_t*)chunk) == dataSize);
     /* Pass in NULL here -- we don't want slop bytes to be counted. */
-    JS_ASSERT(jit->scriptDataSize(NULL) == dataSize);
+    JS_ASSERT(chunk->scriptDataSize(NULL) == dataSize);
 
     /* Link fast and slow paths together. */
     stubcc.fixCrossJumps(result, masm.size(), masm.size() + stubcc.size());
 
 #if defined(JS_CPU_MIPS)
     /* Make sure doubleOffset is aligned to sizeof(double) bytes.  */ 
     size_t doubleOffset = (((size_t)result + masm.size() + stubcc.size() +
                             sizeof(double) - 1) & (~(sizeof(double) - 1))) -
@@ -1385,20 +1728,28 @@ mjit::Compiler::finishThisUp(JITScript *
 
     double *inlineDoubles = (double *) (result + doubleOffset);
     double *oolDoubles = (double*) (result + doubleOffset +
                                     masm.numDoubles() * sizeof(double));
 
     /* Generate jump tables. */
     void **jumpVec = (void **)(oolDoubles + stubcc.masm.numDoubles());
 
-    for (size_t i = 0; i < jumpTableOffsets.length(); i++) {
-        uint32_t offset = jumpTableOffsets[i];
-        JS_ASSERT(jumpMap[offset].isSet());
-        jumpVec[i] = (void *)(result + masm.distanceOf(jumpMap[offset]));
+    for (size_t i = 0; i < jumpTableEdges.length(); i++) {
+        JumpTableEdge edge = jumpTableEdges[i];
+        if (bytecodeInChunk(script->code + edge.target)) {
+            JS_ASSERT(jumpMap[edge.target].isSet());
+            jumpVec[i] = (void *)(result + masm.distanceOf(jumpMap[edge.target]));
+        } else {
+            ChunkJumpTableEdge nedge;
+            nedge.edge = edge;
+            nedge.jumpTableEntry = &jumpVec[i];
+            chunkJumps.infallibleAppend(nedge);
+            jumpVec[i] = NULL;
+        }
     }
 
     /* Patch jump table references. */
     for (size_t i = 0; i < jumpTables.length(); i++) {
         JumpTable &jumpTable = jumpTables[i];
         fullCode.patch(jumpTable.label, &jumpVec[jumpTable.offsetIndex]);
     }
 
@@ -1410,17 +1761,82 @@ mjit::Compiler::finishThisUp(JITScript *
     JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size());
 
     Probes::registerMJITCode(cx, jit,
                              a,
                              (JSActiveFrame**) inlineFrames.begin(),
                              result, masm.size(),
                              result + masm.size(), stubcc.size());
 
-    *jitp = jit;
+    outerChunk.chunk = chunk;
+
+    Repatcher repatch(chunk);
+
+    /* Patch all incoming and outgoing cross-chunk jumps. */
+    CrossChunkEdge *crossEdges = jit->edges();
+    for (unsigned i = 0; i < jit->nedges; i++) {
+        CrossChunkEdge &edge = crossEdges[i];
+        if (bytecodeInChunk(outerScript->code + edge.source)) {
+            JS_ASSERT(!edge.sourceJump1 && !edge.sourceJump2);
+            void *label = edge.targetLabel ? edge.targetLabel : edge.shimLabel;
+            CodeLocationLabel targetLabel(label);
+            JSOp op = JSOp(script->code[edge.source]);
+            if (op == JSOP_TABLESWITCH) {
+                if (edge.jumpTableEntries)
+                    cx->free_(edge.jumpTableEntries);
+                CrossChunkEdge::JumpTableEntryVector *jumpTableEntries = NULL;
+                bool failed = false;
+                for (unsigned j = 0; j < chunkJumps.length(); j++) {
+                    ChunkJumpTableEdge nedge = chunkJumps[j];
+                    if (nedge.edge.source == edge.source && nedge.edge.target == edge.target) {
+                        if (!jumpTableEntries) {
+                            jumpTableEntries = cx->new_<CrossChunkEdge::JumpTableEntryVector>();
+                            if (!jumpTableEntries)
+                                failed = true;
+                        }
+                        if (!jumpTableEntries->append(nedge.jumpTableEntry))
+                            failed = true;
+                        *nedge.jumpTableEntry = label;
+                    }
+                }
+                if (failed) {
+                    execPool->release();
+                    cx->free_(chunk);
+                    js_ReportOutOfMemory(cx);
+                    return Compile_Error;
+                }
+                edge.jumpTableEntries = jumpTableEntries;
+            }
+            for (unsigned j = 0; j < chunkEdges.length(); j++) {
+                const OutgoingChunkEdge &oedge = chunkEdges[j];
+                if (oedge.source == edge.source && oedge.target == edge.target) {
+                    /*
+                     * Only a single edge needs to be patched; we ensured while
+                     * generating chunks that no two cross chunk edges can have
+                     * the same source and target. Note that there may not be
+                     * an edge to patch, if constant folding determined the
+                     * jump is never taken.
+                     */
+                    edge.sourceJump1 = fullCode.locationOf(oedge.fastJump).executableAddress();
+                    repatch.relink(CodeLocationJump(edge.sourceJump1), targetLabel);
+                    if (oedge.slowJump.isSet()) {
+                        edge.sourceJump2 =
+                            stubCode.locationOf(oedge.slowJump.get()).executableAddress();
+                        repatch.relink(CodeLocationJump(edge.sourceJump2), targetLabel);
+                    }
+                    break;
+                }
+            }
+        } else if (bytecodeInChunk(outerScript->code + edge.target)) {
+            JS_ASSERT(!edge.targetLabel);
+            JS_ASSERT(jumpMap[edge.target].isSet());
+            edge.targetLabel = fullCode.locationOf(jumpMap[edge.target]).executableAddress();
+            jit->patchEdge(edge, edge.targetLabel);
+        }
+    }
 
     return Compile_Okay;
 }
 
 #ifdef DEBUG
 #define SPEW_OPCODE()                                                         \
     JS_BEGIN_MACRO                                                            \
         if (IsJaegerSpewChannelActive(JSpew_JSOps)) {                         \
@@ -1446,27 +1862,88 @@ mjit::Compiler::finishThisUp(JITScript *
 
 static inline void
 FixDouble(Value &val)
 {
     if (val.isInt32())
         val.setDouble((double)val.toInt32());
 }
 
+inline bool
+mjit::Compiler::shouldStartLoop(jsbytecode *head)
+{
+    /*
+     * Don't do loop based optimizations or register allocation for loops which
+     * span multiple chunks.
+     */
+    if (*head == JSOP_LOOPHEAD && analysis->getLoop(head)) {
+        uint32_t backedge = analysis->getLoop(head)->backedge;
+        if (!bytecodeInChunk(script->code + backedge))
+            return false;
+        return true;
+    }
+    return false;
+}
+
 CompileStatus
 mjit::Compiler::generateMethod()
 {
     SrcNoteLineScanner scanner(script->notes(), script->lineno);
 
     /* For join points, whether there was fallthrough from the previous opcode. */
     bool fallthrough = true;
 
     /* Last bytecode processed. */
     jsbytecode *lastPC = NULL;
 
+    if (!outerJIT())
+        return Compile_Retry;
+
+    uint32_t chunkBegin = 0, chunkEnd = script->length;
+    if (!a->parent) {
+        const ChunkDescriptor &desc =
+            outerJIT()->chunkDescriptor(chunkIndex);
+        chunkBegin = desc.begin;
+        chunkEnd = desc.end;
+
+        while (PC != script->code + chunkBegin) {
+            Bytecode *opinfo = analysis->maybeCode(PC);
+            if (opinfo) {
+                if (opinfo->jumpTarget) {
+                    /* Update variable types for all new values at this bytecode. */
+                    const SlotValue *newv = analysis->newValues(PC);
+                    if (newv) {
+                        while (newv->slot) {
+                            if (newv->slot < TotalSlots(script)) {
+                                VarType &vt = a->varTypes[newv->slot];
+                                vt.setTypes(analysis->getValueTypes(newv->value));
+                            }
+                            newv++;
+                        }
+                    }
+                }
+                if (analyze::BytecodeUpdatesSlot(JSOp(*PC))) {
+                    uint32_t slot = GetBytecodeSlot(script, PC);
+                    if (analysis->trackSlot(slot)) {
+                        VarType &vt = a->varTypes[slot];
+                        vt.setTypes(analysis->pushedTypes(PC, 0));
+                    }
+                }
+            }
+
+            PC += GetBytecodeLength(PC);
+        }
+
+        if (chunkIndex != 0) {
+            uint32_t depth = analysis->getCode(PC).stackDepth;
+            for (uint32_t i = 0; i < depth; i++)
+                frame.pushSynced(JSVAL_TYPE_UNKNOWN);
+        }
+    }
+
     for (;;) {
         JSOp op = JSOp(*PC);
         int trap = stubs::JSTRAP_NONE;
 
         if (script->hasBreakpointsAt(PC))
             trap |= stubs::JSTRAP_TRAP;
 
         Bytecode *opinfo = analysis->maybeCode(PC);
@@ -1476,16 +1953,19 @@ mjit::Compiler::generateMethod()
                 break;
             if (js_CodeSpec[op].length != -1)
                 PC += js_CodeSpec[op].length;
             else
                 PC += js_GetVariableBytecodeLength(PC);
             continue;
         }
 
+        if (PC >= script->code + script->length)
+            break;
+
         scanner.advanceTo(PC - script->code);
         if (script->stepModeEnabled() &&
             (scanner.isLineHeader() || opinfo->jumpTarget))
         {
             trap |= stubs::JSTRAP_SINGLESTEP;
         }
 
         frame.setPC(PC);
@@ -1506,29 +1986,43 @@ mjit::Compiler::generateMethod()
             for (unsigned i = 0; i < fixedDoubleToAnyEntries.length(); i++) {
                 FrameEntry *fe = frame.getSlotEntry(fixedDoubleToAnyEntries[i]);
                 frame.syncAndForgetFe(fe);
             }
         }
         fixedIntToDoubleEntries.clear();
         fixedDoubleToAnyEntries.clear();
 
+        if (PC >= script->code + chunkEnd) {
+            if (fallthrough) {
+                frame.syncAndForgetEverything();
+                jsbytecode *curPC = PC;
+                do {
+                    PC--;
+                } while (!analysis->maybeCode(PC));
+                if (!jumpAndRun(masm.jump(), curPC, NULL, NULL, /* fallthrough = */ true))
+                    return Compile_Error;
+                PC = curPC;
+            }
+            break;
+        }
+
         if (opinfo->jumpTarget || trap) {
             if (fallthrough) {
                 fixDoubleTypes(PC);
                 fixedIntToDoubleEntries.clear();
                 fixedDoubleToAnyEntries.clear();
 
                 /*
                  * Watch for fallthrough to the head of a 'do while' loop.
                  * We don't know what register state we will be using at the head
                  * of the loop so sync, branch, and fix it up after the loop
                  * has been processed.
                  */
-                if (cx->typeInferenceEnabled() && op == JSOP_LOOPHEAD && analysis->getLoop(PC)) {
+                if (cx->typeInferenceEnabled() && shouldStartLoop(PC)) {
                     frame.syncAndForgetEverything();
                     Jump j = masm.jump();
                     if (!startLoop(PC, j, PC))
                         return Compile_Error;
                 } else {
                     Label start = masm.label();
                     if (!frame.syncForBranch(PC, Uses(0)))
                         return Compile_Error;
@@ -1546,31 +2040,43 @@ mjit::Compiler::generateMethod()
                 return Compile_Error;
             updateJoinVarTypes();
             fallthrough = true;
 
             if (!cx->typeInferenceEnabled()) {
                 /* All join points have synced state if we aren't doing cross-branch regalloc. */
                 opinfo->safePoint = true;
             }
-        } else if (opinfo->safePoint && !cx->typeInferenceEnabled()) {
+        } else if (opinfo->safePoint) {
             frame.syncAndForgetEverything();
         }
         frame.assertValidRegisterState();
         a->jumpMap[uint32_t(PC - script->code)] = masm.label();
 
         // Now that we have the PC's register allocation, make sure it gets
         // explicitly updated if this is the loop entry and new loop registers
         // are allocated later on.
         if (loop && !a->parent)
             loop->setOuterPC(PC);
 
         SPEW_OPCODE();
         JS_ASSERT(frame.stackDepth() == opinfo->stackDepth);
 
+        if (op == JSOP_LOOPHEAD && analysis->getLoop(PC)) {
+            jsbytecode *backedge = script->code + analysis->getLoop(PC)->backedge;
+            if (!bytecodeInChunk(backedge)){
+                for (uint32_t slot = ArgSlot(0); slot < TotalSlots(script); slot++) {
+                    if (a->varTypes[slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
+                        FrameEntry *fe = frame.getSlotEntry(slot);
+                        masm.ensureInMemoryDouble(frame.addressOf(fe));
+                    }
+                }
+            }
+        }
+
         // If this is an exception entry point, then jsl_InternalThrow has set
         // VMFrame::fp to the correct fp for the entry point. We need to copy
         // that value here to FpReg so that FpReg also has the correct sp.
         // Otherwise, we would simply be using a stale FpReg value.
         if (op == JSOP_ENTERBLOCK && analysis->getCode(PC).exceptionEntry)
             masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg);
 
         if (trap) {
@@ -1669,18 +2175,20 @@ mjit::Compiler::generateMethod()
             fixDoubleTypes(target);
 
             /*
              * Watch for gotos which are entering a 'for' or 'while' loop.
              * These jump to the loop condition test and are immediately
              * followed by the head of the loop.
              */
             jsbytecode *next = PC + js_CodeSpec[op].length;
-            if (cx->typeInferenceEnabled() && analysis->maybeCode(next) &&
-                JSOp(*next) == JSOP_LOOPHEAD) {
+            if (cx->typeInferenceEnabled() &&
+                analysis->maybeCode(next) &&
+                shouldStartLoop(next))
+            {
                 frame.syncAndForgetEverything();
                 Jump j = masm.jump();
                 if (!startLoop(next, j, target))
                     return Compile_Error;
             } else {
                 if (!frame.syncForBranch(target, Uses(0)))
                     return Compile_Error;
                 Jump j = masm.jump();
@@ -2169,17 +2677,17 @@ mjit::Compiler::generateMethod()
              */
             if (script->pcCounters)
                 updatePCCounters(PC, &codeStart, &countersUpdated);
 #if defined JS_CPU_ARM /* Need to implement jump(BaseIndex) for ARM */
             frame.syncAndKillEverything();
             masm.move(ImmPtr(PC), Registers::ArgReg1);
 
             /* prepareStubCall() is not needed due to syncAndForgetEverything() */
-            INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE);
+            INLINE_STUBCALL(stubs::TableSwitch, REJOIN_JUMP);
             frame.pop();
 
             masm.jump(Registers::ReturnReg);
 #else
             if (!jsop_tableswitch(PC))
                 return Compile_Error;
 #endif
             PC += js_GetVariableBytecodeLength(PC);
@@ -2188,17 +2696,17 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_LOOKUPSWITCH)
             if (script->pcCounters)
                 updatePCCounters(PC, &codeStart, &countersUpdated);
             frame.syncAndForgetEverything();
             masm.move(ImmPtr(PC), Registers::ArgReg1);
 
             /* prepareStubCall() is not needed due to syncAndForgetEverything() */
-            INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_NONE);
+            INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_JUMP);
             frame.pop();
 
             masm.jump(Registers::ReturnReg);
             PC += js_GetVariableBytecodeLength(PC);
             break;
           END_CASE(JSOP_LOOKUPSWITCH)
 
           BEGIN_CASE(JSOP_CASE)
@@ -3608,17 +4116,19 @@ mjit::Compiler::checkCallApplySpeculatio
 bool
 mjit::Compiler::canUseApplyTricks()
 {
     JS_ASSERT(*PC == JSOP_ARGUMENTS);
     jsbytecode *nextpc = PC + JSOP_ARGUMENTS_LENGTH;
     return *nextpc == JSOP_FUNAPPLY &&
            IsLowerableFunCallOrApply(nextpc) &&
            !analysis->jumpTarget(nextpc) &&
-           !debugMode() && !a->parent;
+           !debugMode() &&
+           !a->parent &&
+           bytecodeInChunk(nextpc);
 }
 
 /* See MonoIC.cpp, CallCompiler for more information on call ICs. */
 bool
 mjit::Compiler::inlineCallHelper(uint32_t callImmArgc, bool callingNew, FrameSize &callFrameSize)
 {
     int32_t speculatedArgc;
     if (applyTricks == LazyArgsObj) {
@@ -5713,25 +6223,21 @@ mjit::Compiler::iterNext(ptrdiff_t offse
     notFast = masm.branchTest32(Assembler::NonZero, T3, Imm32(JSITER_FOREACH));
     stubcc.linkExit(notFast, Uses(1));
 
     RegisterID T2 = frame.allocReg();
 
     /* Get cursor. */
     masm.loadPtr(Address(T1, offsetof(NativeIterator, props_cursor)), T2);
 
-    /* Test if the jsid is a string. */
+    /* Get the next string in the iterator. */
     masm.loadPtr(T2, T3);
-    masm.move(T3, T4);
-    masm.andPtr(Imm32(JSID_TYPE_MASK), T4);
-    notFast = masm.branchTestPtr(Assembler::NonZero, T4, T4);
-    stubcc.linkExit(notFast, Uses(1));
 
     /* It's safe to increase the cursor now. */
-    masm.addPtr(Imm32(sizeof(jsid)), T2, T4);
+    masm.addPtr(Imm32(sizeof(JSString*)), T2, T4);
     masm.storePtr(T4, Address(T1, offsetof(NativeIterator, props_cursor)));
 
     frame.freeReg(T4);
     frame.freeReg(T1);
     frame.freeReg(T2);
 
     stubcc.leave();
     stubcc.masm.move(Imm32(offset), Registers::ArgReg1);
@@ -6450,16 +6956,17 @@ mjit::Compiler::jsop_regexp()
     stubcc.rejoin(Changes(1));
     return true;
 }
 
 bool
 mjit::Compiler::startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
 {
     JS_ASSERT(cx->typeInferenceEnabled() && script == outerScript);
+    JS_ASSERT(shouldStartLoop(head));
 
     if (loop) {
         /*
          * Convert all loop registers in the outer loop into unassigned registers.
          * We don't keep track of which registers the inner loop uses, so the only
          * registers that can be carried in the outer loop must be mentioned before
          * the inner loop starts.
          */
@@ -6475,17 +6982,17 @@ mjit::Compiler::startLoop(jsbytecode *he
     frame.setLoop(loop);
 
     return true;
 }
 
 bool
 mjit::Compiler::finishLoop(jsbytecode *head)
 {
-    if (!cx->typeInferenceEnabled())
+    if (!cx->typeInferenceEnabled() || !bytecodeInChunk(head))
         return true;
 
     /*
      * We're done processing the current loop. Every loop has exactly one backedge
      * at the end ('continue' statements are forward jumps to the loop test),
      * and after jumpAndRun'ing on that edge we can pop it from the frame.
      */
     JS_ASSERT(loop && loop->headOffset() == uint32_t(head - script->code));
@@ -6556,17 +7063,17 @@ mjit::Compiler::finishLoop(jsbytecode *h
         /*
          * The interpreter may store integers in slots we assume are doubles,
          * make sure state is consistent before joining. Note that we don't
          * need any handling for other safe points the interpreter can enter
          * from, i.e. from switch and try blocks, as we don't assume double
          * variables are coherent in such cases.
          */
         for (uint32_t slot = ArgSlot(0); slot < TotalSlots(script); slot++) {
-            if (a->varTypes[slot].type == JSVAL_TYPE_DOUBLE) {
+            if (a->varTypes[slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
                 FrameEntry *fe = frame.getSlotEntry(slot);
                 stubcc.masm.ensureInMemoryDouble(frame.addressOf(fe));
             }
         }
 
         frame.prepareForJump(head, stubcc.masm, true);
         if (!stubcc.jumpInScript(stubcc.masm.jump(), head))
             return false;
@@ -6598,23 +7105,47 @@ mjit::Compiler::finishLoop(jsbytecode *h
  * The state at the fast jump must reflect the frame's current state. If specified
  * the state at the slow jump must be fully synced.
  *
  * The 'trampoline' argument indicates whether a trampoline was emitted into
  * the OOL path loading some registers for the target. If this is the case,
  * the fast path jump was redirected to the stub code's initial label, and the
  * same must happen for any other fast paths for the target (i.e. paths from
  * inline caches).
+ *
+ * The 'fallthrough' argument indicates this is a jump emitted for a fallthrough
+ * at the end of the compiled chunk. In this case the opcode may not be a
+ * JOF_JUMP opcode, and the compiler should not watch for fusions.
  */
 bool
-mjit::Compiler::jumpAndRun(Jump j, jsbytecode *target, Jump *slow, bool *trampoline)
+mjit::Compiler::jumpAndRun(Jump j, jsbytecode *target, Jump *slow, bool *trampoline,
+                           bool fallthrough)
 {
     if (trampoline)
         *trampoline = false;
 
+    if (!a->parent && !bytecodeInChunk(target)) {
+        /*
+         * syncForBranch() must have ensured the stack is synced. Figure out
+         * the source of the jump, which may be the opcode after PC if two ops
+         * were fused for a branch.
+         */
+        OutgoingChunkEdge edge;
+        edge.source = PC - outerScript->code;
+        JSOp op = JSOp(*PC);
+        if (!fallthrough && !(js_CodeSpec[op].format & JOF_JUMP) && op != JSOP_TABLESWITCH)
+            edge.source += GetBytecodeLength(PC);
+        edge.target = target - outerScript->code;
+        edge.fastJump = j;
+        if (slow)
+            edge.slowJump = *slow;
+        chunkEdges.append(edge);
+        return true;
+    }
+
     /*
      * Unless we are coming from a branch which synced everything, syncForBranch
      * must have been called and ensured an allocation at the target.
      */
     RegisterAllocation *lvtarget = NULL;
     bool consistent = true;
     if (cx->typeInferenceEnabled()) {
         RegisterAllocation *&alloc = analysis->getAllocation(target);
@@ -6799,45 +7330,36 @@ mjit::Compiler::constructThis()
 bool
 mjit::Compiler::jsop_tableswitch(jsbytecode *pc)
 {
 #if defined JS_CPU_ARM
     JS_NOT_REACHED("Implement jump(BaseIndex) for ARM");
     return true;
 #else
     jsbytecode *originalPC = pc;
-    JSOp op = JSOp(*originalPC);
+    DebugOnly<JSOp> op = JSOp(*originalPC);
     JS_ASSERT(op == JSOP_TABLESWITCH);
 
     uint32_t defaultTarget = GET_JUMP_OFFSET(pc);
     pc += JUMP_OFFSET_LEN;
 
     jsint low = GET_JUMP_OFFSET(pc);
     pc += JUMP_OFFSET_LEN;
     jsint high = GET_JUMP_OFFSET(pc);
     pc += JUMP_OFFSET_LEN;
     int numJumps = high + 1 - low;
     JS_ASSERT(numJumps >= 0);
 
-    /*
-     * If there are no cases, this is a no-op. The default case immediately
-     * follows in the bytecode and is always taken.
-     */
-    if (numJumps == 0) {
-        frame.pop();
-        return true;
-    }
-
     FrameEntry *fe = frame.peek(-1);
     if (fe->isNotType(JSVAL_TYPE_INT32) || numJumps > 256) {
         frame.syncAndForgetEverything();
         masm.move(ImmPtr(originalPC), Registers::ArgReg1);
 
         /* prepareStubCall() is not needed due to forgetEverything() */
-        INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE);
+        INLINE_STUBCALL(stubs::TableSwitch, REJOIN_JUMP);
         frame.pop();
         masm.jump(Registers::ReturnReg);
         return true;
     }
 
     RegisterID dataReg;
     if (fe->isConstant()) {
         JS_ASSERT(fe->isType(JSVAL_TYPE_INT32));
@@ -6850,39 +7372,41 @@ mjit::Compiler::jsop_tableswitch(jsbytec
     RegisterID reg = frame.allocReg();
     frame.syncAndForgetEverything();
 
     MaybeJump notInt;
     if (!fe->isType(JSVAL_TYPE_INT32))
         notInt = masm.testInt32(Assembler::NotEqual, frame.addressOf(fe));
 
     JumpTable jt;
-    jt.offsetIndex = jumpTableOffsets.length();
+    jt.offsetIndex = jumpTableEdges.length();
     jt.label = masm.moveWithPatch(ImmPtr(NULL), reg);
     jumpTables.append(jt);
 
     for (int i = 0; i < numJumps; i++) {
         uint32_t target = GET_JUMP_OFFSET(pc);
         if (!target)
             target = defaultTarget;
-        uint32_t offset = (originalPC + target) - script->code;
-        jumpTableOffsets.append(offset);
+        JumpTableEdge edge;
+        edge.source = originalPC - script->code;
+        edge.target = (originalPC + target) - script->code;
+        jumpTableEdges.append(edge);
         pc += JUMP_OFFSET_LEN;
     }
     if (low != 0)
         masm.sub32(Imm32(low), dataReg);
     Jump defaultCase = masm.branch32(Assembler::AboveOrEqual, dataReg, Imm32(numJumps));
     BaseIndex jumpTarget(reg, dataReg, Assembler::ScalePtr);
     masm.jump(jumpTarget);
 
     if (notInt.isSet()) {
         stubcc.linkExitDirect(notInt.get(), stubcc.masm.label());
         stubcc.leave();
         stubcc.masm.move(ImmPtr(originalPC), Registers::ArgReg1);
-        OOL_STUBCALL(stubs::TableSwitch, REJOIN_NONE);
+        OOL_STUBCALL(stubs::TableSwitch, REJOIN_JUMP);
         stubcc.masm.jump(Registers::ReturnReg);
     }
     frame.pop();
     return jumpAndRun(defaultCase, originalPC + defaultTarget);
 #endif
 }
 
 void
@@ -6953,33 +7477,34 @@ mjit::Compiler::fixDoubleTypes(jsbytecod
                 !analysis->trackSlot(newv->slot)) {
                 newv++;
                 continue;
             }
             JS_ASSERT(newv->slot < TotalSlots(script));
             types::TypeSet *targetTypes = analysis->getValueTypes(newv->value);
             FrameEntry *fe = frame.getSlotEntry(newv->slot);
             VarType &vt = a->varTypes[newv->slot];
+            JSValueType type = vt.getTypeTag(cx);
             if (targetTypes->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
-                if (vt.type == JSVAL_TYPE_INT32) {
+                if (type == JSVAL_TYPE_INT32) {
                     fixedIntToDoubleEntries.append(newv->slot);
                     frame.ensureDouble(fe);
                     frame.forgetLoopReg(fe);
-                } else if (vt.type == JSVAL_TYPE_UNKNOWN) {
+                } else if (type == JSVAL_TYPE_UNKNOWN) {
                     /*
                      * Unknown here but a double at the target. The type
                      * 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);
+                    JS_ASSERT(type == JSVAL_TYPE_DOUBLE);
                 }
-            } else if (vt.type == JSVAL_TYPE_DOUBLE) {
+            } else if (type == JSVAL_TYPE_DOUBLE) {
                 fixedDoubleToAnyEntries.append(newv->slot);
                 frame.syncAndForgetFe(fe);
                 frame.forgetLoopReg(fe);
             }
             newv++;
         }
     }
 }
@@ -7007,48 +7532,51 @@ mjit::Compiler::updateVarType()
      * (see prepareInferenceTypes).
      */
 
     types::TypeSet *types = pushedTypeSet(0);
     uint32_t slot = GetBytecodeSlot(script, PC);
 
     if (analysis->trackSlot(slot)) {
         VarType &vt = a->varTypes[slot];
-        vt.types = types;
-        vt.type = types->getKnownTypeTag(cx);
+        vt.setTypes(types);
 
         /*
          * Variables whose type has been inferred as a double need to be
          * maintained by the frame as a double. We might forget the exact
          * representation used by the next call to fixDoubleTypes, fix it now.
          */
-        if (vt.type == JSVAL_TYPE_DOUBLE)
+        if (vt.getTypeTag(cx) == JSVAL_TYPE_DOUBLE)
             frame.ensureDouble(frame.getSlotEntry(slot));
     }
 }
 
 void
 mjit::Compiler::updateJoinVarTypes()
 {
     if (!cx->typeInferenceEnabled())
         return;
 
     /* Update variable types for all new values at this bytecode. */
     const SlotValue *newv = analysis->newValues(PC);
     if (newv) {
         while (newv->slot) {
             if (newv->slot < TotalSlots(script)) {
                 VarType &vt = a->varTypes[newv->slot];
-                vt.types = analysis->getValueTypes(newv->value);
-                JSValueType newType = vt.types->getKnownTypeTag(cx);
-                if (newType != vt.type) {
+                JSValueType type = vt.getTypeTag(cx);
+                vt.setTypes(analysis->getValueTypes(newv->value));
+                if (vt.getTypeTag(cx) != type) {
+                    /*
+                     * If the known type of a variable changes (even if the
+                     * variable itself has not been reassigned) then we can't
+                     * carry a loop register for the var.
+                     */
                     FrameEntry *fe = frame.getSlotEntry(newv->slot);
                     frame.forgetLoopReg(fe);
                 }
-                vt.type = newType;
             }
             newv++;
         }
     }
 }
 
 void
 mjit::Compiler::restoreVarType()
@@ -7061,17 +7589,17 @@ mjit::Compiler::restoreVarType()
     if (slot >= analyze::TotalSlots(script))
         return;
 
     /*
      * Restore the known type of a live local or argument. We ensure that types
      * of tracked variables match their inferred type (as tracked in varTypes),
      * but may have forgotten it due to a branch or syncAndForgetEverything.
      */
-    JSValueType type = a->varTypes[slot].type;
+    JSValueType type = a->varTypes[slot].getTypeTag(cx);
     if (type != JSVAL_TYPE_UNKNOWN &&
         (type != JSVAL_TYPE_DOUBLE || analysis->trackSlot(slot))) {
         FrameEntry *fe = frame.getSlotEntry(slot);
         JS_ASSERT_IF(fe->isTypeKnown(), fe->isType(type));
         if (!fe->isTypeKnown())
             frame.learnType(fe, type, false);
     }
 }
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -332,35 +332,74 @@ class Compiler : public BaseCompiler
         bool ool;
     };
 
     struct JumpTable {
         DataLabelPtr label;
         size_t offsetIndex;
     };
 
+    struct JumpTableEdge {
+        uint32_t source;
+        uint32_t target;
+    };
+
+    struct ChunkJumpTableEdge {
+        JumpTableEdge edge;
+        void **jumpTableEntry;
+    };
+
     struct LoopEntry {
         uint32_t pcOffset;
         Label label;
     };
 
-    struct VarType {
+    /*
+     * Information about the current type of an argument or local in the
+     * script. The known type tag of these types is cached when possible to
+     * avoid generating duplicate dependency constraints.
+     */
+    class VarType {
         JSValueType type;
         types::TypeSet *types;
+
+      public:
+        void setTypes(types::TypeSet *types) {
+            this->types = types;
+            this->type = JSVAL_TYPE_MISSING;
+        }
+
+        types::TypeSet *getTypes() { return types; }
+
+        JSValueType getTypeTag(JSContext *cx) {
+            if (type == JSVAL_TYPE_MISSING)
+                type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
+            return type;
+        }
+    };
+
+    struct OutgoingChunkEdge {
+        uint32_t source;
+        uint32_t target;
+
+        Jump fastJump;
+        MaybeJump slowJump;
     };
 
     struct SlotType
     {
         uint32_t slot;
         VarType vt;
         SlotType(uint32_t slot, VarType vt) : slot(slot), vt(vt) {}
     };
 
     JSScript *outerScript;
+    unsigned chunkIndex;
     bool isConstructing;
+    ChunkDescriptor &outerChunk;
 
     /* SSA information for the outer script and all frames we will be inlining. */
     analyze::CrossScriptSSA ssa;
 
     GlobalObject *globalObj;
     const HeapValue *globalSlots;  /* Original slots pointer. */
 
     Assembler masm;
@@ -423,18 +462,19 @@ private:
     js::Vector<SetElementICInfo, 16, CompilerAllocPolicy> setElemICs;
 #endif
     js::Vector<CallPatchInfo, 64, CompilerAllocPolicy> callPatches;
     js::Vector<InternalCallSite, 64, CompilerAllocPolicy> callSites;
     js::Vector<DoublePatch, 16, CompilerAllocPolicy> doubleList;
     js::Vector<uint32_t> fixedIntToDoubleEntries;
     js::Vector<uint32_t> fixedDoubleToAnyEntries;
     js::Vector<JumpTable, 16> jumpTables;
-    js::Vector<uint32_t, 16> jumpTableOffsets;
+    js::Vector<JumpTableEdge, 16> jumpTableEdges;
     js::Vector<LoopEntry, 16> loopEntries;
+    js::Vector<OutgoingChunkEdge, 16> chunkEdges;
     StubCompiler stubcc;
     Label invokeLabel;
     Label arityLabel;
     Label argsCheckLabel;
 #ifdef JS_MONOIC
     Label argsCheckStub;
     Label argsCheckFallthrough;
     Jump argsCheckJump;
@@ -447,17 +487,17 @@ private:
     uint32_t gcNumber;
     enum { NoApplyTricks, LazyArgsObj } applyTricks;
     PCLengthEntry *pcLengths;
 
     Compiler *thisFromCtor() { return this; }
 
     friend class CompilerAllocPolicy;
   public:
-    Compiler(JSContext *cx, JSScript *outerScript, bool isConstructing);
+    Compiler(JSContext *cx, JSScript *outerScript, unsigned chunkIndex, bool isConstructing);
     ~Compiler();
 
     CompileStatus compile();
 
     Label getLabel() { return masm.label(); }
     bool knownJump(jsbytecode *pc);
     Label labelOf(jsbytecode *target, uint32_t inlineIndex);
     void addCallSite(const InternalCallSite &callSite);
@@ -472,16 +512,25 @@ private:
         if (a == outer)
             return PC;
         ActiveFrame *scan = a;
         while (scan && scan->parent != outer)
             scan = static_cast<ActiveFrame *>(scan->parent);
         return scan->parentPC;
     }
 
+    JITScript *outerJIT() {
+        return outerScript->getJIT(isConstructing);
+    }
+
+    bool bytecodeInChunk(jsbytecode *pc) {
+        return (unsigned(pc - outerScript->code) >= outerChunk.begin)
+            && (unsigned(pc - outerScript->code) < outerChunk.end);
+    }
+
     jsbytecode *inlinePC() { return PC; }
     uint32_t inlineIndex() { return a->inlineIndex; }
 
     Assembler &getAssembler(bool ool) { return ool ? stubcc.masm : masm; }
 
     InvariantCodePatch *getInvariantPatch(unsigned index) {
         return &callSites[index].loopPatch;
     }
@@ -495,21 +544,21 @@ private:
             if (na->exitState)
                 return true;
             na = static_cast<ActiveFrame *>(na->parent);
         }
         return false;
     }
 
   private:
-    CompileStatus performCompilation(JITScript **jitp);
+    CompileStatus performCompilation();
     CompileStatus generatePrologue();
     CompileStatus generateMethod();
     CompileStatus generateEpilogue();
-    CompileStatus finishThisUp(JITScript **jitp);
+    CompileStatus finishThisUp();
     CompileStatus pushActiveFrame(JSScript *script, uint32_t argc);
     void popActiveFrame();
     void updatePCCounters(jsbytecode *pc, Label *start, bool *updated);
     void updatePCTypes(jsbytecode *pc, FrameEntry *fe);
     void updateArithCounters(jsbytecode *pc, FrameEntry *fe,
                              JSValueType firstUseType, JSValueType secondUseType);
     void updateElemCounters(jsbytecode *pc, FrameEntry *obj, FrameEntry *id);
     void bumpPropCounter(jsbytecode *pc, int counter);
@@ -586,19 +635,22 @@ private:
 
     /*
      * Try to convert a double fe to an integer, with no truncation performed,
      * or jump to the slow path per uses.
      */
     void tryConvertInteger(FrameEntry *fe, Uses uses);
 
     /* Opcode handlers. */
-    bool jumpAndRun(Jump j, jsbytecode *target, Jump *slow = NULL, bool *trampoline = NULL);
+    bool jumpAndRun(Jump j, jsbytecode *target,
+                    Jump *slow = NULL, bool *trampoline = NULL,
+                    bool fallthrough = false);
     bool startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget);
     bool finishLoop(jsbytecode *head);
+    inline bool shouldStartLoop(jsbytecode *head);
     void jsop_bindname(PropertyName *name);
     void jsop_setglobal(uint32_t index);
     void jsop_getprop_slow(PropertyName *name, bool forPrototype = false);
     void jsop_getarg(uint32_t slot);
     void jsop_setarg(uint32_t slot, bool popped);
     void jsop_this();
     void emitReturn(FrameEntry *fe);
     void emitFinalReturn(Assembler &masm);
--- a/js/src/methodjit/FastArithmetic.cpp
+++ b/js/src/methodjit/FastArithmetic.cpp
@@ -1169,17 +1169,17 @@ mjit::Compiler::jsop_equality_int_string
 
         ic.cond = cond;
         ic.tempReg = tempReg;
         ic.lvr = lvr;
         ic.rvr = rvr;
         ic.stubEntry = stubEntry;
         ic.stub = stub;
 
-        bool useIC = !a->parent;
+        bool useIC = !a->parent && bytecodeInChunk(target);
 
         /* Call the IC stub, which may generate a fast path. */
         if (useIC) {
             /* Adjust for the two values just pushed. */
             ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
             ic.stubCall = OOL_STUBCALL_LOCAL_SLOTS(ic::Equality, REJOIN_BRANCH,
                                                    frame.totalDepth() + 2);
             needStub = false;
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -763,19 +763,20 @@ mjit::Compiler::jsop_typeof()
                 type = JSVAL_TYPE_BOOLEAN;
             } else if (atom == rt->atomState.typeAtoms[JSTYPE_NUMBER]) {
                 type = JSVAL_TYPE_INT32;
 
                 /* JSVAL_TYPE_DOUBLE is 0x0 and JSVAL_TYPE_INT32 is 0x1, use <= or > to match both */
                 cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above;
             }
 
-            if (type != JSVAL_TYPE_UNKNOWN) {
-                PC += JSOP_STRING_LENGTH;;
-                PC += JSOP_EQ_LENGTH;
+            jsbytecode *afterPC = PC + JSOP_STRING_LENGTH + JSOP_EQ_LENGTH;
+
+            if (type != JSVAL_TYPE_UNKNOWN && bytecodeInChunk(afterPC)) {
+                PC = afterPC;
 
                 RegisterID result = frame.allocReg(Registers::SingleByteRegs).reg();
 
 #if defined JS_NUNBOX32
                 if (frame.shouldAvoidTypeRemat(fe))
                     masm.set32(cond, masm.tagOf(frame.addressOf(fe)), ImmType(type), result);
                 else
                     masm.set32(cond, frame.tempRegForType(fe), ImmType(type), result);
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -1356,16 +1356,18 @@ FrameState::pushLocal(uint32_t n)
         /*
          * We really want to assert on local variables, but in the presence of
          * SETLOCAL equivocation of stack slots, and let expressions, just
          * weakly assert on the fixed local vars.
          */
         if (fe->isTracked() && n < a->script->nfixed)
             JS_ASSERT(fe->data.inMemory());
 #endif
+        if (n >= a->script->nfixed)
+            syncFe(fe);
         JSValueType type = fe->isTypeKnown() ? fe->getKnownType() : JSVAL_TYPE_UNKNOWN;
         push(addressOf(fe), type);
     }
 }
 
 inline void
 FrameState::pushArg(uint32_t n)
 {
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -350,18 +350,17 @@ FrameState::bestEvictReg(uint32_t mask, 
             JaegerSpew(JSpew_Regalloc, "    %s is 'this' in a constructor\n", reg.name());
             continue;
         }
 
         /*
          * Evict variables which are only live in future loop iterations, and are
          * not carried around the loop in a register.
          */
-        JS_ASSERT_IF(lifetime->loopTail, loop);
-        if (lifetime->loopTail && !loop->carriesLoopReg(fe)) {
+        if (lifetime->loopTail && (!loop || !loop->carriesLoopReg(fe))) {
             JaegerSpew(JSpew_Regalloc, "result: %s (%s) only live in later iterations\n",
                        entryName(fe), reg.name());
             return reg;
         }
 
         JaegerSpew(JSpew_Regalloc, "    %s (%s): %u\n", entryName(fe), reg.name(), lifetime->end);
 
         /*
@@ -574,19 +573,22 @@ FrameState::dumpAllocation(RegisterAlloc
 RegisterAllocation *
 FrameState::computeAllocation(jsbytecode *target)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     RegisterAllocation *alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(false);
     if (!alloc)
         return NULL;
 
-    if (a->analysis->getCode(target).exceptionEntry || a->analysis->getCode(target).switchTarget ||
-        a->script->hasBreakpointsAt(target)) {
-        /* State must be synced at exception and switch targets, and at traps. */
+    /*
+     * State must be synced at exception and switch targets, at traps and when
+     * crossing between compilation chunks.
+     */
+    if (a->analysis->getCode(target).safePoint ||
+        (!a->parent && !cc.bytecodeInChunk(target))) {
 #ifdef DEBUG
         if (IsJaegerSpewChannelActive(JSpew_Regalloc)) {
             JaegerSpew(JSpew_Regalloc, "allocation at %u:", unsigned(target - a->script->code));
             dumpAllocation(alloc);
         }
 #endif
         return alloc;
     }
--- a/js/src/methodjit/ICRepatcher.h
+++ b/js/src/methodjit/ICRepatcher.h
@@ -55,17 +55,17 @@ class Repatcher : public JSC::RepatchBuf
 {
     typedef JSC::CodeLocationLabel  CodeLocationLabel;
     typedef JSC::CodeLocationCall   CodeLocationCall;
     typedef JSC::FunctionPtr        FunctionPtr;
 
     CodeLocationLabel label;
 
   public:
-    explicit Repatcher(JITScript *js)
+    explicit Repatcher(JITChunk *js)
       : JSC::RepatchBuffer(js->code), label(js->code.m_code.executableAddress())
     { }
 
     explicit Repatcher(const JSC::JITCode &code)
       : JSC::RepatchBuffer(code), label(code.start())
     { }
 
     using JSC::RepatchBuffer::relink;
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -59,17 +59,16 @@
 
 #include "jsinterpinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 #include "jsobjinlines.h"
 #include "jscntxtinlines.h"
 #include "jsatominlines.h"
 #include "StubCalls-inl.h"
-#include "MethodJIT-inl.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
 using namespace js::mjit;
 using namespace JSC;
 
 using ic::Repatcher;
@@ -316,25 +315,23 @@ UncachedInlineCall(VMFrame &f, InitialFr
     bool construct = InitialFrameFlagsAreConstructing(initial);
 
     bool newType = construct && cx->typeInferenceEnabled() &&
         types::UseNewType(cx, f.script(), f.pc());
 
     types::TypeMonitorCall(cx, args, construct);
 
     /* Try to compile if not already compiled. */
-    if (newscript->getJITStatus(construct) == JITScript_None) {
-        CompileStatus status = CanMethodJIT(cx, newscript, construct, CompileRequest_Interpreter);
-        if (status == Compile_Error) {
-            /* A runtime exception was thrown, get out. */
-            return false;
-        }
-        if (status == Compile_Abort)
-            *unjittable = true;
+    CompileStatus status = CanMethodJIT(cx, newscript, newscript->code, construct, CompileRequest_Interpreter);
+    if (status == Compile_Error) {
+        /* A runtime exception was thrown, get out. */
+        return false;
     }
+    if (status == Compile_Abort)
+        *unjittable = true;
 
     /*
      * Make sure we are not calling from an inline frame if we need to make a
      * call object for the callee, as doing so could trigger GC and cause
      * jitcode discarding / frame expansion.
      */
     if (f.regs.inlined() && newfun->isHeavyweight()) {
         ExpandInlineFrames(cx->compartment);
@@ -362,21 +359,23 @@ UncachedInlineCall(VMFrame &f, InitialFr
         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())) {
-            *pret = jit->invokeEntry;
+            if (jit->invokeEntry) {
+                *pret = jit->invokeEntry;
 
-            /* Restore the old fp around and let the JIT code repush the new fp. */
-            regs.popFrame((Value *) regs.fp());
-            return true;
+                /* Restore the old fp around and let the JIT code repush the new fp. */
+                regs.popFrame((Value *) regs.fp());
+                return true;
+            }
         }
     }
 
     /*
      * Otherwise, run newscript in the interpreter. Expand any inlined frame we
      * are calling from, as the new frame is not associated with the VMFrame
      * and will not have its prevpc info updated if frame expansion is
      * triggered while interpreting.
@@ -587,21 +586,21 @@ js_InternalThrow(VMFrame &f)
         ScriptEpilogue(f.cx, f.fp(), false);
 
         // Don't remove the last frame, this is the responsibility of
         // JaegerShot()'s caller. We only guarantee that ScriptEpilogue()
         // has been run.
         if (f.entryfp == f.fp())
             break;
 
-        JS_ASSERT(f.regs.sp == cx->regs().sp);
+        JS_ASSERT(&cx->regs() == &f.regs);
         InlineReturn(f);
     }
 
-    JS_ASSERT(f.regs.sp == cx->regs().sp);
+    JS_ASSERT(&cx->regs() == &f.regs);
 
     if (!pc)
         return NULL;
 
     StackFrame *fp = cx->fp();
     JSScript *script = fp->script();
 
     /*
@@ -615,19 +614,16 @@ js_InternalThrow(VMFrame &f)
 
     if (!script->ensureRanAnalysis(cx, NULL)) {
         js_ReportOutOfMemory(cx);
         return NULL;
     }
 
     analyze::AutoEnterAnalysis enter(cx);
 
-    cx->regs().pc = pc;
-    cx->regs().sp = fp->base() + script->analysis()->getCode(pc).stackDepth;
-
     /*
      * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go
      * back into the interpreter with a pending exception. This will cause
      * it to immediately rethrow.
      */
     if (cx->isExceptionPending()) {
         JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK);
         StaticBlockObject &blockObj = script->getObject(GET_SLOTNO(pc))->asStaticBlock();
@@ -693,16 +689,38 @@ stubs::ScriptProbeOnlyPrologue(VMFrame &
 }
 
 void JS_FASTCALL
 stubs::ScriptProbeOnlyEpilogue(VMFrame &f)
 {
     Probes::exitJSFun(f.cx, f.fp()->fun(), f.fp()->script());
 }
 
+void JS_FASTCALL
+stubs::CrossChunkShim(VMFrame &f, void *edge_)
+{
+    CrossChunkEdge *edge = (CrossChunkEdge *) edge_;
+
+    mjit::ExpandInlineFrames(f.cx->compartment);
+
+    JSScript *script = f.script();
+    JS_ASSERT(edge->target < script->length);
+    JS_ASSERT(script->code + edge->target == f.pc());
+
+    CompileStatus status = CanMethodJIT(f.cx, script, f.pc(), f.fp()->isConstructing(),
+                                        CompileRequest_Interpreter);
+    if (status == Compile_Error)
+        THROW();
+
+    void **addr = f.returnAddressLocation();
+    *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
+
+    f.fp()->setRejoin(StubRejoin(REJOIN_RESUME));
+}
+
 JS_STATIC_ASSERT(JSOP_NOP == 0);
 
 /* :XXX: common out with identical copy in Compiler.cpp */
 #if defined(JS_METHODJIT_SPEW)
 static const char *OpcodeNames[] = {
 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) #name,
 # include "jsopcode.tbl"
 # undef OPDEF
@@ -849,16 +867,21 @@ js_InternalInterpret(void *returnData, v
         if (script->hasBreakpointsAt(pc))
             skipTrap = true;
         break;
 
       case REJOIN_FALLTHROUGH:
         f.regs.pc = nextpc;
         break;
 
+      case REJOIN_JUMP:
+        f.regs.pc = (jsbytecode *) returnReg;
+        JS_ASSERT(unsigned(f.regs.pc - script->code) < script->length);
+        break;
+
       case REJOIN_NATIVE:
       case REJOIN_NATIVE_LOWERED:
       case REJOIN_NATIVE_GETTER: {
         /*
          * We don't rejoin until after the native stub finishes execution, in
          * which case the return value will be in memory. For lowered natives,
          * the return value will be in the 'this' value's slot.
          */
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1097,16 +1097,17 @@ mjit::EnterMethodJIT(JSContext *cx, Stac
 }
 
 static inline JaegerStatus
 CheckStackAndEnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, bool partial)
 {
     JS_CHECK_RECURSION(cx, return Jaeger_Throwing);
 
     JS_ASSERT(!cx->compartment->activeAnalysis);
+    JS_ASSERT(code);
 
     Value *stackLimit = cx->stack.space().getStackLimit(cx, REPORT_ERROR);
     if (!stackLimit)
         return Jaeger_Throwing;
 
     return EnterMethodJIT(cx, fp, code, stackLimit, partial);
 }
 
@@ -1124,117 +1125,136 @@ mjit::JaegerShot(JSContext *cx, bool par
 
 JaegerStatus
 js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint, bool partial)
 {
     return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint, partial);
 }
 
 NativeMapEntry *
-JITScript::nmap() const
+JITChunk::nmap() const
 {
-    return (NativeMapEntry *)((char*)this + sizeof(JITScript));
+    return (NativeMapEntry *)((char*)this + sizeof(*this));
 }
 
 js::mjit::InlineFrame *
-JITScript::inlineFrames() const
+JITChunk::inlineFrames() const
 {
     return (js::mjit::InlineFrame *)((char *)nmap() + sizeof(NativeMapEntry) * nNmapPairs);
 }
 
 js::mjit::CallSite *
-JITScript::callSites() const
+JITChunk::callSites() const
 {
     return (js::mjit::CallSite *)&inlineFrames()[nInlineFrames];
 }
 
 char *
-JITScript::commonSectionLimit() const
+JITChunk::commonSectionLimit() const
 {
     return (char *)&callSites()[nCallSites];
 }
 
 #ifdef JS_MONOIC
 ic::GetGlobalNameIC *
-JITScript::getGlobalNames() const
+JITChunk::getGlobalNames() const
 {
     return (ic::GetGlobalNameIC *) commonSectionLimit();
 }
 
 ic::SetGlobalNameIC *
-JITScript::setGlobalNames() const
+JITChunk::setGlobalNames() const
 {
     return (ic::SetGlobalNameIC *)((char *)getGlobalNames() +
             sizeof(ic::GetGlobalNameIC) * nGetGlobalNames);
 }
 
 ic::CallICInfo *
-JITScript::callICs() const
+JITChunk::callICs() const
 {
     return (ic::CallICInfo *)&setGlobalNames()[nSetGlobalNames];
 }
 
 ic::EqualityICInfo *
-JITScript::equalityICs() const
+JITChunk::equalityICs() const
 {
     return (ic::EqualityICInfo *)&callICs()[nCallICs];
 }
 
 char *
-JITScript::monoICSectionsLimit() const
+JITChunk::monoICSectionsLimit() const
 {
     return (char *)&equalityICs()[nEqualityICs];
 }
 #else   // JS_MONOIC
 char *
-JITScript::monoICSectionsLimit() const
+JITChunk::monoICSectionsLimit() const
 {
     return commonSectionLimit();
 }
 #endif  // JS_MONOIC
 
 #ifdef JS_POLYIC
 ic::GetElementIC *
-JITScript::getElems() const
+JITChunk::getElems() const
 {
     return (ic::GetElementIC *)monoICSectionsLimit();
 }
 
 ic::SetElementIC *
-JITScript::setElems() const
+JITChunk::setElems() const
 {
     return (ic::SetElementIC *)((char *)getElems() + sizeof(ic::GetElementIC) * nGetElems);
 }
 
 ic::PICInfo *
-JITScript::pics() const
+JITChunk::pics() const
 {
     return (ic::PICInfo *)((char *)setElems() + sizeof(ic::SetElementIC) * nSetElems);
 }
 
 char *
-JITScript::polyICSectionsLimit() const
+JITChunk::polyICSectionsLimit() const
 {
     return (char *)pics() + sizeof(ic::PICInfo) * nPICs;
 }
 #else   // JS_POLYIC
 char *
-JITScript::polyICSectionsLimit() const
+JITChunk::polyICSectionsLimit() const
 {
     return monoICSectionsLimit();
 }
 #endif  // JS_POLYIC
 
+void
+JITScript::patchEdge(const CrossChunkEdge &edge, void *label)
+{
+    if (edge.sourceJump1 || edge.sourceJump2) {
+        JITChunk *sourceChunk = chunk(script->code + edge.source);
+        JSC::CodeLocationLabel targetLabel(label);
+        ic::Repatcher repatch(sourceChunk);
+
+        if (edge.sourceJump1)
+            repatch.relink(JSC::CodeLocationJump(edge.sourceJump1), targetLabel);
+        if (edge.sourceJump2)
+            repatch.relink(JSC::CodeLocationJump(edge.sourceJump2), targetLabel);
+    }
+    if (edge.jumpTableEntries) {
+        for (unsigned i = 0; i < edge.jumpTableEntries->length(); i++)
+            *(*edge.jumpTableEntries)[i] = label;
+    }
+}
+
 template <typename T>
 static inline void Destroy(T &t)
 {
     t.~T();
 }
 
-mjit::JITScript::~JITScript()
+JITChunk::~JITChunk()
 {
     code.release();
 
     if (pcLengths)
         Foreground::free_(pcLengths);
 
 #if defined JS_POLYIC
     ic::GetElementIC *getElems_ = getElems();
@@ -1244,19 +1264,16 @@ mjit::JITScript::~JITScript()
         Destroy(getElems_[i]);
     for (uint32_t i = 0; i < nSetElems; i++)
         Destroy(setElems_[i]);
     for (uint32_t i = 0; i < nPICs; i++)
         Destroy(pics_[i]);
 #endif
 
 #if defined JS_MONOIC
-    if (argsCheckPool)
-        argsCheckPool->release();
-
     for (JSC::ExecutablePool **pExecPool = execPools.begin();
          pExecPool != execPools.end();
          ++pExecPool)
     {
         (*pExecPool)->release();
     }
 
     for (unsigned i = 0; i < nativeCallStubs.length(); i++) {
@@ -1266,49 +1283,118 @@ mjit::JITScript::~JITScript()
     }
 
     ic::CallICInfo *callICs_ = callICs();
     for (uint32_t i = 0; i < nCallICs; i++) {
         callICs_[i].releasePools();
         if (callICs_[i].fastGuardedObject)
             callICs_[i].purgeGuardedObject();
     }
+#endif
+}
 
-    // Fixup any ICs still referring to this JIT.
-    while (!JS_CLIST_IS_EMPTY(&callers)) {
-        JS_STATIC_ASSERT(offsetof(ic::CallICInfo, links) == 0);
-        ic::CallICInfo *ic = (ic::CallICInfo *) callers.next;
+void
+JITScript::destroy(JSContext *cx)
+{
+    for (unsigned i = 0; i < nchunks; i++)
+        destroyChunk(cx, i);
+}
+
+void
+JITScript::destroyChunk(JSContext *cx, unsigned chunkIndex, bool resetUses)
+{
+    ChunkDescriptor &desc = chunkDescriptor(chunkIndex);
+
+    if (desc.chunk) {
+        Probes::discardMJITCode(cx, this, script, desc.chunk->code.m_code.executableAddress());
+        cx->delete_(desc.chunk);
+        desc.chunk = NULL;
 
-        uint8_t *start = (uint8_t *)ic->funGuard.executableAddress();
-        JSC::RepatchBuffer repatch(JSC::JITCode(start - 32, 64));
+        CrossChunkEdge *edges = this->edges();
+        for (unsigned i = 0; i < nedges; i++) {
+            CrossChunkEdge &edge = edges[i];
+            if (edge.source >= desc.begin && edge.source < desc.end) {
+                edge.sourceJump1 = edge.sourceJump2 = NULL;
+                if (edge.jumpTableEntries) {
+                    cx->delete_(edge.jumpTableEntries);
+                    edge.jumpTableEntries = NULL;
+                }
+            } else if (edge.target >= desc.begin && edge.target < desc.end) {
+                edge.targetLabel = NULL;
+                patchEdge(edge, edge.shimLabel);
+            }
+        }
+    }
+
+    if (resetUses)
+        desc.counter = 0;
 
-        repatch.repatch(ic->funGuard, NULL);
-        repatch.relink(ic->funJump, ic->slowPathStart);
-        ic->purgeGuardedObject();
+    if (chunkIndex == 0) {
+        if (argsCheckPool) {
+            argsCheckPool->release();
+            argsCheckPool = NULL;
+        }
+
+        invokeEntry = NULL;
+        fastEntry = NULL;
+        arityCheckEntry = NULL;
+        argsCheckEntry = NULL;
+
+        if (script->jitNormal == this)
+            script->jitArityCheckNormal = NULL;
+        else
+            script->jitArityCheckCtor = NULL;
+
+        // Fixup any ICs still referring to this chunk.
+        while (!JS_CLIST_IS_EMPTY(&callers)) {
+            JS_STATIC_ASSERT(offsetof(ic::CallICInfo, links) == 0);
+            ic::CallICInfo *ic = (ic::CallICInfo *) callers.next;
+
+            uint8_t *start = (uint8_t *)ic->funGuard.executableAddress();
+            JSC::RepatchBuffer repatch(JSC::JITCode(start - 32, 64));
+
+            repatch.repatch(ic->funGuard, NULL);
+            repatch.relink(ic->funJump, ic->slowPathStart);
+            ic->purgeGuardedObject();
+        }
     }
-#endif
 }
 
 size_t
 JSScript::jitDataSize(JSMallocSizeOfFun mallocSizeOf)
 {
     size_t n = 0;
     if (jitNormal)
         n += jitNormal->scriptDataSize(mallocSizeOf); 
     if (jitCtor)
         n += jitCtor->scriptDataSize(mallocSizeOf); 
     return n;
 }
 
-/* Please keep in sync with Compiler::finishThisUp! */
 size_t
 mjit::JITScript::scriptDataSize(JSMallocSizeOfFun mallocSizeOf)
 {
+    size_t usable = mallocSizeOf(this,
+                                 sizeof(JITScript)
+                                 + (nchunks * sizeof(ChunkDescriptor))
+                                 + (nedges * sizeof(CrossChunkEdge)));
+    for (unsigned i = 0; i < nchunks; i++) {
+        const ChunkDescriptor &desc = chunkDescriptor(i);
+        if (desc.chunk)
+            usable += desc.chunk->scriptDataSize(mallocSizeOf);
+    }
+    return usable;
+}
+
+/* Please keep in sync with Compiler::finishThisUp! */
+size_t
+mjit::JITChunk::scriptDataSize(JSMallocSizeOfFun mallocSizeOf)
+{
     size_t computedSize =
-        sizeof(JITScript) +
+        sizeof(JITChunk) +
         sizeof(NativeMapEntry) * nNmapPairs +
         sizeof(InlineFrame) * nInlineFrames +
         sizeof(CallSite) * nCallSites +
 #if defined JS_MONOIC
         sizeof(ic::GetGlobalNameIC) * nGetGlobalNames +
         sizeof(ic::SetGlobalNameIC) * nSetGlobalNames +
         sizeof(ic::CallICInfo) * nCallICs +
         sizeof(ic::EqualityICInfo) * nEqualityICs +
@@ -1329,89 +1415,52 @@ mjit::ReleaseScriptCode(JSContext *cx, J
     // NB: The recompiler may call ReleaseScriptCode, in which case it
     // will get called again when the script is destroyed, so we
     // must protect against calling ReleaseScriptCode twice.
 
     JITScript **pjit = construct ? &script->jitCtor : &script->jitNormal;
     void **parity = construct ? &script->jitArityCheckCtor : &script->jitArityCheckNormal;
 
     if (*pjit) {
-        Probes::discardMJITCode(cx, *pjit, script, (*pjit)->code.m_code.executableAddress());
-        (*pjit)->~JITScript();
+        (*pjit)->destroy(cx);
         cx->free_(*pjit);
         *pjit = NULL;
         *parity = NULL;
     }
 }
 
 #ifdef JS_METHODJIT_PROFILE_STUBS
 void JS_FASTCALL
 mjit::ProfileStubCall(VMFrame &f)
 {
     JSOp op = JSOp(*f.regs.pc);
     StubCallsForOp[op]++;
 }
 #endif
 
-#ifdef JS_POLYIC
-static int
-PICPCComparator(const void *key, const void *entry)
+JITChunk *
+JITScript::findCodeChunk(void *addr)
 {
-    const jsbytecode *pc = (const jsbytecode *)key;
-    const ic::PICInfo *pic = (const ic::PICInfo *)entry;
-
-    /*
-     * We can't just return |pc - pic->pc| because the pointers may be
-     * far apart and an int (or even a ptrdiff_t) may not be large
-     * enough to hold the difference. C says that pointer subtraction
-     * is only guaranteed to work for two pointers into the same array.
-     */
-    if (pc < pic->pc)
-        return -1;
-    else if (pc == pic->pc)
-        return 0;
-    else
-        return 1;
+    for (unsigned i = 0; i < nchunks; i++) {
+        ChunkDescriptor &desc = chunkDescriptor(i);
+        if (desc.chunk && desc.chunk->isValidCode(addr))
+            return desc.chunk;
+    }
+    return NULL;
 }
 
-uintN
-mjit::GetCallTargetCount(JSScript *script, jsbytecode *pc)
+jsbytecode *
+JITScript::nativeToPC(void *returnAddress, CallSite **pinline)
 {
-    ic::PICInfo *pic;
-    
-    if (mjit::JITScript *jit = script->getJIT(false)) {
-        pic = (ic::PICInfo *)bsearch(pc, jit->pics(), jit->nPICs, sizeof(ic::PICInfo),
-                                     PICPCComparator);
-        if (pic)
-            return pic->stubsGenerated + 1; /* Add 1 for the inline path. */
-    }
-    
-    if (mjit::JITScript *jit = script->getJIT(true)) {
-        pic = (ic::PICInfo *)bsearch(pc, jit->pics(), jit->nPICs, sizeof(ic::PICInfo),
-                                     PICPCComparator);
-        if (pic)
-            return pic->stubsGenerated + 1; /* Add 1 for the inline path. */
-    }
+    JITChunk *chunk = findCodeChunk(returnAddress);
+    JS_ASSERT(chunk);
 
-    return 1;
-}
-#else
-uintN
-mjit::GetCallTargetCount(JSScript *script, jsbytecode *pc)
-{
-    return 1;
-}
-#endif
-
-jsbytecode *
-JITScript::nativeToPC(void *returnAddress, CallSite **pinline) const
-{
     size_t low = 0;
-    size_t high = nCallICs;
-    js::mjit::ic::CallICInfo *callICs_ = callICs();
+    size_t high = chunk->nCallICs;
+    js::mjit::ic::CallICInfo *callICs_ = chunk->callICs();
     while (high > low + 1) {
         /* Could overflow here on a script with 2 billion calls. Oh well. */
         size_t mid = (high + low) / 2;
         void *entry = callICs_[mid].funGuard.executableAddress();
 
         /*
          * Use >= here as the return address of the call is likely to be
          * the start address of the next (possibly IC'ed) operation.
@@ -1423,17 +1472,17 @@ JITScript::nativeToPC(void *returnAddres
     }
 
     js::mjit::ic::CallICInfo &ic = callICs_[low];
     JS_ASSERT((uint8_t*)ic.funGuard.executableAddress() + ic.joinPointOffset == returnAddress);
 
     if (ic.call->inlineIndex != UINT32_MAX) {
         if (pinline)
             *pinline = ic.call;
-        InlineFrame *frame = &inlineFrames()[ic.call->inlineIndex];
+        InlineFrame *frame = &chunk->inlineFrames()[ic.call->inlineIndex];
         while (frame && frame->parent)
             frame = frame->parent;
         return frame->parentpc;
     }
 
     if (pinline)
         *pinline = NULL;
     return script->code + ic.call->pcOffset;
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -58,17 +58,20 @@
 #endif
 
 #if !defined(JS_NUNBOX32) && !defined(JS_PUNBOX64)
 # error "No boxing format selected."
 #endif
 
 namespace js {
 
-namespace mjit { struct JITScript; }
+namespace mjit {
+    struct JITChunk;
+    struct JITScript;
+}
 
 struct VMFrame
 {
 #if defined(JS_CPU_SPARC)
     void *savedL0;
     void *savedL1;
     void *savedL2;
     void *savedL3;
@@ -238,16 +241,19 @@ struct VMFrame
      * Get the current frame and JIT. Note that these are NOT stable in case
      * of recompilations; all code which expects these to be stable should
      * check that cx->recompilations() has not changed across a call that could
      * trigger recompilation (pretty much any time the VM is called into).
      */
     StackFrame *fp() { return regs.fp(); }
     mjit::JITScript *jit() { return fp()->jit(); }
 
+    inline mjit::JITChunk *chunk();
+    inline unsigned chunkIndex();
+
     /* Get the inner script/PC in case of inlining. */
     inline JSScript *script();
     inline jsbytecode *pc();
 
 #if defined(JS_CPU_SPARC)
     static const size_t offsetOfFp = 30 * sizeof(void *) + FrameRegs::offsetOfFp;
     static const size_t offsetOfInlined = 30 * sizeof(void *) + FrameRegs::offsetOfInlined;
 #elif defined(JS_CPU_MIPS)
@@ -298,16 +304,19 @@ enum RejoinState {
      * State is coherent for the start of the current bytecode, which is a TRAP
      * that has already been invoked and should not be invoked again.
      */
     REJOIN_TRAP,
 
     /* State is coherent for the start of the next (fallthrough) bytecode. */
     REJOIN_FALLTHROUGH,
 
+    /* State is coherent for the start of the bytecode returned by the call. */
+    REJOIN_JUMP,
+
     /*
      * As for REJOIN_FALLTHROUGH, but holds a reference on the compartment's
      * orphaned native pools which needs to be reclaimed by InternalInterpret.
      * The return value needs to be adjusted if REJOIN_NATIVE_LOWERED, and
      * REJOIN_NATIVE_GETTER is for ABI calls made for property accesses.
      */
     REJOIN_NATIVE,
     REJOIN_NATIVE_LOWERED,
@@ -363,16 +372,30 @@ enum RejoinState {
 
     /*
      * For an opcode fused with IFEQ/IFNE, call returns a boolean indicating
      * the result of the comparison and whether to take or not take the branch.
      */
     REJOIN_BRANCH
 };
 
+/* Get the rejoin state for a StackFrame after returning from a scripted call. */
+static inline JSRejoinState
+ScriptedRejoin(uint32_t pcOffset)
+{
+    return REJOIN_SCRIPTED | (pcOffset << 1);
+}
+
+/* Get the rejoin state for a StackFrame after returning from a stub call. */
+static inline JSRejoinState
+StubRejoin(RejoinState rejoin)
+{
+    return rejoin << 1;
+}
+
 /* Helper to watch for recompilation and frame expansion activity on a compartment. */
 struct RecompilationMonitor
 {
     JSContext *cx;
 
     /*
      * If either inline frame expansion or recompilation occurs, then ICs and
      * stubs should not depend on the frame or JITs being intact. The two are
@@ -631,67 +654,48 @@ struct NativeCallStub {
      */
 #ifdef JS_CPU_X64
     JSC::CodeLocationDataLabelPtr jump;
 #else
     JSC::CodeLocationJump jump;
 #endif
 };
 
-struct JITScript {
+struct JITChunk
+{
     typedef JSC::MacroAssemblerCodeRef CodeRef;
     CodeRef         code;       /* pool & code addresses */
 
-    JSScript        *script;
-
-    void            *invokeEntry;       /* invoke address */
-    void            *fastEntry;         /* cached entry, fastest */
-    void            *arityCheckEntry;   /* arity check address */
-    void            *argsCheckEntry;    /* arguments check address */
-
     PCLengthEntry   *pcLengths;         /* lengths for outer and inline frames */
 
     /*
      * This struct has several variable-length sections that are allocated on
      * the end:  nmaps, MICs, callICs, etc.  To save space -- worthwhile
      * because JITScripts are common -- we only record their lengths.  We can
      * find any of the sections from the lengths because we know their order.
      * Therefore, do not change the section ordering in finishThisUp() without
      * changing nMICs() et al as well.
      */
-    uint32_t        nNmapPairs:31;      /* The NativeMapEntrys are sorted by .bcOff.
+    uint32_t        nNmapPairs;         /* The NativeMapEntrys are sorted by .bcOff.
                                            .ncode values may not be NULL. */
-    bool            singleStepMode:1;   /* compiled in "single step mode" */
     uint32_t        nInlineFrames;
     uint32_t        nCallSites;
 #ifdef JS_MONOIC
     uint32_t        nGetGlobalNames;
     uint32_t        nSetGlobalNames;
     uint32_t        nCallICs;
     uint32_t        nEqualityICs;
 #endif
 #ifdef JS_POLYIC
     uint32_t        nGetElems;
     uint32_t        nSetElems;
     uint32_t        nPICs;
 #endif
 
 #ifdef JS_MONOIC
-    /* Inline cache at function entry for checking this/argument types. */
-    JSC::CodeLocationLabel argsCheckStub;
-    JSC::CodeLocationLabel argsCheckFallthrough;
-    JSC::CodeLocationJump  argsCheckJump;
-    JSC::ExecutablePool *argsCheckPool;
-    void resetArgsCheck();
-#endif
-
-    /* List of inline caches jumping to the fastEntry. */
-    JSCList          callers;
-
-#ifdef JS_MONOIC
     // Additional ExecutablePools that IC stubs were generated into.
     typedef Vector<JSC::ExecutablePool *, 0, SystemAllocPolicy> ExecPoolVector;
     ExecPoolVector execPools;
 #endif
 
     // Additional ExecutablePools for native call and getter stubs.
     Vector<NativeCallStub, 0, SystemAllocPolicy> nativeCallStubs;
 
@@ -705,38 +709,149 @@ struct JITScript {
     ic::EqualityICInfo *equalityICs() const;
 #endif
 #ifdef JS_POLYIC
     ic::GetElementIC *getElems() const;
     ic::SetElementIC *setElems() const;
     ic::PICInfo     *pics() const;
 #endif
 
-    ~JITScript();
-
     bool isValidCode(void *ptr) {
         char *jitcode = (char *)code.m_code.executableAddress();
         char *jcheck = (char *)ptr;
         return jcheck >= jitcode && jcheck < jitcode + code.m_size;
     }
 
     void nukeScriptDependentICs();
 
     /* |mallocSizeOf| can be NULL here, in which case the fallback size computation will be used. */
     size_t scriptDataSize(JSMallocSizeOfFun mallocSizeOf);
 
-    jsbytecode *nativeToPC(void *returnAddress, CallSite **pinline) const;
+    ~JITChunk();
 
   private:
     /* Helpers used to navigate the variable-length sections. */
     char *commonSectionLimit() const;
     char *monoICSectionsLimit() const;
     char *polyICSectionsLimit() const;
 };
 
+void
+SetChunkLimit(uint32_t limit);
+
+/* Information about a compilation chunk within a script. */
+struct ChunkDescriptor
+{
+    /* Bytecode range of the chunk: [begin,end) */
+    uint32_t begin;
+    uint32_t end;
+
+    /* Use counter for the chunk. */
+    uint32_t counter;
+
+    /* Optional compiled code for the chunk. */
+    JITChunk *chunk;
+};
+
+/* Jump or fallthrough edge in the bytecode which crosses a chunk boundary. */
+struct CrossChunkEdge
+{
+    /* Bytecode offsets of the source and target of the edge. */
+    uint32_t source;
+    uint32_t target;
+
+    /* Locations of the jump(s) for the source, NULL if not compiled. */
+    void *sourceJump1;
+    void *sourceJump2;
+
+    /* Any jump table entries along this edge. */
+    typedef Vector<void**,4,SystemAllocPolicy> JumpTableEntryVector;
+    JumpTableEntryVector *jumpTableEntries;
+
+    /* Location of the label for the target, NULL if not compiled. */
+    void *targetLabel;
+
+    /*
+     * Location of a shim which will transfer control to the interpreter at the
+     * target bytecode. The source jumps are patched to jump to this label if
+     * the source is compiled but not the target.
+     */
+    void *shimLabel;
+};
+
+struct JITScript
+{
+    JSScript        *script;
+
+    void            *invokeEntry;       /* invoke address */
+    void            *fastEntry;         /* cached entry, fastest */
+    void            *arityCheckEntry;   /* arity check address */
+    void            *argsCheckEntry;    /* arguments check address */
+
+    /* List of inline caches jumping to the fastEntry. */
+    JSCList         callers;
+
+    uint32_t        nchunks;
+    uint32_t        nedges;
+
+    /*
+     * Pool for shims which transfer control to the interpreter on cross chunk
+     * edges to chunks which do not have compiled code.
+     */
+    JSC::ExecutablePool *shimPool;
+
+#ifdef JS_MONOIC
+    /* Inline cache at function entry for checking this/argument types. */
+    JSC::CodeLocationLabel argsCheckStub;
+    JSC::CodeLocationLabel argsCheckFallthrough;
+    JSC::CodeLocationJump  argsCheckJump;
+    JSC::ExecutablePool *argsCheckPool;
+    void resetArgsCheck();
+#endif
+
+    ChunkDescriptor &chunkDescriptor(unsigned i) {
+        JS_ASSERT(i < nchunks);
+        ChunkDescriptor *descs = (ChunkDescriptor *) ((char *) this + sizeof(JITScript));
+        return descs[i];
+    }
+
+    unsigned chunkIndex(jsbytecode *pc) {
+        unsigned offset = pc - script->code;
+        JS_ASSERT(offset < script->length);
+        for (unsigned i = 0; i < nchunks; i++) {
+            const ChunkDescriptor &desc = chunkDescriptor(i);
+            JS_ASSERT(desc.begin <= offset);
+            if (offset < desc.end)
+                return i;
+        }
+        JS_NOT_REACHED("Bad chunk layout");
+        return 0;
+    }
+
+    JITChunk *chunk(jsbytecode *pc) {
+        return chunkDescriptor(chunkIndex(pc)).chunk;
+    }
+
+    JITChunk *findCodeChunk(void *addr);
+
+    CrossChunkEdge *edges() {
+        return (CrossChunkEdge *) (&chunkDescriptor(0) + nchunks);
+    }
+
+    /* Patch any compiled sources in edge to jump to label. */
+    void patchEdge(const CrossChunkEdge &edge, void *label);
+
+    jsbytecode *nativeToPC(void *returnAddress, CallSite **pinline);
+
+    size_t scriptDataSize(JSMallocSizeOfFun mallocSizeOf);
+
+    void destroy(JSContext *cx);
+    void destroyChunk(JSContext *cx, unsigned chunkIndex, bool resetUses = true);
+};
+
 /*
  * Execute the given mjit code. This is a low-level call and callers must
  * provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT.
  */
 JaegerStatus EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit,
                             bool partial);
 
 /* Execute a method that has been JIT compiled. */
@@ -753,18 +868,25 @@ enum CompileStatus
     Compile_Retry,        // static overflow or failed inline, try to recompile
     Compile_Error,        // OOM
     Compile_Skipped
 };
 
 void JS_FASTCALL
 ProfileStubCall(VMFrame &f);
 
-CompileStatus JS_NEVER_INLINE
-TryCompile(JSContext *cx, JSScript *script, bool construct);
+enum CompileRequest
+{
+    CompileRequest_Interpreter,
+    CompileRequest_JIT
+};
+
+CompileStatus
+CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
+             bool construct, CompileRequest request);
 
 void
 ReleaseScriptCode(JSContext *cx, JSScript *script, bool construct);
 
 inline void
 ReleaseScriptCode(JSContext *cx, JSScript *script)
 {
     if (script->jitCtor)
@@ -809,19 +931,16 @@ struct CallSite
         this->rejoin = rejoin;
     }
 
     bool isTrap() const {
         return rejoin == REJOIN_TRAP;
     }
 };
 
-uintN
-GetCallTargetCount(JSScript *script, jsbytecode *pc);
-
 void
 DumpAllProfiles(JSContext *cx);
 
 inline void * bsearch_nmap(NativeMapEntry *nmap, size_t nPairs, size_t bcOff)
 {
     size_t lo = 1, hi = nPairs;
     while (1) {
         /* current unsearched space is from lo-1 to hi-1, inclusive. */
@@ -838,52 +957,56 @@ inline void * bsearch_nmap(NativeMapEntr
             continue;
         }
         return nmap[mid-1].ncode;
     }
 }
 
 } /* namespace mjit */
 
+inline mjit::JITChunk *
+VMFrame::chunk()
+{
+    return jit()->chunk(regs.pc);
+}
+
+inline unsigned
+VMFrame::chunkIndex()
+{
+    return jit()->chunkIndex(regs.pc);
+}
+
 inline JSScript *
 VMFrame::script()
 {
     if (regs.inlined())
-        return jit()->inlineFrames()[regs.inlined()->inlineIndex].fun->script();
+        return chunk()->inlineFrames()[regs.inlined()->inlineIndex].fun->script();
     return fp()->script();
 }
 
 inline jsbytecode *
 VMFrame::pc()
 {
     if (regs.inlined())
         return script()->code + regs.inlined()->pcOffset;
     return regs.pc;
 }
 
 } /* namespace js */
 
 inline void *
-JSScript::maybeNativeCodeForPC(bool constructing, jsbytecode *pc)
+JSScript::nativeCodeForPC(bool constructing, jsbytecode *pc)
 {
     js::mjit::JITScript *jit = getJIT(constructing);
     if (!jit)
         return NULL;
-    JS_ASSERT(pc >= code && pc < code + length);
-    return bsearch_nmap(jit->nmap(), jit->nNmapPairs, (size_t)(pc - code));
-}
-
-inline void *
-JSScript::nativeCodeForPC(bool constructing, jsbytecode *pc)
-{
-    js::mjit::JITScript *jit = getJIT(constructing);
-    JS_ASSERT(pc >= code && pc < code + length);
-    void* native = bsearch_nmap(jit->nmap(), jit->nNmapPairs, (size_t)(pc - code));
-    JS_ASSERT(native);
-    return native;
+    js::mjit::JITChunk *chunk = jit->chunk(pc);
+    if (!chunk)
+        return NULL;
+    return bsearch_nmap(chunk->nmap(), chunk->nNmapPairs, (size_t)(pc - code));
 }
 
 extern "C" void JaegerTrampolineReturn();
 extern "C" void JaegerInterpoline();
 extern "C" void JaegerInterpolineScripted();
 
 #if defined(_MSC_VER) || defined(_WIN64)
 extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame);
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -73,17 +73,17 @@ typedef JSC::MacroAssembler::Label Label
 typedef JSC::MacroAssembler::DataLabel32 DataLabel32;
 typedef JSC::MacroAssembler::DataLabelPtr DataLabelPtr;
 
 #if defined JS_MONOIC
 
 static void
 PatchGetFallback(VMFrame &f, ic::GetGlobalNameIC *ic)
 {
-    Repatcher repatch(f.jit());
+    Repatcher repatch(f.chunk());
     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::Name));
     repatch.relink(ic->slowPathCall, fptr);
 }
 
 void JS_FASTCALL
 ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
 {
     JSObject &obj = f.fp()->scopeChain().global();
@@ -105,17 +105,17 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlo
         if (shape)
             PatchGetFallback(f, ic);
         stubs::Name(f);
         return;
     }
     uint32_t slot = shape->slot();
 
     /* Patch shape guard. */
-    Repatcher repatcher(f.jit());
+    Repatcher repatcher(f.chunk());
     repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj.lastProperty());
 
     /* Patch loads. */
     uint32_t index = obj.dynamicSlotIndex(slot);
     JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
     repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value));
 
     /* Do load anyway... this time. */
@@ -131,17 +131,17 @@ DisabledSetGlobal(VMFrame &f, ic::SetGlo
 
 template void JS_FASTCALL DisabledSetGlobal<true>(VMFrame &f, ic::SetGlobalNameIC *ic);
 template void JS_FASTCALL DisabledSetGlobal<false>(VMFrame &f, ic::SetGlobalNameIC *ic);
 
 static void
 PatchSetFallback(VMFrame &f, ic::SetGlobalNameIC *ic)
 {
     JSScript *script = f.script();
-    Repatcher repatch(f.jit());
+    Repatcher repatch(f.chunk());
     VoidStubSetGlobal stub = STRICT_VARIANT(DisabledSetGlobal);
     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stub));
     repatch.relink(ic->slowPathCall, fptr);
 }
 
 void
 SetGlobalNameIC::patchExtraShapeGuard(Repatcher &repatcher, const Shape *shape)
 {
@@ -172,17 +172,17 @@ UpdateSetGlobalName(VMFrame &f, ic::SetG
         obj->watched())
     {
         /* Disable the IC for weird shape attributes and watchpoints. */
         PatchSetFallback(f, ic);
         return Lookup_Uncacheable;
     }
 
     /* Object is not branded, so we can use the inline path. */
-    Repatcher repatcher(f.jit());
+    Repatcher repatcher(f.chunk());
     ic->patchInlineShapeGuard(repatcher, obj->lastProperty());
 
     uint32_t index = obj->dynamicSlotIndex(shape->slot());
     JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
     repatcher.patchAddressOffsetForValueStore(label, index * sizeof(Value),
                                               ic->vr.isTypeKnown());
 
     return Lookup_Cacheable;
@@ -217,19 +217,17 @@ class EqualityICLinker : public LinkerHe
         : LinkerHelper(masm, JSC::METHOD_CODE), f(f)
     { }
 
     bool init(JSContext *cx) {
         JSC::ExecutablePool *pool = LinkerHelper::init(cx);
         if (!pool)
             return false;
         JS_ASSERT(!f.regs.inlined());
-        JSScript *script = f.fp()->script();
-        JITScript *jit = script->getJIT(f.fp()->isConstructing());
-        if (!jit->execPools.append(pool)) {
+        if (!f.chunk()->execPools.append(pool)) {
             pool->release();
             js_ReportOutOfMemory(cx);
             return false;
         }
         return true;
     }
 };
 
@@ -349,24 +347,24 @@ class EqualityCompiler : public BaseComp
     }
 
     bool linkForIC(Assembler &masm)
     {
         EqualityICLinker buffer(masm, f);
         if (!buffer.init(cx))
             return false;
 
-        Repatcher repatcher(f.jit());
+        Repatcher repatcher(f.chunk());
 
         /* Overwrite the call to the IC with a call to the stub. */
         JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic.stub));
         repatcher.relink(ic.stubCall, fptr);
 
         // Silently fail, the IC is disabled now.
-        if (!buffer.verifyRange(f.jit()))
+        if (!buffer.verifyRange(f.chunk()))
             return true;
 
         /* Set the targets of all type test failures to go to the stub. */
         for (size_t i = 0; i < jumpList.length(); i++)
             buffer.link(jumpList[i], ic.stubEntry);
         jumpList.clear();
 
         /* Set the targets for the the success and failure of the actual equality test. */
@@ -435,17 +433,17 @@ NativeStubLinker::init(JSContext *cx)
     JSC::ExecutablePool *pool = LinkerHelper::init(cx);
     if (!pool)
         return false;
 
     NativeCallStub stub;
     stub.pc = pc;
     stub.pool = pool;
     stub.jump = locationOf(done);
-    if (!jit->nativeCallStubs.append(stub)) {
+    if (!chunk->nativeCallStubs.append(stub)) {
         pool->release();
         return false;
     }
 
     return true;
 }
 
 /*
@@ -575,27 +573,27 @@ class CallCompiler : public BaseCompiler
         JSC::ExecutablePool *ep = linker.init(f.cx);
         if (!ep)
             return NULL;
         JS_ASSERT(!ic.pools[index]);
         ic.pools[index] = ep;
         return ep;
     }
 
-    void disable(JITScript *jit)
+    void disable()
     {
         JSC::CodeLocationCall oolCall = ic.slowPathStart.callAtOffset(ic.oolCallOffset);
-        Repatcher repatch(jit);
+        Repatcher repatch(f.chunk());
         JSC::FunctionPtr fptr = callingNew
                                 ? JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowNewFromIC))
                                 : JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowCallFromIC));
         repatch.relink(oolCall, fptr);
     }
 
-    bool generateFullCallStub(JITScript *from, JSScript *script, uint32_t flags)
+    bool generateFullCallStub(JSScript *script, uint32_t flags)
     {
         /*
          * Create a stub that works with arity mismatches. Like the fast-path,
          * this allocates a frame on the caller side, but also performs extra
          * checks for compilability. Perhaps this should be a separate, shared
          * trampoline, but for now we generate it dynamically.
          */
         Assembler masm;
@@ -644,17 +642,17 @@ class CallCompiler : public BaseCompiler
                                 compilePtr, f.regs.pc, &inlined, -1);
         }
 
         Jump notCompiled = masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg,
                                               Registers::ReturnReg);
         masm.loadPtr(FrameAddress(VMFrame::offsetOfRegsSp()), JSFrameReg);
 
         /* Compute the value of ncode to use at this call site. */
-        ncode = (uint8_t *) f.jit()->code.m_code.executableAddress() + ic.call->codeOffset;
+        ncode = (uint8_t *) f.chunk()->code.m_code.executableAddress() + ic.call->codeOffset;
         masm.storePtr(ImmPtr(ncode), Address(JSFrameReg, StackFrame::offsetOfNcode()));
 
         masm.jump(Registers::ReturnReg);
 
         hasCode.linkTo(masm.label(), &masm);
 
         /* Get nmap[ARITY], set argc, call. */
         if (ic.frameSize.isStatic())
@@ -663,46 +661,46 @@ class CallCompiler : public BaseCompiler
             masm.load32(FrameAddress(VMFrame::offsetOfDynamicArgc()), JSParamReg_Argc);
         masm.jump(t0);
 
         LinkerHelper linker(masm, JSC::METHOD_CODE);
         JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_ScriptStub);
         if (!ep)
             return false;
 
-        if (!linker.verifyRange(from)) {
-            disable(from);
+        if (!linker.verifyRange(f.chunk())) {
+            disable();
             return true;
         }
 
         linker.link(notCompiled, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset));
         JSC::CodeLocationLabel cs = linker.finalize(f);
 
         JaegerSpew(JSpew_PICs, "generated CALL stub %p (%lu bytes)\n", cs.executableAddress(),
                    (unsigned long) masm.size());
 
         if (f.regs.inlined()) {
             JSC::LinkBuffer code((uint8_t *) cs.executableAddress(), masm.size(), JSC::METHOD_CODE);
             code.patch(inlined, f.regs.inlined());
         }
 
-        Repatcher repatch(from);
+        Repatcher repatch(f.chunk());
         JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset);
         repatch.relink(oolJump, cs);
 
         return true;
     }
 
-    bool patchInlinePath(JITScript *from, JSScript *script, JSObject *obj)
+    bool patchInlinePath(JSScript *script, JSObject *obj)
     {
         JS_ASSERT(ic.frameSize.isStatic());
         JITScript *jit = script->getJIT(callingNew);
 
         /* Very fast path. */
-        Repatcher repatch(from);
+        Repatcher repatch(f.chunk());
 
         /*
          * Use the arguments check entry if this is a monitored call, we might
          * not have accounted for all possible argument types.
          */
         void *entry = ic.typeMonitored ? jit->argsCheckEntry : jit->fastEntry;
 
         if (!repatch.canRelink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset),
@@ -719,17 +717,17 @@ class CallCompiler : public BaseCompiler
 
         JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n",
                    ic.funGuard.executableAddress(),
                    static_cast<void*>(ic.fastGuardedObject));
 
         return true;
     }
 
-    bool generateStubForClosures(JITScript *from, JSObject *obj)
+    bool generateStubForClosures(JSObject *obj)
     {
         JS_ASSERT(ic.frameSize.isStatic());
 
         /* Slightly less fast path - guard on fun->script() instead. */
         Assembler masm;
 
         Registers tempRegs(Registers::AvailRegs);
         tempRegs.takeReg(ic.funObjReg);
@@ -747,39 +745,37 @@ class CallCompiler : public BaseCompiler
 
         LinkerHelper linker(masm, JSC::METHOD_CODE);
         JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_ClosureStub);
         if (!ep)
             return false;
 
         ic.hasJsFunCheck = true;
 
-        if (!linker.verifyRange(from)) {
-            disable(from);
+        if (!linker.verifyRange(f.chunk())) {
+            disable();
             return true;
         }
 
         linker.link(claspGuard, ic.slowPathStart);
         linker.link(funGuard, ic.slowPathStart);
         linker.link(done, ic.funGuard.labelAtOffset(ic.hotPathOffset));
         JSC::CodeLocationLabel cs = linker.finalize(f);
 
         JaegerSpew(JSpew_PICs, "generated CALL closure stub %p (%lu bytes)\n",
                    cs.executableAddress(), (unsigned long) masm.size());
 
-        Repatcher repatch(from);
+        Repatcher repatch(f.chunk());
         repatch.relink(ic.funJump, cs);
 
         return true;
     }
 
     bool generateNativeStub()
     {
-        JITScript *jit = f.jit();
-
         /* Snapshot the frameDepth before SplatApplyArgs modifies it. */
         uintN initialFrameDepth = f.regs.sp - f.fp()->slots();
 
         /*
          * SplatApplyArgs has not been called, so we call it here before
          * potentially touching f.u.call.dynamicArgc.
          */
         CallArgs args;
@@ -920,45 +916,43 @@ class CallCompiler : public BaseCompiler
         if (native == regexp_exec && !CallResultEscapes(f.pc()))
             native = regexp_test;
 
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, native), false);
 
         NativeStubLinker::FinalJump done;
         if (!NativeStubEpilogue(f, masm, &done, initialFrameDepth, vpOffset, MaybeRegisterID(), MaybeRegisterID()))
             return false;
-        NativeStubLinker linker(masm, f.jit(), f.regs.pc, done);
+        NativeStubLinker linker(masm, f.chunk(), f.regs.pc, done);
         if (!linker.init(f.cx))
             THROWV(true);
 
-        if (!linker.verifyRange(jit)) {
-            disable(jit);
+        if (!linker.verifyRange(f.chunk())) {
+            disable();
             return true;
         }
 
         linker.patchJump(ic.slowPathStart.labelAtOffset(ic.slowJoinOffset));
 
         ic.fastGuardedNative = fun;
 
         linker.link(funGuard, ic.slowPathStart);
         JSC::CodeLocationLabel start = linker.finalize(f);
 
         JaegerSpew(JSpew_PICs, "generated native CALL stub %p (%lu bytes)\n",
                    start.executableAddress(), (unsigned long) masm.size());
 
-        Repatcher repatch(jit);
+        Repatcher repatch(f.chunk());
         repatch.relink(ic.funJump, start);
 
         return true;
     }
 
     void *update()
     {
-        StackFrame *fp = f.fp();
-        JITScript *jit = fp->jit();
         RecompilationMonitor monitor(cx);
 
         bool lowered = ic.frameSize.lowered(f.pc());
         JS_ASSERT_IF(lowered, !callingNew);
 
         stubs::UncachedCallResult ucr;
         if (callingNew)
             stubs::UncachedNewHelper(f, ic.frameSize.staticArgc(), &ucr);
@@ -970,50 +964,50 @@ class CallCompiler : public BaseCompiler
         // and the compilation has a static overflow.
         if (monitor.recompiled())
             return ucr.codeAddr;
 
         // If the function cannot be jitted (generally unjittable or empty script),
         // patch this site to go to a slow path always.
         if (!ucr.codeAddr) {
             if (ucr.unjittable)
-                disable(jit);
+                disable();
             return NULL;
         }
             
         JSFunction *fun = ucr.fun;
         JS_ASSERT(fun);
         JSScript *script = fun->script();
         JS_ASSERT(script);
 
         uint32_t flags = callingNew ? StackFrame::CONSTRUCTING : 0;
 
         if (!ic.hit) {
             ic.hit = true;
             return ucr.codeAddr;
         }
 
         if (!ic.frameSize.isStatic() || ic.frameSize.staticArgc() != fun->nargs) {
-            if (!generateFullCallStub(jit, script, flags))
+            if (!generateFullCallStub(script, flags))
                 THROWV(NULL);
         } else {
-            if (!ic.fastGuardedObject && patchInlinePath(jit, script, fun)) {
+            if (!ic.fastGuardedObject && patchInlinePath(script, fun)) {
                 // Nothing, done.
             } else if (ic.fastGuardedObject &&
                        !ic.hasJsFunCheck &&
                        !ic.fastGuardedNative &&
                        ic.fastGuardedObject->toFunction()->script() == fun->script()) {
                 /*
                  * Note: Multiple "function guard" stubs are not yet
                  * supported, thus the fastGuardedNative check.
                  */
-                if (!generateStubForClosures(jit, fun))
+                if (!generateStubForClosures(fun))
                     THROWV(NULL);
             } else {
-                if (!generateFullCallStub(jit, script, flags))
+                if (!generateFullCallStub(script, flags))
                     THROWV(NULL);
             }
         }
 
         return ucr.codeAddr;
     }
 };
 
@@ -1216,38 +1210,38 @@ ic::GenerateArgumentCheckStub(VMFrame &f
     Jump done = masm.jump();
 
     LinkerHelper linker(masm, JSC::METHOD_CODE);
     JSC::ExecutablePool *ep = linker.init(f.cx);
     if (!ep)
         return;
     jit->argsCheckPool = ep;
 
-    if (!linker.verifyRange(jit)) {
+    if (!linker.verifyRange(f.chunk())) {
         jit->resetArgsCheck();
         return;
     }
 
     for (unsigned i = 0; i < mismatches.length(); i++)
         linker.link(mismatches[i], jit->argsCheckStub);
     linker.link(done, jit->argsCheckFallthrough);
 
     JSC::CodeLocationLabel cs = linker.finalize(f);
 
     JaegerSpew(JSpew_PICs, "generated ARGS CHECK stub %p (%lu bytes)\n",
                cs.executableAddress(), (unsigned long)masm.size());
 
-    Repatcher repatch(jit);
+    Repatcher repatch(f.chunk());
     repatch.relink(jit->argsCheckJump, cs);
 }
 
 void
 JITScript::resetArgsCheck()
 {
     argsCheckPool->release();
     argsCheckPool = NULL;
 
-    Repatcher repatch(this);
+    Repatcher repatch(chunk(script->code));
     repatch.relink(argsCheckJump, argsCheckStub);
 }
 
 #endif /* JS_MONOIC */
 
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -123,17 +123,21 @@ class PICStubCompiler : public BaseCompi
         return error();
     }
 
     LookupStatus disable(const char *reason) {
         return disable(f.cx, reason);
     }
 
     LookupStatus disable(JSContext *cx, const char *reason) {
-        return pic.disable(cx, reason, stub);
+        return pic.disable(f, reason, stub);
+    }
+
+    LookupStatus disable(VMFrame &f, const char *reason) {
+        return pic.disable(f, reason, stub);
     }
 
     bool hadGC() {
         return gcNumber != f.cx->runtime->gcNumber;
     }
 
   protected:
     void spew(const char *event, const char *op) {
@@ -215,17 +219,17 @@ class SetPropCompiler : public PICStubCo
         repatcher.relink(pic.slowPathCall, target);
     }
 
     LookupStatus patchInline(const Shape *shape)
     {
         JS_ASSERT(!pic.inlinePathPatched);
         JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress());
 
-        Repatcher repatcher(f.jit());
+        Repatcher repatcher(f.chunk());
         SetPropLabels &labels = pic.setPropLabels();
 
         int32_t offset;
         if (obj->isFixedSlot(shape->slot())) {
             CodeLocationInstruction istr = labels.getDslotsLoad(pic.fastPathRejoin, pic.u.vr);
             repatcher.repatchLoadPtrToLEA(istr);
 
             //
@@ -254,17 +258,17 @@ class SetPropCompiler : public PICStubCo
     }
 
     int getLastStubSecondShapeGuard() const {
         return lastStubSecondShapeGuard ? POST_INST_OFFSET(lastStubSecondShapeGuard) : 0;
     }
 
     void patchPreviousToHere(CodeLocationLabel cs)
     {
-        Repatcher repatcher(pic.lastCodeBlock(f.jit()));
+        Repatcher repatcher(pic.lastCodeBlock(f.chunk()));
         CodeLocationLabel label = pic.lastPathStart();
 
         // Patch either the inline fast path or a generated stub. The stub
         // omits the prefix of the inline fast path that loads the shape, so
         // the offsets are different.
         if (pic.stubsGenerated) {
             repatcher.relink(pic.setPropLabels().getStubShapeJump(label), cs);
         } else {
@@ -432,24 +436,24 @@ class SetPropCompiler : public PICStubCo
             for (Jump *pj = otherGuards.begin(); pj != otherGuards.end(); ++pj)
                 pj->linkTo(masm.label(), &masm);
             slowExit = masm.jump();
             pic.secondShapeGuard = masm.distanceOf(masm.label()) - masm.distanceOf(start);
         } else {
             pic.secondShapeGuard = 0;
         }
 
-        pic.updatePCCounters(cx, masm);
+        pic.updatePCCounters(f, masm);
 
         PICLinker buffer(masm, pic);
         if (!buffer.init(cx))
             return error();
 
-        if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
-            !buffer.verifyRange(f.jit())) {
+        if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) ||
+            !buffer.verifyRange(f.chunk())) {
             return disable("code memory is out of range");
         }
 
         buffer.link(shapeGuard, pic.slowPathStart);
         if (slowExit.isSet())
             buffer.link(slowExit.get(), pic.slowPathStart);
         for (Jump *pj = slowExits.begin(); pj != slowExits.end(); ++pj)
             buffer.link(*pj, pic.slowPathStart);
@@ -734,76 +738,78 @@ struct GetPropHelper {
 
     GetPropHelper(JSContext *cx, JSObject *obj, PropertyName *name, IC &ic, VMFrame &f)
       : cx(cx), obj(obj), name(name), ic(ic), f(f), holder(NULL), prop(NULL), shape(NULL)
     { }
 
   public:
     LookupStatus bind() {
         RecompilationMonitor monitor(cx);
-        bool global = (js_CodeSpec[*f.pc()].format & JOF_GNAME);
-        if (!FindProperty(cx, name, global, &obj, &holder, &prop))
+        JSObject *scopeChain = cx->stack.currentScriptedScopeChain();
+        if (js_CodeSpec[*f.pc()].format & JOF_GNAME)
+            scopeChain = &scopeChain->global();
+        if (!FindProperty(cx, name, scopeChain, &obj, &holder, &prop))
             return ic.error(cx);
         if (monitor.recompiled())
             return Lookup_Uncacheable;
         if (!prop)
             return ic.disable(cx, "lookup failed");
         if (!obj->isNative())
             return ic.disable(cx, "non-native");
         if (!IsCacheableProtoChain(obj, holder))
             return ic.disable(cx, "non-native holder");
         shape = (const Shape *)prop;
         return Lookup_Cacheable;
     }
 
     LookupStatus lookup() {
         JSObject *aobj = js_GetProtoIfDenseArray(obj);
         if (!aobj->isNative())
-            return ic.disable(cx, "non-native");
+            return ic.disable(f, "non-native");
 
         RecompilationMonitor monitor(cx);
         if (!aobj->lookupProperty(cx, name, &holder, &prop))
             return ic.error(cx);
         if (monitor.recompiled())
             return Lookup_Uncacheable;
 
         if (!prop)
-            return ic.disable(cx, "lookup failed");
+            return ic.disable(f, "lookup failed");
         if (!IsCacheableProtoChain(obj, holder))
-            return ic.disable(cx, "non-native holder");
+            return ic.disable(f, "non-native holder");
         shape = (const Shape *)prop;
         return Lookup_Cacheable;
     }
 
     LookupStatus testForGet() {
         if (!shape->hasDefaultGetter()) {
             if (shape->isMethod()) {
                 if (JSOp(*f.pc()) != JSOP_CALLPROP)
-                    return ic.disable(cx, "method valued shape");
+                    return ic.disable(f, "method valued shape");
             } else {
                 if (shape->hasGetterValue())
-                    return ic.disable(cx, "getter value shape");
+                    return ic.disable(f, "getter value shape");
                 if (shape->hasSlot() && holder != obj)
-                    return ic.disable(cx, "slotful getter hook through prototype");
+                    return ic.disable(f, "slotful getter hook through prototype");
                 if (!ic.canCallHook)
-                    return ic.disable(cx, "can't call getter hook");
+                    return ic.disable(f, "can't call getter hook");
                 if (f.regs.inlined()) {
                     /*
                      * As with native stubs, getter hook stubs can't be
                      * generated for inline frames. Mark the inner function
                      * as uninlineable and recompile.
                      */
                     f.script()->uninlineable = true;
                     MarkTypeObjectFlags(cx, f.script()->function(),
                                         types::OBJECT_FLAG_UNINLINEABLE);
                     return Lookup_Uncacheable;
                 }
             }
         } else if (!shape->hasSlot()) {
-            return ic.disable(cx, "no slot");
+            return ic.disable(f, "no slot");
         }
 
         return Lookup_Cacheable;
     }
 
     LookupStatus lookupAndTest() {
         LookupStatus status = lookup();
         if (status != Lookup_Cacheable)
@@ -860,24 +866,24 @@ class GetPropCompiler : public PICStubCo
         masm.move(pic.objReg, pic.shapeReg);
         Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg,
                                             Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT));
         masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), pic.objReg);
 
         masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
         Jump done = masm.jump();
 
-        pic.updatePCCounters(cx, masm);
+        pic.updatePCCounters(f, masm);
 
         PICLinker buffer(masm, pic);
         if (!buffer.init(cx))
             return error();
 
-        if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
-            !buffer.verifyRange(f.jit())) {
+        if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) ||
+            !buffer.verifyRange(f.chunk())) {
             return disable("code memory is out of range");
         }
 
         buffer.link(notArgs, pic.slowPathStart);
         buffer.link(overridden, pic.slowPathStart);
         buffer.link(done, pic.fastPathRejoin);
 
         CodeLocationLabel start = buffer.finalize(f);
@@ -901,24 +907,24 @@ class GetPropCompiler : public PICStubCo
 
         isDense.linkTo(masm.label(), &masm);
         masm.loadPtr(Address(pic.objReg, JSObject::offsetOfElements()), pic.objReg);
         masm.load32(Address(pic.objReg, ObjectElements::offsetOfLength()), pic.objReg);
         Jump oob = masm.branch32(Assembler::Above, pic.objReg, Imm32(JSVAL_INT_MAX));
         masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
         Jump done = masm.jump();
 
-        pic.updatePCCounters(cx, masm);
+        pic.updatePCCounters(f, masm);
 
         PICLinker buffer(masm, pic);
         if (!buffer.init(cx))
             return error();
 
-        if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
-            !buffer.verifyRange(f.jit())) {
+        if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) ||
+            !buffer.verifyRange(f.chunk())) {
             return disable("code memory is out of range");
         }
 
         buffer.link(notArray, pic.slowPathStart);
         buffer.link(oob, pic.slowPathStart);