#jsapi observed the extent of orange in the TM tree and saw that OS X x86 opt was consistently and totally evil. So jwalden was sorry he had ever pushed changes for bug 631135. It broke his heart. And he said, “I will wipe this tree of every push back to 9105cd721d46. Yes, and I will revert the entire TM CLOSED TREE. I am sorry I ever pushed to it.” But 9105cd721d46 found favor with #jsapi.
authorJeff Walden <jwalden@mit.edu>
Sun, 20 Feb 2011 17:46:35 -0800
changeset 62959 92aa2e2f4c63a57b8ca252bbd09d765b4a8a094e
parent 62958 335dbb9502e0240c2ffa30cce40f08343304a2fe
child 62960 35ddad724c32c7771824267220eecc266b8bae9a
push id1
push userroot
push dateTue, 10 Dec 2013 15:46:25 +0000
bugs631135
milestone2.0b12pre
#jsapi observed the extent of orange in the TM tree and saw that OS X x86 opt was consistently and totally evil. So jwalden was sorry he had ever pushed changes for bug 631135. It broke his heart. And he said, “I will wipe this tree of every push back to 9105cd721d46. Yes, and I will revert the entire TM CLOSED TREE. I am sorry I ever pushed to it.” But 9105cd721d46 found favor with #jsapi.
dom/base/nsJSEnvironment.cpp
js/src/jit-test/tests/basic/testBug634590.js
js/src/jit-test/tests/basic/testBug634590b.js
js/src/jit-test/tests/basic/testBug634590c.js
js/src/jit-test/tests/basic/testBug634590d.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.h
js/src/jsfuninlines.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsscan.cpp
js/src/jsscan.h
js/src/jstracer.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3360,16 +3360,18 @@ nsJSContext::PokeGC()
     // There's already a timer for GC'ing, just return
     return;
   }
 
   CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
 
   if (!sGCTimer) {
     NS_WARNING("Failed to create timer");
+
+    GarbageCollectNow();
     return;
   }
 
   static PRBool first = PR_TRUE;
 
   sGCTimer->InitWithFuncCallback(GCTimerFired, nsnull,
                                  first
                                  ? NS_FIRST_GC_DELAY
@@ -3396,16 +3398,18 @@ nsJSContext::PokeCC()
     // There's already a timer for GC'ing, just return
     return;
   }
 
   CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
 
   if (!sCCTimer) {
     NS_WARNING("Failed to create timer");
+
+    CycleCollectNow();
     return;
   }
 
   sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull,
                                  NS_CC_DELAY,
                                  nsITimer::TYPE_ONE_SHOT);
 }
 
@@ -3475,22 +3479,16 @@ DOMGCCallback(JSContext *cx, JSGCStatus 
         nsJSContext::KillCCTimer();
       }
     } else {
       // If this was a full GC, poke the CC to run soon.
       if (!cx->runtime->gcTriggerCompartment) {
         nsJSContext::PokeCC();
       }
     }
-
-    // If we didn't end up scheduling a GC, and there are unused
-    // chunks waiting to expire, make sure we will GC again soon.
-    if (!sGCTimer && JS_GetGCParameter(cx->runtime, JSGC_UNUSED_CHUNKS) > 0) {
-      nsJSContext::PokeGC();
-    }
   }
 
   JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
 
   if (status == JSGC_BEGIN && !NS_IsMainThread())
     return JS_FALSE;
 
   return result;
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testBug634590.js
+++ /dev/null
@@ -1,12 +0,0 @@
-this.name = "outer";
-var sb = evalcx('');
-sb.name = "inner";
-sb.parent = this;
-function f() { return this.name; }
-assertEq(evalcx('this.f = parent.f;\n' +
-                'var s = "";\n' +
-                'for (i = 0; i < 10; ++i)\n' +
-                '  s += f();\n' +
-                's',
-                sb),
-	 "innerinnerinnerinnerinnerinnerinnerinnerinnerinner");
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testBug634590b.js
+++ /dev/null
@@ -1,19 +0,0 @@
-this.name = "outer";
-var sb = evalcx('');
-sb.name = "inner";
-sb.parent = this;
-function f() { return this.name; }
-f.notMuchTodo = '42';
-assertEq(evalcx('{\n' +
-                '  let f = parent.f;\n' +
-                '  let name = "block";\n' +
-                '  (function () {\n' +
-                '    eval(f.notMuchTodo);\n' + // reify Block
-                '    var s = "";\n' +
-                '    for (i = 0; i < 10; ++i)\n' +
-                '      s += f();\n' +
-                '    return s;\n' +
-                '  })();\n' +
-                '}',
-                sb),
-	 "outerouterouterouterouterouterouterouterouterouter");
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testBug634590c.js
+++ /dev/null
@@ -1,20 +0,0 @@
-this.name = "outer";
-var sb = evalcx('');
-sb.name = "inner";
-sb.parent = this;
-function f() { return this.name; }
-f.notMuchTodo = '42';
-assertEq(evalcx('(function () {\n' +
-                '  arguments = null;\n' + // force heavyweight
-                '  var f = parent.f;\n' +
-                '  var name = "call";\n' +
-                '  return (function () {\n' +
-                '    eval(f.notMuchTodo);\n' + // reify Call, make f() compile to JSOP_CALLNAME
-                '    var s = "";\n' +
-                '    for (i = 0; i < 10; ++i)\n' +
-                '      s += f();\n' +
-                '    return s;\n' +
-                '  })();\n' +
-                '})()',
-                sb),
-	 "outerouterouterouterouterouterouterouterouterouter");
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testBug634590d.js
+++ /dev/null
@@ -1,15 +0,0 @@
-this.name = "outer";
-var sb = evalcx('');
-sb.name = "inner";
-sb.parent = this;
-this.f = function name(outer) {
-    if (outer) return name(false);
-    return this.name;
-}
-assertEq(evalcx('this.f = parent.f;\n' +
-                'var s = "";\n' +
-                'for (i = 0; i < 10; ++i)\n' +
-                '  s += f(true);\n' +
-                's',
-                sb),
-	 "outerouterouterouterouterouterouterouterouterouter");
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2639,18 +2639,16 @@ JS_GetGCParameter(JSRuntime *rt, JSGCPar
       case JSGC_STACKPOOL_LIFESPAN:
         return rt->gcEmptyArenaPoolLifespan;
       case JSGC_TRIGGER_FACTOR:
         return rt->gcTriggerFactor;
       case JSGC_BYTES:
         return rt->gcBytes;
       case JSGC_MODE:
         return uint32(rt->gcMode);
-      case JSGC_UNUSED_CHUNKS:
-        return uint32(rt->gcChunksWaitingToExpire);
       default:
         JS_ASSERT(key == JSGC_NUMBER);
         return rt->gcNumber;
     }
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32 value)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1780,20 +1780,17 @@ typedef enum JSGCParamKey {
 
     /* Number of times when GC was invoked. */
     JSGC_NUMBER = 5,
 
     /* Max size of the code cache in bytes. */
     JSGC_MAX_CODE_CACHE_BYTES = 6,
 
     /* Select GC mode. */
-    JSGC_MODE = 7,
-
-    /* Number of GC chunks waiting to expire. */
-    JSGC_UNUSED_CHUNKS = 8
+    JSGC_MODE = 7
 } JSGCParamKey;
 
 typedef enum JSGCMode {
     /* Perform only global GCs. */
     JSGC_MODE_GLOBAL = 0,
 
     /* Perform per-compartment GCs until too much garbage has accumulated. */
     JSGC_MODE_COMPARTMENT = 1
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1042,17 +1042,16 @@ struct JSRuntime {
     js::RootedValueMap  gcRootsHash;
     js::GCLocks         gcLocksHash;
     jsrefcount          gcKeepAtoms;
     size_t              gcBytes;
     size_t              gcTriggerBytes;
     size_t              gcLastBytes;
     size_t              gcMaxBytes;
     size_t              gcMaxMallocBytes;
-    size_t              gcChunksWaitingToExpire;
     uint32              gcEmptyArenaPoolLifespan;
     uint32              gcNumber;
     js::GCMarker        *gcMarkingTracer;
     uint32              gcTriggerFactor;
     int64               gcJitReleaseTime;
     JSGCMode            gcMode;
     volatile bool       gcIsNeeded;
 
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -44,40 +44,9 @@
 #include "jsscript.h"
 
 inline bool
 JSFunction::inStrictMode() const
 {
     return script()->strictModeCode;
 }
 
-namespace js {
-
-static inline bool
-IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee)
-{
-    /*
-     * Look past any function wrappers. If the callee is a wrapped strict-mode
-     * function, lazy 'this' coercion is vacuously safe because strict-mode
-     * functions don't coerce 'this' at all. Otherwise, the callee is safe to
-     * transform into the lazy 'this' cookie (the undefined value) only if it
-     * is in the current scope.
-     *
-     * Without this restriction, lazy 'this' coercion would pick up the "wrong"
-     * global at the end of the callee function object's scope, rather than the
-     * "right" (backward compatible since 1995) global that was the "Reference
-     * base object" in the callee expression.
-     */
-    if (callee->isProxy()) {
-        callee = callee->unwrap();
-        if (!callee->isFunction())
-            return true; // treat any non-wrapped-function proxy as strict
-
-        JSFunction *fun = callee->getFunctionPrivate();
-        if (fun->isInterpreted() && fun->inStrictMode())
-            return true;
-    }
-    return callee->getGlobal() == cx->fp()->scopeChain().getGlobal();
-}
-
-}
-
 #endif /* jsfuninlines_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -355,16 +355,24 @@ Chunk::releaseArena(Arena<T> *arena)
     comp->gcBytes -= sizeof(Arena<T>);
     info.emptyArenaLists.insert((Arena<Cell> *)arena);
     arena->header()->isUsed = false;
     ++info.numFree;
     if (unused())
         info.age = 0;
 }
 
+bool
+Chunk::expire()
+{
+    if (!unused())
+        return false;
+    return info.age++ > MaxAge;
+}
+
 JSRuntime *
 Chunk::getRuntime()
 {
     return info.runtime;
 }
 
 inline jsuword
 GetGCChunk(JSRuntime *rt)
@@ -443,32 +451,26 @@ PickChunk(JSRuntime *rt)
     chunk->init(rt);
 
     return chunk;
 }
 
 static void
 ExpireGCChunks(JSRuntime *rt)
 {
-    static const size_t MaxAge = 3;
-
     /* Remove unused chunks. */
     AutoLockGC lock(rt);
 
-    rt->gcChunksWaitingToExpire = 0;
     for (GCChunkSet::Enum e(rt->gcChunkSet); !e.empty(); e.popFront()) {
         Chunk *chunk = e.front();
         JS_ASSERT(chunk->info.runtime == rt);
-        if (chunk->unused()) {
-            if (chunk->info.age++ > MaxAge) {
-                e.removeFront();
-                ReleaseGCChunk(rt, chunk);
-                continue;
-            }
-            rt->gcChunksWaitingToExpire++;
+        if (chunk->expire()) {
+            e.removeFront();
+            ReleaseGCChunk(rt, chunk);
+            continue;
         }
     }
 }
 
 template <typename T>
 static Arena<T> *
 AllocateArena(JSContext *cx, unsigned thingKind)
 {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -335,16 +335,17 @@ struct ChunkInfo {
 
 /* Chunks contain arenas and associated data structures (mark bitmap, delayed marking state). */
 struct Chunk {
     static const size_t BytesPerArena = sizeof(Arena<FreeCell>) +
                                         sizeof(ArenaBitmap) +
                                         sizeof(MarkingDelay);
 
     static const size_t ArenasPerChunk = (GC_CHUNK_SIZE - sizeof(ChunkInfo)) / BytesPerArena;
+    static const size_t MaxAge = 3;
 
     Arena<FreeCell> arenas[ArenasPerChunk];
     ArenaBitmap     bitmaps[ArenasPerChunk];
     MarkingDelay    markingDelay[ArenasPerChunk];
 
     ChunkInfo       info;
 
     void clearMarkBitmap();
@@ -356,16 +357,17 @@ struct Chunk {
 
     template <typename T>
     Arena<T> *allocateArena(JSCompartment *comp, unsigned thingKind);
 
     template <typename T>
     void releaseArena(Arena<T> *a);
 
     JSRuntime *getRuntime();
+    bool expire();
 };
 JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE);
 JS_STATIC_ASSERT(sizeof(Chunk) + Chunk::BytesPerArena > GC_CHUNK_SIZE);
 
 Arena<Cell> *
 Cell::arena() const
 {
     uintptr_t addr = uintptr_t(this);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2173,41 +2173,16 @@ ScriptPrologue(JSContext *cx, JSStackFra
         if (JSInterpreterHook hook = cx->debugHooks->callHook)
             fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
         Probes::enterJSFun(cx, fp->maybeFun(), fp->maybeScript());
     }
 
     return true;
 }
 
-static inline bool
-ComputeThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
-{
-    if (!funval.isObject() ||
-        (obj->isGlobal()
-         ? IsSafeForLazyThisCoercion(cx, &funval.toObject())
-         : IsCacheableNonGlobalScope(obj))) {
-        /*
-         * We can avoid computing 'this' eagerly and push the implicit 'this'
-         * value (undefined), as long the scope is cachable and we are not
-         * crossing into another scope (in which case lazy calculation of 'this'
-         * would pick up the new and incorrect scope). 'strict' functions are an
-         * exception. We don't want to eagerly calculate 'this' for them even if
-         * the callee is in a different scope.
-         */
-        *vp = UndefinedValue();
-        return true;
-    }
-
-    if (!(obj = obj->thisObject(cx)))
-        return false;
-    *vp = ObjectValue(*obj);
-    return true;
-}
-
 namespace js {
 
 JS_REQUIRES_STACK JS_NEVER_INLINE bool
 Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInterpMode interpMode)
 {
 #ifdef MOZ_TRACEVIS
     TraceVisStateObj tvso(cx, S_INTERP);
 #endif
@@ -2223,18 +2198,18 @@ Interpret(JSContext *cx, JSStackFrame *e
      * their work, and many opcodes share entry points with a run of
      * consecutive BEGIN_CASEs.
      *
      * Take care to trace OP only when it is the opcode fetched from
      * the instruction stream, so the trace matches what one would
      * expect from looking at the code.  (We do omit POPs after SETs;
      * unfortunate, but not worth fixing.)
      */
-#  define LOG_OPCODE(OP)    JS_BEGIN_MACRO                                    \
-                                if (JS_UNLIKELY(cx->logfp != NULL) &&         \
+#  define LOG_OPCODE(OP)    JS_BEGIN_MACRO                                      \
+                                if (JS_UNLIKELY(cx->logfp != NULL) &&       \
                                     (OP) == *regs.pc)                         \
                                     js_LogOpcode(cx);                         \
                             JS_END_MACRO
 # else
 #  define LOG_OPCODE(OP)    ((void) 0)
 # endif
 
     /*
@@ -4812,23 +4787,36 @@ END_CASE(JSOP_CALL)
 
 BEGIN_CASE(JSOP_SETCALL)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
     goto error;
 }
 END_CASE(JSOP_SETCALL)
 
-#define PUSH_THISV(cx, obj, funval)                                           \
-    JS_BEGIN_MACRO                                                            \
-        Value v;                                                              \
-        if (!ComputeThis(cx, obj, funval, &v))                                \
-            goto error;                                                       \
-        PUSH_COPY(v);                                                         \
-    JS_END_MACRO                                                              \
+#define SLOW_PUSH_THISV(cx, obj)                                            \
+    JS_BEGIN_MACRO                                                          \
+        Class *clasp;                                                       \
+        JSObject *thisp = obj;                                              \
+        if (!thisp->getParent() ||                                          \
+            (clasp = thisp->getClass()) == &js_CallClass ||                 \
+            clasp == &js_BlockClass ||                                      \
+            clasp == &js_DeclEnvClass) {                                    \
+            /* Push the ImplicitThisValue for the Environment Record */     \
+            /* associated with obj. See ES5 sections 10.2.1.1.6 and  */     \
+            /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3     */     \
+            /* (Function Calls). */                                         \
+            PUSH_UNDEFINED();                                               \
+        } else {                                                            \
+            thisp = thisp->thisObject(cx);                                  \
+            if (!thisp)                                                     \
+                goto error;                                                 \
+            PUSH_OBJECT(*thisp);                                            \
+        }                                                                   \
+    JS_END_MACRO
 
 BEGIN_CASE(JSOP_GETGNAME)
 BEGIN_CASE(JSOP_CALLGNAME)
 BEGIN_CASE(JSOP_NAME)
 BEGIN_CASE(JSOP_CALLNAME)
 {
     JSObject *obj = &regs.fp->scopeChain();
 
@@ -4848,19 +4836,30 @@ BEGIN_CASE(JSOP_CALLNAME)
             PUSH_COPY(obj2->nativeGetSlot(slot));
         } else {
             JS_ASSERT(entry->vword.isShape());
             shape = entry->vword.toShape();
             NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval);
             PUSH_COPY(rval);
         }
 
-        JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
+        /*
+         * Push results, the same as below, but with a prop$ hit there
+         * is no need to test for the unusual and uncacheable case where
+         * the caller determines |this|.
+         */
+#if DEBUG
+        Class *clasp;
+        JS_ASSERT(!obj->getParent() ||
+                  (clasp = obj->getClass()) == &js_CallClass ||
+                  clasp == &js_BlockClass ||
+                  clasp == &js_DeclEnvClass);
+#endif
         if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
-            PUSH_THISV(cx, obj, regs.sp[-1]);
+            PUSH_UNDEFINED();
         len = JSOP_NAME_LENGTH;
         DO_NEXT_OP(len);
     }
 
     jsid id;
     id = ATOM_TO_JSID(atom);
     JSProperty *prop;
     if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
@@ -4888,17 +4887,17 @@ BEGIN_CASE(JSOP_CALLNAME)
             normalized = js_UnwrapWithObject(cx, normalized);
         NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval);
     }
 
     PUSH_COPY(rval);
 
     /* obj must be on the scope chain, thus not a function. */
     if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
-        PUSH_THISV(cx, obj, rval);
+        SLOW_PUSH_THISV(cx, obj);
 }
 END_CASE(JSOP_NAME)
 
 BEGIN_CASE(JSOP_UINT16)
     PUSH_INT32((int32_t) GET_UINT16(regs.pc));
 END_CASE(JSOP_UINT16)
 
 BEGIN_CASE(JSOP_UINT24)
@@ -6391,17 +6390,17 @@ BEGIN_CASE(JSOP_XMLNAME)
     jsid id;
     if (!js_FindXMLProperty(cx, lval, &obj, &id))
         goto error;
     Value rval;
     if (!obj->getProperty(cx, id, &rval))
         goto error;
     regs.sp[-1] = rval;
     if (op == JSOP_CALLXMLNAME)
-        PUSH_THISV(cx, obj, rval);
+        SLOW_PUSH_THISV(cx, obj);
 }
 END_CASE(JSOP_XMLNAME)
 
 BEGIN_CASE(JSOP_DESCENDANTS)
 BEGIN_CASE(JSOP_DELDESC)
 {
     JSObject *obj;
     FETCH_OBJECT(cx, -2, obj);
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -474,17 +474,17 @@ JSStackFrame::clearCallObj()
 }
 
 inline JSObject &
 JSStackFrame::callObj() const
 {
     JS_ASSERT(hasCallObj());
     JSObject *pobj = &scopeChain();
     while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) {
-        JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith());
+        JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith());
         pobj = pobj->getParent();
     }
     return *pobj;
 }
 
 inline JSObject *
 JSStackFrame::maybeCallObj() const
 {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4440,21 +4440,16 @@ JSObject::freeSlot(JSContext *cx, uint32
             last = slot;
             return true;
         }
     }
     vref.setUndefined();
     return false;
 }
 
-namespace js {
-
-
-}
-
 /* JSBOXEDWORD_INT_MAX as a string */
 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
 
 /*
  * Convert string indexes that convert to int jsvals as ints to save memory.
  * Care must be taken to use this macro every time a property name is used, or
  * else double-sets, incorrect property cache misses, or other mistakes could
  * occur.
@@ -5052,17 +5047,17 @@ js_FindPropertyHelper(JSContext *cx, jsi
     scopeChain = &js_GetTopStackFrame(cx)->scopeChain();
 
     /* Scan entries on the scope chain that we can cache across. */
     entry = JS_NO_PROP_CACHE_FILL;
     obj = scopeChain;
     parent = obj->getParent();
     for (scopeIndex = 0;
          parent
-         ? IsCacheableNonGlobalScope(obj)
+         ? js_IsCacheableNonGlobalScope(obj)
          : !obj->getOps()->lookupProperty;
          ++scopeIndex) {
         protoIndex =
             js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
                                        &pobj, &prop);
         if (protoIndex < 0)
             return NULL;
 
@@ -5162,21 +5157,21 @@ js_FindIdentifierBase(JSContext *cx, JSO
     JSObject *obj = scopeChain;
 
     /*
      * Loop over cacheable objects on the scope chain until we find a
      * property. We also stop when we reach the global object skipping any
      * farther checks or lookups. For details see the JSOP_BINDNAME case of
      * js_Interpret.
      *
-     * The test order here matters because IsCacheableNonGlobalScope
+     * The test order here matters because js_IsCacheableNonGlobalScope
      * must not be passed a global object (i.e. one with null parent).
      */
     for (int scopeIndex = 0;
-         !obj->getParent() || IsCacheableNonGlobalScope(obj);
+         !obj->getParent() || js_IsCacheableNonGlobalScope(obj);
          scopeIndex++) {
         JSObject *pobj;
         JSProperty *prop;
         int protoIndex = js_LookupPropertyWithFlags(cx, obj, id,
                                                     cx->resolveFlags,
                                                     &pobj, &prop);
         if (protoIndex < 0)
             return NULL;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1656,42 +1656,37 @@ js_DefineNativeProperty(JSContext *cx, J
  * returns the index along the prototype chain in which *propp was found, or
  * the last index if not found, or -1 on error.
  */
 extern int
 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
                            JSObject **objp, JSProperty **propp);
 
 
-extern JS_FRIEND_DATA(js::Class) js_CallClass;
-extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
-
-namespace js {
-
 /*
  * We cache name lookup results only for the global object or for native
  * non-global objects without prototype or with prototype that never mutates,
  * see bug 462734 and bug 487039.
  */
-static inline bool
-IsCacheableNonGlobalScope(JSObject *obj)
+inline bool
+js_IsCacheableNonGlobalScope(JSObject *obj)
 {
+    extern JS_FRIEND_DATA(js::Class) js_CallClass;
+    extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
     JS_ASSERT(obj->getParent());
 
     js::Class *clasp = obj->getClass();
     bool cacheable = (clasp == &js_CallClass ||
                       clasp == &js_BlockClass ||
                       clasp == &js_DeclEnvClass);
 
     JS_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty);
     return cacheable;
 }
 
-}
-
 /*
  * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success.
  */
 extern js::PropertyCacheEntry *
 js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
                       JSObject **objp, JSObject **pobjp, JSProperty **propp);
 
 /*
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -44,29 +44,27 @@
 #include <new>
 #include "jsdate.h"
 #include "jsfun.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsobj.h"
 #include "jsprobes.h"
 #include "jspropertytree.h"
-#include "jsproxy.h"
 #include "jsscope.h"
 #include "jsstaticcheck.h"
 #include "jsxml.h"
 
 /* Headers included for inline implementations used by this header. */
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsnum.h"
 #include "jsscopeinlines.h"
 #include "jsstr.h"
 
-#include "jsfuninlines.h"
 #include "jsgcinlines.h"
 #include "jsprobes.h"
 
 inline bool
 JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props)
 {
     JS_ASSERT(isExtensible());
 
--- a/js/src/jsscan.cpp
+++ b/js/src/jsscan.cpp
@@ -366,52 +366,70 @@ TokenStream::peekChars(intN n, jschar *c
         }
         cp[i] = (jschar)c;
     }
     for (j = i - 1; j >= 0; j--)
         ungetChar(cp[j]);
     return i == n;
 }
 
+/* Finds the next EOL, but stops once 'max' chars past the start of the token have been scanned. */
 jschar *
-TokenStream::findEOL()
+TokenStream::findWlineLimit(jschar *tokptr, int max)
 {
     TokenBuf tmpUserbuf = userbuf;
     jschar *tmpLinebase = linebase;
     jschar *tmpPrevLinebase = prevLinebase;
     uintN tmpFlags = flags;
     uintN tmpLineno = lineno;
 
+    /* FIXME: this assertion is commented out due to bug 635144. */
+    /*JS_ASSERT(userbuf.base <= tokptr && tokptr <= userbuf.limit);*/
+    userbuf.ptr = tokptr;   /* Start scanning at tokptr. */
+
+    jschar *wlinelimit;
+    jschar *wlinelimitmax = tokptr + max + 1;
     while (true) {
+        if (userbuf.ptr > wlinelimitmax) {
+            wlinelimit = wlinelimitmax;
+            break;
+        }
+        jschar* next = userbuf.ptr;
         int32 c = getChar();
-        if (c == '\n' || c == EOF)
+        if (c == '\n' || c == EOF) {
+            wlinelimit = next;
             break;
+        }
     }
-    jschar *linelimit = userbuf.ptr;
 
     /* Need to restore everything changed by getChar(). */
     userbuf = tmpUserbuf;
     linebase = tmpLinebase;
     prevLinebase = tmpPrevLinebase;
     flags = tmpFlags;
     lineno = tmpLineno;
 
-    return linelimit;
+    return wlinelimit;
 }
 
 bool
 TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN errorNumber,
                                         va_list ap)
 {
     JSErrorReport report;
     char *message;
-    size_t linelength;
-    jschar *linechars;
-    jschar *linelimit;
-    char *linebytes;
+
+    /* "wline" is short for "window into line", because we might not show it all. */
+    size_t wlinelength;
+    jschar *wlinechars;
+    jschar *wlinelimit;
+    jschar *wlinebase;
+    jschar *tokptr;
+    char *wlinebytes;
+
     bool warning;
     JSBool ok;
     TokenPos *tp;
     uintN index, i;
     JSErrorReporter onError;
 
     if (JSREPORT_IS_STRICT(flags) && !cx->hasStrictOption())
         return JS_TRUE;
@@ -421,18 +439,18 @@ TokenStream::reportCompileErrorNumberVA(
         flags &= ~JSREPORT_WARNING;
         warning = false;
     }
 
     PodZero(&report);
     report.flags = flags;
     report.errorNumber = errorNumber;
     message = NULL;
-    linechars = NULL;
-    linebytes = NULL;
+    wlinechars = NULL;
+    wlinebytes = NULL;
 
     MUST_FLOW_THROUGH("out");
     ok = js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL,
                                  errorNumber, &message, &report,
                                  !(flags & JSREPORT_UC), ap);
     if (!ok) {
         warning = false;
         goto out;
@@ -446,39 +464,59 @@ TokenStream::reportCompileErrorNumberVA(
             goto report;
         tp = &pn->pn_pos;
     } else {
         /* Point to the current token, not the next one to get. */
         tp = &tokens[cursor].pos;
     }
     report.lineno = lineno;
 
-    linelimit = findEOL();
-    linelength = linelimit - linebase;
+    index = (tp->begin.lineno == tp->end.lineno) 
+            ? tp->begin.index   /* the column number of the start of the bad token */
+            : 0;                /* the bad token didn't start on this line; don't give a column */
+    tokptr = linebase + index;
+
+    /*
+     * We show only a portion of the line around the erroneous token -- WINDOW
+     * chars before and after the first char in the token.  This is because
+     * lines can be very long and printing the whole line is (a) not that
+     * helpful, and (b) can waste a lot of memory.  See bug 634444.
+     */
+    static const size_t WINDOW = 100;
 
-    linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar));
-    if (!linechars) {
+    /* Truncate at the front if necessary. */
+    if (linebase + WINDOW < tokptr) {
+        wlinebase = tokptr - WINDOW;
+        size_t nTrunc = wlinebase - linebase;
+        index -= nTrunc;
+    } else {
+        wlinebase = linebase;
+    }
+
+    /* Find EOL, or truncate at the back if necessary. */
+    wlinelimit = findWlineLimit(tokptr, WINDOW);
+
+    wlinelength = wlinelimit - wlinebase;
+    JS_ASSERT(wlinelength <= WINDOW * 2 + 1);
+    wlinechars = (jschar *)cx->malloc((wlinelength + 1) * sizeof(jschar));
+    if (!wlinechars) {
         warning = false;
         goto out;
     }
-    memcpy(linechars, linebase, linelength * sizeof(jschar));
-    linechars[linelength] = 0;
-    linebytes = js_DeflateString(cx, linechars, linelength);
-    if (!linebytes) {
+    memcpy(wlinechars, wlinebase, wlinelength * sizeof(jschar));
+    wlinechars[wlinelength] = 0;
+    wlinebytes = js_DeflateString(cx, wlinechars, wlinelength);
+    if (!wlinebytes) {
         warning = false;
         goto out;
     }
-    report.linebuf = linebytes;     /* the offending source line, without final \n */
-
-    index = (tp->begin.lineno == tp->end.lineno) 
-            ? tp->begin.index         /* the column number of the start of the bad token */
-            : 0;
+    report.linebuf = wlinebytes;    /* some or all of the offending source line, without final \n */
 
     report.tokenptr = report.linebuf + index;
-    report.uclinebuf = linechars;
+    report.uclinebuf = wlinechars;
     report.uctokenptr = report.uclinebuf + index;
 
     /*
      * If there's a runtime exception type associated with this error
      * number, set that as the pending exception.  For errors occuring at
      * compile time, this is very likely to be a JSEXN_SYNTAXERR.
      *
      * If an exception is thrown but not caught, the JSREPORT_EXCEPTION
@@ -525,20 +563,20 @@ TokenStream::reportCompileErrorNumberVA(
                           cx->debugHooks->debugErrorHookData)) {
             onError = NULL;
         }
     }
     if (onError)
         (*onError)(cx, message, &report);
 
   out:
-    if (linebytes)
-        cx->free(linebytes);
-    if (linechars)
-        cx->free(linechars);
+    if (wlinebytes)
+        cx->free(wlinebytes);
+    if (wlinechars)
+        cx->free(wlinechars);
     if (message)
         cx->free(message);
     if (report.ucmessage)
         cx->free((void *)report.ucmessage);
 
     if (report.messageArgs) {
         if (!(flags & JSREPORT_UC)) {
             i = 0;
@@ -767,16 +805,17 @@ TokenStream::matchUnicodeEscapeIdent(int
 }
 
 Token *
 TokenStream::newToken(ptrdiff_t adjust)
 {
     cursor = (cursor + 1) & ntokensMask;
     Token *tp = &tokens[cursor];
     tp->ptr = userbuf.ptr + adjust;
+    JS_ASSERT(tp->ptr >= linebase);
     tp->pos.begin.index = tp->ptr - linebase;
     tp->pos.begin.lineno = tp->pos.end.lineno = lineno;
     return tp;
 }
 
 static JS_ALWAYS_INLINE JSBool
 ScanAsSpace(jschar c)
 {
@@ -795,16 +834,17 @@ TokenStream::atomize(JSContext *cx, Char
 TokenKind
 TokenStream::getTokenInternal()
 {
     TokenKind tt;
     int c, qc;
     Token *tp;
     JSAtom *atom;
     bool hadUnicodeEscape;
+    int adjust;
 #if JS_HAS_XML_SUPPORT
     JSBool inTarget;
     size_t targetLength;
     ptrdiff_t contentIndex;
 #endif
 
 #if JS_HAS_XML_SUPPORT
     /*
@@ -852,17 +892,16 @@ TokenStream::getTokenInternal()
     if (flags & TSF_XMLTAGMODE) {
         tp = newToken(0);
         c = getChar();
         if (JS_ISXMLSPACE(c)) {
             do {
                 c = getChar();
             } while (JS_ISXMLSPACE(c));
             ungetChar(c);
-            tp->pos.end.lineno = lineno;
             tt = TOK_XMLSPACE;
             goto out;
         }
 
         if (c == EOF) {
             tt = TOK_EOF;
             goto out;
         }
@@ -971,32 +1010,38 @@ TokenStream::getTokenInternal()
             goto error;
         }
         /* NOTREACHED */
     }
 #endif /* JS_HAS_XML_SUPPORT */
 
   retry:
     /*
-     * This gets the next non-space char and starts the token.
+     * This gets the next non-space char and starts the token.  adjust is set
+     * to -1 because we'll probably scan the first char of the upcoming token
+     * while chewing up the whitespace.
      */
+    adjust = -1;   
     do {
         c = getChar();
         if (c == '\n') {
             flags &= ~TSF_DIRTYLINE;
-            if (flags & TSF_NEWLINES)
+            if (flags & TSF_NEWLINES) {
+                adjust = 0;     /* early break means we didn't scan an extra char */
                 break;
+            }
         }
     } while (ScanAsSpace((jschar)c));
 
-    tp = newToken(-1);
     if (c == EOF) {
+        tp = newToken(0);   /* no -1 here because userbuf.ptr isn't incremented for EOF */
         tt = TOK_EOF;
         goto out;
     }
+    tp = newToken(adjust);
 
     /*
      * Look for an identifier.
      */
 
     hadUnicodeEscape = false;
     if (JS_ISIDSTART(c) ||
         (c == '\\' && (hadUnicodeEscape = matchUnicodeEscapeIdStart(&qc))))
--- a/js/src/jsscan.h
+++ b/js/src/jsscan.h
@@ -471,17 +471,17 @@ class TokenStream
     void ungetChar(int32 c);
     void ungetCharIgnoreEOL(int32 c);
     Token *newToken(ptrdiff_t adjust);
     bool peekUnicodeEscape(int32 *c);
     bool matchUnicodeEscapeIdStart(int32 *c);
     bool matchUnicodeEscapeIdent(int32 *c);
     JSBool peekChars(intN n, jschar *cp);
     JSBool getXMLEntity();
-    jschar *findEOL();
+    jschar *findWlineLimit(jschar *tokptr, int max);
 
     JSBool matchChar(int32 expect) {
         int32 c = getChar();
         if (c == expect)
             return JS_TRUE;
         ungetChar(c);
         return JS_FALSE;
     }
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -6590,17 +6590,17 @@ ScopeChainCheck(JSContext* cx, TreeFragm
      *
      * #2 is checked here while following the scope-chain links, via
      * js_IsCacheableNonGlobalScope, which consults a whitelist of known
      * class types; once a global is found, it's checked for #1. Failing
      * either check causes an early return from execution.
      */
     JSObject* child = &cx->fp()->scopeChain();
     while (JSObject* parent = child->getParent()) {
-        if (!IsCacheableNonGlobalScope(child)) {
+        if (!js_IsCacheableNonGlobalScope(child)) {
             debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n");
             Blacklist((jsbytecode*) f->root->ip);
             return false;
         }
         child = parent;
     }
     JS_ASSERT(child == f->globalObj);
 
@@ -15178,17 +15178,17 @@ TraceRecorder::traverseScopeChain(JSObje
     }
 
     if (foundBlockObj)
         RETURN_STOP("cannot traverse this scope chain on trace");
 
     /* There was a call object, or should be a call object now. */
     for (;;) {
         if (obj != globalObj) {
-            if (!IsCacheableNonGlobalScope(obj))
+            if (!js_IsCacheableNonGlobalScope(obj))
                 RETURN_STOP("scope chain lookup crosses non-cacheable object");
 
             // We must guard on the shape of all call objects for heavyweight functions
             // that we traverse on the scope chain: if the shape changes, a variable with
             // the same name may have been inserted in the scope chain.
             if (IsFindableCallObj(obj)) {
                 if (!exit)
                     exit = snapshot(BRANCH_EXIT);
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -1253,30 +1253,16 @@ JITScript::sweepCallICs(JSContext *cx, b
          * executing a stub generated by a guard on that object. This lets us
          * precisely GC call ICs while keeping the identity guard safe.
          */
         bool fastFunDead = ic.fastGuardedObject &&
             (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedObject));
         bool nativeDead = ic.fastGuardedNative &&
             (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedNative));
 
-        /*
-         * There are three conditions where we need to relink:
-         * (1) purgeAll is true.
-         * (2) The native is dead, since it always has a stub.
-         * (3) The fastFun is dead *and* there is a closure stub.
-         *
-         * Note although both objects can be non-NULL, there can only be one
-         * of [closure, native] stub per call IC.
-         */
-        if (purgeAll || nativeDead || (fastFunDead && ic.hasJsFunCheck)) {
-            repatcher.relink(ic.funJump, ic.slowPathStart);
-            ic.hit = false;
-        }
-
         if (fastFunDead) {
             repatcher.repatch(ic.funGuard, NULL);
             ic.releasePool(CallICInfo::Pool_ClosureStub);
             ic.hasJsFunCheck = false;
             ic.fastGuardedObject = NULL;
         }
 
         if (nativeDead) {
@@ -1285,16 +1271,26 @@ JITScript::sweepCallICs(JSContext *cx, b
         }
 
         if (purgeAll) {
             ic.releasePool(CallICInfo::Pool_ScriptStub);
             JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset);
             JSC::CodeLocationLabel icCall = ic.slowPathStart.labelAtOffset(ic.icCallOffset);
             repatcher.relink(oolJump, icCall);
         }
+
+        /*
+         * Only relink the fast-path if there are no connected stubs, or we're
+         * trying to disconnect all stubs. Otherwise, we're just disabling an
+         * optimization that must take up space anyway (see bug 632729).
+         */
+        if (purgeAll || !(ic.fastGuardedObject || ic.fastGuardedNative)) {
+            repatcher.relink(ic.funJump, ic.slowPathStart);
+            ic.hit = false;
+        }
     }
 
     if (purgeAll) {
         /* Purge ICs generating stubs into execPools. */
         uint32 released = 0;
 
         ic::EqualityICInfo *equalityICs_ = equalityICs();
         for (uint32 i = 0; i < nEqualityICs; i++) {
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1182,17 +1182,17 @@ class ScopeNameCompiler : public PICStub
         /* Walk the scope chain. */
         JSObject *tobj = scopeChain;
 
         /* For GETXPROP, we'll never enter this loop. */
         JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, tobj && tobj == getprop.holder);
         JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, getprop.obj == tobj);
 
         while (tobj && tobj != getprop.holder) {
-            if (!IsCacheableNonGlobalScope(tobj))
+            if (!js_IsCacheableNonGlobalScope(tobj))
                 return disable("non-cacheable scope chain object");
             JS_ASSERT(tobj->isNative());
 
             if (tobj != scopeChain) {
                 /* scopeChain will never be NULL, but parents can be NULL. */
                 Jump j = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
                 if (!fails.append(j))
                     return error();
@@ -1534,17 +1534,17 @@ class BindNameCompiler : public PICStubC
         masm.loadShape(pic.objReg, pic.shapeReg);
         Jump firstShape = masm.branch32(Assembler::NotEqual, pic.shapeReg,
                                         Imm32(scopeChain->shape()));
 
         /* Walk up the scope chain. */
         JSObject *tobj = scopeChain;
         Address parent(pic.objReg, offsetof(JSObject, parent));
         while (tobj && tobj != obj) {
-            if (!IsCacheableNonGlobalScope(tobj))
+            if (!js_IsCacheableNonGlobalScope(tobj))
                 return disable("non-cacheable obj in scope chain");
             masm.loadPtr(parent, pic.objReg);
             Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
             if (!fails.append(nullTest))
                 return error();
             masm.loadShape(pic.objReg, pic.shapeReg);
             Jump shapeTest = masm.branch32(Assembler::NotEqual, pic.shapeReg,
                                            Imm32(tobj->shape()));